summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp')
-rw-r--r--dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp337
1 files changed, 337 insertions, 0 deletions
diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
new file mode 100644
index 0000000000..4dac27ce41
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
@@ -0,0 +1,337 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FFmpegLibWrapper.h"
+#include "FFmpegLog.h"
+#include "mozilla/PodOperations.h"
+#ifdef MOZ_FFMPEG
+# include "mozilla/StaticPrefs_media.h"
+#endif
+#include "mozilla/Types.h"
+#include "PlatformDecoderModule.h"
+#include "prlink.h"
+#ifdef MOZ_WAYLAND
+# include "mozilla/gfx/gfxVars.h"
+# include "mozilla/widget/DMABufLibWrapper.h"
+#endif
+
+#define AV_LOG_DEBUG 48
+#define AV_LOG_INFO 32
+
+namespace mozilla {
+
+FFmpegLibWrapper::LinkResult FFmpegLibWrapper::Link() {
+ if (!mAVCodecLib || !mAVUtilLib) {
+ Unlink();
+ return LinkResult::NoProvidedLib;
+ }
+
+ avcodec_version =
+ (decltype(avcodec_version))PR_FindSymbol(mAVCodecLib, "avcodec_version");
+ if (!avcodec_version) {
+ Unlink();
+ return LinkResult::NoAVCodecVersion;
+ }
+ uint32_t version = avcodec_version();
+ uint32_t macro = (version >> 16) & 0xFFu;
+ mVersion = static_cast<int>(macro);
+ uint32_t micro = version & 0xFFu;
+ // A micro version >= 100 indicates that it's FFmpeg (as opposed to LibAV).
+ bool isFFMpeg = micro >= 100;
+ if (!isFFMpeg) {
+ if (macro == 57) {
+ // Due to current AVCodecContext binary incompatibility we can only
+ // support FFmpeg 57 at this stage.
+ Unlink();
+ return LinkResult::CannotUseLibAV57;
+ }
+#ifdef MOZ_FFMPEG
+ if (version < (54u << 16 | 35u << 8 | 1u) &&
+ !StaticPrefs::media_libavcodec_allow_obsolete()) {
+ // Refuse any libavcodec version prior to 54.35.1.
+ // (Unless media.libavcodec.allow-obsolete==true)
+ Unlink();
+ return LinkResult::BlockedOldLibAVVersion;
+ }
+#endif
+ }
+
+ enum {
+ AV_FUNC_AVUTIL_MASK = 1 << 8,
+ AV_FUNC_53 = 1 << 0,
+ AV_FUNC_54 = 1 << 1,
+ AV_FUNC_55 = 1 << 2,
+ AV_FUNC_56 = 1 << 3,
+ AV_FUNC_57 = 1 << 4,
+ AV_FUNC_58 = 1 << 5,
+ AV_FUNC_59 = 1 << 6,
+ AV_FUNC_AVUTIL_53 = AV_FUNC_53 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_54 = AV_FUNC_54 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_55 = AV_FUNC_55 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_56 = AV_FUNC_56 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_57 = AV_FUNC_57 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_58 = AV_FUNC_58 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVUTIL_59 = AV_FUNC_59 | AV_FUNC_AVUTIL_MASK,
+ AV_FUNC_AVCODEC_ALL = AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 | AV_FUNC_56 |
+ AV_FUNC_57 | AV_FUNC_58 | AV_FUNC_59,
+ AV_FUNC_AVUTIL_ALL = AV_FUNC_AVCODEC_ALL | AV_FUNC_AVUTIL_MASK
+ };
+
+ switch (macro) {
+ case 53:
+ version = AV_FUNC_53;
+ break;
+ case 54:
+ version = AV_FUNC_54;
+ break;
+ case 55:
+ version = AV_FUNC_55;
+ break;
+ case 56:
+ version = AV_FUNC_56;
+ break;
+ case 57:
+ version = AV_FUNC_57;
+ break;
+ case 58:
+ version = AV_FUNC_58;
+ break;
+ case 59:
+ version = AV_FUNC_59;
+ break;
+ default:
+ FFMPEG_LOG("Unknown avcodec version");
+ Unlink();
+ return isFFMpeg ? ((macro > 57) ? LinkResult::UnknownFutureFFMpegVersion
+ : LinkResult::UnknownOlderFFMpegVersion)
+ // All LibAV versions<54.35.1 are blocked, therefore we
+ // must be dealing with a later one.
+ : LinkResult::UnknownFutureLibAVVersion;
+ }
+
+#define AV_FUNC_OPTION_SILENT(func, ver) \
+ if ((ver)&version) { \
+ if (!(func = (decltype(func))PR_FindSymbol( \
+ ((ver)&AV_FUNC_AVUTIL_MASK) ? mAVUtilLib : mAVCodecLib, \
+ #func))) { \
+ } \
+ } else { \
+ func = (decltype(func))nullptr; \
+ }
+
+#define AV_FUNC_OPTION(func, ver) \
+ AV_FUNC_OPTION_SILENT(func, ver) \
+ if ((ver)&version && (func) == (decltype(func))nullptr) { \
+ FFMPEG_LOG("Couldn't load function " #func); \
+ }
+
+#define AV_FUNC(func, ver) \
+ AV_FUNC_OPTION(func, ver) \
+ if ((ver)&version && !func) { \
+ Unlink(); \
+ return isFFMpeg ? LinkResult::MissingFFMpegFunction \
+ : LinkResult::MissingLibAVFunction; \
+ }
+
+ AV_FUNC(av_lockmgr_register, AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 |
+ AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58)
+ AV_FUNC(avcodec_alloc_context3, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_close, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_decode_audio4, AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 |
+ AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58)
+ AV_FUNC(avcodec_decode_video2, AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 |
+ AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58)
+ AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_register_all, AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 |
+ AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58)
+ AV_FUNC(av_init_packet, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(av_parser_init, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(av_parser_close, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(av_parser_parse2, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_align_dimensions, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(avcodec_alloc_frame, (AV_FUNC_53 | AV_FUNC_54))
+ AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
+ AV_FUNC(avcodec_free_frame, AV_FUNC_54)
+ AV_FUNC(avcodec_send_packet, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC(avcodec_receive_frame, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC(avcodec_default_get_buffer2,
+ (AV_FUNC_55 | AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58 | AV_FUNC_59))
+ AV_FUNC_OPTION(av_rdft_init, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC_OPTION(av_rdft_calc, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC_OPTION(av_rdft_end, AV_FUNC_AVCODEC_ALL)
+ AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
+ AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
+ AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
+ AV_FUNC(av_frame_alloc,
+ (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59))
+ AV_FUNC(av_frame_free,
+ (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59))
+ AV_FUNC(av_frame_unref,
+ (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59))
+ AV_FUNC(av_image_check_size, AV_FUNC_AVUTIL_ALL)
+ AV_FUNC(av_image_get_buffer_size, AV_FUNC_AVUTIL_ALL)
+ AV_FUNC_OPTION(av_buffer_get_opaque, (AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59))
+ AV_FUNC(av_buffer_create,
+ (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59))
+ AV_FUNC_OPTION(av_frame_get_colorspace,
+ AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58)
+ AV_FUNC_OPTION(av_frame_get_color_range,
+ AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 |
+ AV_FUNC_AVUTIL_58)
+ AV_FUNC(av_strerror, AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59)
+
+#ifdef MOZ_WAYLAND
+ AV_FUNC_OPTION_SILENT(avcodec_get_hw_config, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_codec_iterate, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_codec_is_decoder, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_init, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_alloc, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwdevice_hwconfig_alloc, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwdevice_get_hwframe_constraints,
+ AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwframe_constraints_free, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_buffer_ref, AV_FUNC_AVUTIL_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_buffer_unref, AV_FUNC_AVUTIL_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwframe_transfer_get_formats,
+ AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_create_derived, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_hwframe_ctx_alloc, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_dict_set, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_dict_free, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(avcodec_get_name, AV_FUNC_58 | AV_FUNC_59)
+ AV_FUNC_OPTION_SILENT(av_get_pix_fmt_string,
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59)
+#endif
+#undef AV_FUNC
+#undef AV_FUNC_OPTION
+
+#ifdef MOZ_WAYLAND
+# define VA_FUNC_OPTION_SILENT(func) \
+ if (!(func = (decltype(func))PR_FindSymbol(mVALib, #func))) { \
+ func = (decltype(func))nullptr; \
+ }
+
+ // mVALib is optional and may not be present.
+ if (mVALib) {
+ VA_FUNC_OPTION_SILENT(vaExportSurfaceHandle)
+ VA_FUNC_OPTION_SILENT(vaSyncSurface)
+ VA_FUNC_OPTION_SILENT(vaInitialize)
+ VA_FUNC_OPTION_SILENT(vaTerminate)
+ }
+# undef VA_FUNC_OPTION_SILENT
+
+# define VAD_FUNC_OPTION_SILENT(func) \
+ if (!(func = (decltype(func))PR_FindSymbol(mVALibDrm, #func))) { \
+ FFMPEG_LOG("Couldn't load function " #func); \
+ }
+
+ // mVALibDrm is optional and may not be present.
+ if (mVALibDrm) {
+ VAD_FUNC_OPTION_SILENT(vaGetDisplayDRM)
+ }
+# undef VAD_FUNC_OPTION_SILENT
+#endif
+
+ if (avcodec_register_all) {
+ avcodec_register_all();
+ }
+ if (MOZ_LOG_TEST(sPDMLog, LogLevel::Debug)) {
+ av_log_set_level(AV_LOG_DEBUG);
+ } else if (MOZ_LOG_TEST(sPDMLog, LogLevel::Info)) {
+ av_log_set_level(AV_LOG_INFO);
+ } else {
+ av_log_set_level(0);
+ }
+ return LinkResult::Success;
+}
+
+void FFmpegLibWrapper::Unlink() {
+ if (av_lockmgr_register) {
+ // Registering a null lockmgr cause the destruction of libav* global mutexes
+ // as the default lockmgr that allocated them will be deregistered.
+ // This prevents ASAN and valgrind to report sizeof(pthread_mutex_t) leaks.
+ av_lockmgr_register(nullptr);
+ }
+#ifndef MOZ_TSAN
+ // With TSan, we cannot unload libav once we have loaded it because
+ // TSan does not support unloading libraries that are matched from its
+ // suppression list. Hence we just keep the library loaded in TSan builds.
+ if (mAVUtilLib && mAVUtilLib != mAVCodecLib) {
+ PR_UnloadLibrary(mAVUtilLib);
+ }
+ if (mAVCodecLib) {
+ PR_UnloadLibrary(mAVCodecLib);
+ }
+#endif
+#ifdef MOZ_WAYLAND
+ if (mVALib) {
+ PR_UnloadLibrary(mVALib);
+ }
+ if (mVALibDrm) {
+ PR_UnloadLibrary(mVALibDrm);
+ }
+#endif
+ PodZero(this);
+}
+
+#ifdef MOZ_WAYLAND
+void FFmpegLibWrapper::LinkVAAPILibs() {
+ if (!gfx::gfxVars::CanUseHardwareVideoDecoding() || !XRE_IsRDDProcess()) {
+ return;
+ }
+
+ PRLibSpec lspec;
+ lspec.type = PR_LibSpec_Pathname;
+ const char* libDrm = "libva-drm.so.2";
+ lspec.value.pathname = libDrm;
+ mVALibDrm = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
+ if (!mVALibDrm) {
+ FFMPEG_LOG("VA-API support: Missing or old %s library.\n", libDrm);
+ return;
+ }
+
+ const char* lib = "libva.so.2";
+ lspec.value.pathname = lib;
+ mVALib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
+ // Don't use libva when it's missing vaExportSurfaceHandle.
+ if (mVALib && !PR_FindSymbol(mVALib, "vaExportSurfaceHandle")) {
+ PR_UnloadLibrary(mVALib);
+ mVALib = nullptr;
+ }
+ if (!mVALib) {
+ FFMPEG_LOG("VA-API support: Missing or old %s library.\n", lib);
+ }
+}
+#endif
+
+#ifdef MOZ_WAYLAND
+bool FFmpegLibWrapper::IsVAAPIAvailable() {
+# define VA_FUNC_LOADED(func) (func != nullptr)
+ return VA_FUNC_LOADED(avcodec_get_hw_config) &&
+ VA_FUNC_LOADED(av_hwdevice_ctx_alloc) &&
+ VA_FUNC_LOADED(av_hwdevice_ctx_init) &&
+ VA_FUNC_LOADED(av_hwdevice_hwconfig_alloc) &&
+ VA_FUNC_LOADED(av_hwdevice_get_hwframe_constraints) &&
+ VA_FUNC_LOADED(av_hwframe_constraints_free) &&
+ VA_FUNC_LOADED(av_buffer_ref) && VA_FUNC_LOADED(av_buffer_unref) &&
+ VA_FUNC_LOADED(av_hwframe_transfer_get_formats) &&
+ VA_FUNC_LOADED(av_hwdevice_ctx_create_derived) &&
+ VA_FUNC_LOADED(av_hwframe_ctx_alloc) && VA_FUNC_LOADED(av_dict_set) &&
+ VA_FUNC_LOADED(av_dict_free) && VA_FUNC_LOADED(avcodec_get_name) &&
+ VA_FUNC_LOADED(av_get_pix_fmt_string) &&
+ VA_FUNC_LOADED(vaExportSurfaceHandle) &&
+ VA_FUNC_LOADED(vaSyncSurface) && VA_FUNC_LOADED(vaInitialize) &&
+ VA_FUNC_LOADED(vaTerminate) && VA_FUNC_LOADED(vaGetDisplayDRM);
+}
+#endif
+
+} // namespace mozilla