引言
接续“增强axmol引擎视频播放之 - android视频播放支持”,本文主要描述如何在 著名的Linux发行版Ubuntu
支持视频渲染到纹理播放。如无特殊说明,一下描述中出现Linux
均指的是Ubuntu Linux
,版本Ubuntu 22.04+
。
为什么开发
当笔者成功完成了android视频纹理渲染后,axmol引擎所支持的平台(macos,windows,ios/tvos,android,linux),就只剩下Linux了,因此萌生了何不顺带把Linux支持下。
调研
经过研究发现,Linux下比较优秀的播放器里,VLC提供了libvlc
LGPL-2.1授权(不修改源码的情况下,以动态库形式免费商用),因此集成libvlc
是比较好的方案。
如何实现
依然是实现axmol的MediaEngine
接口,不过我们使用libvlc
作为backend
。
-
安装libvlc开发包
sudo apt install libvlc-dev libvlccore-dev vlc
其中
libvlc-dev
和libvlccore-dev
是编译连接必须的,而安装vlc
实际上是安装播放器和插件.so,其中播放器不需要,插件才是运行必须的,否则运行就会无法解码视频。
TIPS: 插件加载潜规则:- 默认情况下,当调用
libvlc_new
API时,libvlc会从libvlc.so
和libvlccore.so
库所在目录下的plugins
目录搜索播放视频所需解码插件。安装vlc
会将插件放到正确的路径 - 通过环境变量:
VLC_PLUGIN_PATH
指定libvlc
加载插件路径- Linux可通过API
setenv
动态设置:setenv("VLC_PLUGIN_PATH", "/path/to/vlc/plugins", true);
- Windows可通过API
_putenv_s
设置,但特别注意需要根据libvlc.dll
和libvlccore.dll
以来的c运行库决定,官方预编译好的libvlc库使用的是msvcrt
,意味着在win10系统上,直接调用_putenv_s
是无效的,直接调用_putenv_s
通常会设置ucrt
里的环境变量,msvcrt
和ucrtbased/ucrtbase
不同C运行库共存时,内存时隔离的,如果想时两种运行库都生效,可通过如下方式设置:_putenv_s("VLC_PLUGIN_PATH", R"(D:\dev\axmol\thirdparty\vlc\win\lib\plugins)"); HMODULE hmsvcrt = GetModuleHandleW(L"msvcrt.dll"); auto msvcrt_getenv = (decltype(getenv)*)GetProcAddress(hmsvcrt, "getenv"); if (msvcrt_getenv) { auto msvcrt_putenv_s = (decltype(_putenv_s)*)GetProcAddress(hmsvcrt, "_putenv_s"); if (msvcrt_putenv_s) msvcrt_putenv_s("VLC_PLUGIN_PATH", R"(D:\dev\axmol\thirdparty\vlc\win\lib\plugins)"); }
- Linux可通过API
- 默认情况下,当调用
-
编写代码实现
MediaEngine
的核心功能,如播放,暂停等,注册callbacks(媒体事件,视频NV12 Frame事件)等功能。 -
关于如何让获取VLC解码的NV12视频数据(网上多数都是RGBA),本文只贴出关键代码
libvlc
lock回调中,为本次视频数据frame锁定内存以便写入:static constexpr auto VLC_OUTPUT_FORMAT = ax::MEVideoPixelFormat::NV12; void* VlcMediaEngine::libvlc_video_lock(void* data, void** p_pixels) { VlcMediaEngine* mediaEngine = static_cast<VlcMediaEngine*>(data); auto& bufferDim = mediaEngine->_videoDim; auto& outputBuffer = mediaEngine->_frameBuffer1; mediaEngine->_frameBuffer1Mtx.lock(); if constexpr (VLC_OUTPUT_FORMAT == ax::MEVideoPixelFormat::NV12) { outputBuffer.resize_fit(bufferDim.x * bufferDim.y + (bufferDim.x * bufferDim.y >> 1)); // NV12 p_pixels[0] = outputBuffer.data(); p_pixels[1] = outputBuffer.data() + (bufferDim.x * bufferDim.y); } else if constexpr (VLC_OUTPUT_FORMAT == ax::MEVideoPixelFormat::YUY2) { outputBuffer.resize_fit(bufferDim.x * bufferDim.y + ((bufferDim.x >> 1) * bufferDim.y * 4)); // YUY2 p_pixels[0] = outputBuffer.data(); } else { outputBuffer.resize_fit(bufferDim.x * bufferDim.y * 4); // RGBA32 p_pixels[0] = outputBuffer.data(); } return nullptr; } void VlcMediaEngine::libvlc_video_unlock(void* data, void* id, void* const* p_pixels) { VlcMediaEngine* mediaEngine = static_cast<VlcMediaEngine*>(data); mediaEngine->_frameBuffer1Mtx.unlock(); ++mediaEngine->_frameIndex; assert(id == nullptr); }
libvlc
使用回调模式格式设置,值得注意的是NV12需要指定第二个plane
的pitch
(bytesPerRow)和lines
(视频期望渲染高度/2)unsigned int VlcMediaEngine::libvlc_video_format_setup(void** opaque, char* chroma, // forcc, refer to:vlc_fourcc.h unsigned* width, unsigned* height, unsigned* pitches, unsigned* lines) { // refer to: vmem.c:Open https://github.com/videolan/vlc/blob/3.0.18/modules/video_output/vmem.c#L150 // future: 4.0: will be widths, heights: // https://github.com/videolan/vlc/blob/master/modules/video_output/vmem.c#L156 VlcMediaEngine* mediaEngine = static_cast<VlcMediaEngine*>(*opaque); // vlc tell us the original codecDim(ALIGNED) mediaEngine->_codecDim.set(width[0], height[0]); // tell vlc we want render as video size width[0] = mediaEngine->_videoDim.x; height[0] = mediaEngine->_videoDim.y; // plane0 pitches[0] = width[0]; // bytesPerRow lines[0] = height[0]; // rows # if LIBVLC_VERSION_MAJOR >= 4 mediaEngine->_videoDim.set(width[1], height[1]); # endif int num_of_plane = 1; if constexpr (VLC_OUTPUT_FORMAT == ax::MEVideoPixelFormat::NV12) { memcpy(chroma, "NV12", 4); // plane1 pitches[1] = mediaEngine->_videoDim.x; // bytesPerRow lines[1] = mediaEngine->_videoDim.y >> 1; // rows num_of_plane = 2; } else if constexpr (VLC_OUTPUT_FORMAT == ax::MEVideoPixelFormat::YUY2) { memcpy(chroma, "YUY2", 4); pitches[0] = width[0] * 2; // bytesPerRow } else { memcpy(chroma, "RGBA", 4); pitches[0] = width[0] * 4; // bytesPerRow } // return the number of picture buffers allocated, 0 indicates failure return num_of_plane; }
运行和注意事项
请下载axmol引擎最新源码在Ubuntu 22.04+
系统下编译运行。如果遇到播放视频出现无法解码问题,请安装ubuntu扩展组件
,命令如下:文章来源:https://www.toymoban.com/news/detail-480679.html
sudo apt install ubuntu-restricted-extras
结语
自此,axmol引擎的ui::MediaPlayer
支持全部平台上的视频纹理渲染播放,这也得益于重新设计的MediaEngine
框架,该框架不仅能在axmol
引擎中工作,也可以独立编译,例如笔者编写的bgfx视频播放示例:bgfx-axplay。
另外:libvlc
本身是跨平台的,因此本文中的实现VlcMediaEngine
经过简单配置也可以运行在其他平台,例如Windows
文章来源地址https://www.toymoban.com/news/detail-480679.html
到了这里,关于增强axmol引擎视频播放之 - Linux(Ubuntu)视频播放支持的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!