mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 941298 - FFmpeg PlatformDecoderModule for Linux r=doublec,cpearce
This commit is contained in:
parent
372079241f
commit
cab563a65a
@ -12,6 +12,9 @@
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegDecoderModule.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -64,17 +67,33 @@ MP4Decoder::GetSupportedCodecs(const nsACString& aType,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFFmpegAvailable()
|
||||
{
|
||||
#ifndef MOZ_FFMPEG
|
||||
return false;
|
||||
#else
|
||||
if (!Preferences::GetBool("media.fragmented-mp4.ffmpeg.enabled", false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we can link to FFmpeg, then we can almost certainly play H264 and AAC
|
||||
// with it.
|
||||
return FFmpegDecoderModule::Link();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
HavePlatformMPEGDecoders()
|
||||
{
|
||||
return
|
||||
Preferences::GetBool("media.fragmented-mp4.use-blank-decoder") ||
|
||||
return Preferences::GetBool("media.fragmented-mp4.use-blank-decoder") ||
|
||||
#ifdef XP_WIN
|
||||
// We have H.264/AAC platform decoders on Windows Vista and up.
|
||||
IsVistaOrLater() ||
|
||||
// We have H.264/AAC platform decoders on Windows Vista and up.
|
||||
IsVistaOrLater() ||
|
||||
#endif
|
||||
// TODO: Other platforms...
|
||||
false;
|
||||
IsFFmpegAvailable() ||
|
||||
// TODO: Other platforms...
|
||||
false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -8,6 +8,9 @@
|
||||
#ifdef XP_WIN
|
||||
#include "WMFDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegDecoderModule.h"
|
||||
#endif
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -15,6 +18,7 @@ namespace mozilla {
|
||||
extern PlatformDecoderModule* CreateBlankDecoderModule();
|
||||
|
||||
bool PlatformDecoderModule::sUseBlankDecoder = false;
|
||||
bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
|
||||
|
||||
/* static */
|
||||
void
|
||||
@ -26,7 +30,12 @@ PlatformDecoderModule::Init()
|
||||
return;
|
||||
}
|
||||
alreadyInitialized = true;
|
||||
sUseBlankDecoder = Preferences::GetBool("media.fragmented-mp4.use-blank-decoder");
|
||||
|
||||
Preferences::AddBoolVarCache(&sUseBlankDecoder,
|
||||
"media.fragmented-mp4.use-blank-decoder");
|
||||
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
|
||||
"media.fragmented-mp4.ffmpeg.enabled", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
WMFDecoderModule::Init();
|
||||
#endif
|
||||
@ -44,6 +53,11 @@ PlatformDecoderModule::Create()
|
||||
if (NS_SUCCEEDED(m->Startup())) {
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
if (sFFmpegDecoderEnabled) {
|
||||
return new FFmpegDecoderModule();
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ protected:
|
||||
PlatformDecoderModule() {}
|
||||
// Caches pref media.fragmented-mp4.use-blank-decoder
|
||||
static bool sUseBlankDecoder;
|
||||
static bool sFFmpegDecoderEnabled;
|
||||
};
|
||||
|
||||
// A callback used by MediaDataDecoder to return output/errors to the
|
||||
|
130
content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp
Normal file
130
content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "MediaTaskQueue.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
#include "FFmpegAACDecoder.h"
|
||||
|
||||
#define MAX_CHANNELS 16
|
||||
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
FFmpegAACDecoder::FFmpegAACDecoder(
|
||||
MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
|
||||
const mp4_demuxer::AudioDecoderConfig &aConfig)
|
||||
: FFmpegDataDecoder(aTaskQueue, AV_CODEC_ID_AAC)
|
||||
, mCallback(aCallback)
|
||||
, mConfig(aConfig)
|
||||
{
|
||||
MOZ_COUNT_CTOR(FFmpegAACDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegAACDecoder::Init()
|
||||
{
|
||||
nsresult rv = FFmpegDataDecoder::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static AudioDataValue*
|
||||
CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumSamples)
|
||||
{
|
||||
// These are the only two valid AAC packet sizes.
|
||||
NS_ASSERTION(aNumSamples == 960 || aNumSamples == 1024,
|
||||
"Should have exactly one AAC audio packet.");
|
||||
MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue> audio(
|
||||
new AudioDataValue[aNumChannels * aNumSamples]);
|
||||
|
||||
AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data);
|
||||
|
||||
if (aFrame->format == AV_SAMPLE_FMT_FLT) {
|
||||
// Audio data already packed. No need to do anything other than copy it
|
||||
// into a buffer we own.
|
||||
memcpy(audio, data[0], aNumChannels * aNumSamples * sizeof(AudioDataValue));
|
||||
} else if (aFrame->format == AV_SAMPLE_FMT_FLTP) {
|
||||
// Planar audio data. Pack it into something we can understand.
|
||||
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
|
||||
for (uint32_t sample = 0; sample < aNumSamples; sample++) {
|
||||
audio[sample * aNumChannels + channel] = data[channel][sample];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return audio.forget();
|
||||
}
|
||||
|
||||
void
|
||||
FFmpegAACDecoder::DecodePacket(MP4Sample* aSample)
|
||||
{
|
||||
nsAutoPtr<AVFrame> frame(avcodec_alloc_frame());
|
||||
avcodec_get_frame_defaults(frame);
|
||||
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
packet.data = &(*aSample->data)[0];
|
||||
packet.size = aSample->data->size();
|
||||
packet.pos = aSample->byte_offset;
|
||||
packet.dts = aSample->decode_timestamp;
|
||||
|
||||
int decoded;
|
||||
int bytesConsumed =
|
||||
avcodec_decode_audio4(&mCodecContext, frame.get(), &decoded, &packet);
|
||||
|
||||
if (bytesConsumed < 0 || !decoded) {
|
||||
NS_WARNING("FFmpeg audio decoder error.");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(bytesConsumed == (int)aSample->data->size(),
|
||||
"Only one audio packet should be received at a time.");
|
||||
|
||||
uint32_t numChannels = mCodecContext.channels;
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue> audio(
|
||||
CopyAndPackAudio(frame.get(), numChannels, frame->nb_samples));
|
||||
|
||||
nsAutoPtr<AudioData> data(new AudioData(packet.pos, aSample->decode_timestamp,
|
||||
aSample->duration, frame->nb_samples,
|
||||
audio.forget(), numChannels));
|
||||
|
||||
mCallback->Output(data.forget());
|
||||
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegAACDecoder::Input(MP4Sample* aSample)
|
||||
{
|
||||
mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg<nsAutoPtr<MP4Sample> >(
|
||||
this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegAACDecoder::Drain()
|
||||
{
|
||||
// AAC is never delayed; nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
FFmpegAACDecoder::~FFmpegAACDecoder() {
|
||||
MOZ_COUNT_DTOR(FFmpegAACDecoder);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
36
content/media/fmp4/ffmpeg/FFmpegAACDecoder.h
Normal file
36
content/media/fmp4/ffmpeg/FFmpegAACDecoder.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegAACDecoder_h__
|
||||
#define __FFmpegAACDecoder_h__
|
||||
|
||||
#include "FFmpegDataDecoder.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
class FFmpegAACDecoder : public FFmpegDataDecoder
|
||||
{
|
||||
public:
|
||||
FFmpegAACDecoder(MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
const mp4_demuxer::AudioDecoderConfig &aConfig);
|
||||
virtual ~FFmpegAACDecoder();
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
void DecodePacket(mp4_demuxer::MP4Sample* aSample);
|
||||
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
mp4_demuxer::AudioDecoderConfig mConfig;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __FFmpegAACDecoder_h__
|
18
content/media/fmp4/ffmpeg/FFmpegCompat.h
Normal file
18
content/media/fmp4/ffmpeg/FFmpegCompat.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegCompat_h__
|
||||
#define __FFmpegCompat_h__
|
||||
|
||||
#include <libavcodec/version.h>
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
#define AV_CODEC_ID_H264 CODEC_ID_H264
|
||||
#define AV_CODEC_ID_AAC CODEC_ID_AAC
|
||||
typedef CodecID AVCodecID;
|
||||
#endif
|
||||
|
||||
#endif // __FFmpegCompat_h__
|
124
content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
Normal file
124
content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
#include "FFmpegDataDecoder.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
bool FFmpegDataDecoder::sFFmpegInitDone = false;
|
||||
|
||||
FFmpegDataDecoder::FFmpegDataDecoder(MediaTaskQueue* aTaskQueue,
|
||||
AVCodecID aCodecID)
|
||||
: mTaskQueue(aTaskQueue), mCodecID(aCodecID)
|
||||
{
|
||||
MOZ_COUNT_CTOR(FFmpegDataDecoder);
|
||||
}
|
||||
|
||||
FFmpegDataDecoder::~FFmpegDataDecoder() {
|
||||
MOZ_COUNT_DTOR(FFmpegDataDecoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* FFmpeg calls back to this function with a list of pixel formats it supports.
|
||||
* We choose a pixel format that we support and return it.
|
||||
* For now, we just look for YUV420P as it is the only non-HW accelerated format
|
||||
* supported by FFmpeg's H264 decoder.
|
||||
*/
|
||||
static PixelFormat
|
||||
ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
|
||||
{
|
||||
FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
|
||||
for (; *aFormats > -1; aFormats++) {
|
||||
if (*aFormats == PIX_FMT_YUV420P) {
|
||||
FFMPEG_LOG("Requesting pixel format YUV420P.");
|
||||
return PIX_FMT_YUV420P;
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARNING("FFmpeg does not share any supported pixel formats.");
|
||||
return PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegDataDecoder::Init()
|
||||
{
|
||||
FFMPEG_LOG("Initialising FFmpeg decoder.");
|
||||
|
||||
if (!FFmpegRuntimeLinker::Link()) {
|
||||
NS_WARNING("Failed to link FFmpeg shared libraries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!sFFmpegInitDone) {
|
||||
av_register_all();
|
||||
#ifdef DEBUG
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
#endif
|
||||
sFFmpegInitDone = true;
|
||||
}
|
||||
|
||||
AVCodec* codec = avcodec_find_decoder(mCodecID);
|
||||
if (!codec) {
|
||||
NS_WARNING("Couldn't find ffmpeg decoder");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (avcodec_get_context_defaults3(&mCodecContext, codec) < 0) {
|
||||
NS_WARNING("Couldn't init ffmpeg context");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mCodecContext.opaque = this;
|
||||
|
||||
// FFmpeg takes this as a suggestion for what format to use for audio samples.
|
||||
mCodecContext.request_sample_fmt = AV_SAMPLE_FMT_FLT;
|
||||
|
||||
// FFmpeg will call back to this to negotiate a video pixel format.
|
||||
mCodecContext.get_format = ChoosePixelFormat;
|
||||
|
||||
AVDictionary* opts = nullptr;
|
||||
if (avcodec_open2(&mCodecContext, codec, &opts) < 0) {
|
||||
NS_WARNING("Couldn't initialise ffmpeg decoder");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mCodecContext.codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
mCodecContext.sample_fmt != AV_SAMPLE_FMT_FLT &&
|
||||
mCodecContext.sample_fmt != AV_SAMPLE_FMT_FLTP) {
|
||||
NS_WARNING("FFmpeg AAC decoder outputs unsupported audio format.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FFMPEG_LOG("FFmpeg init successful.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegDataDecoder::Flush()
|
||||
{
|
||||
mTaskQueue->Flush();
|
||||
avcodec_flush_buffers(&mCodecContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegDataDecoder::Shutdown()
|
||||
{
|
||||
if (sFFmpegInitDone) {
|
||||
avcodec_close(&mCodecContext);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
45
content/media/fmp4/ffmpeg/FFmpegDataDecoder.h
Normal file
45
content/media/fmp4/ffmpeg/FFmpegDataDecoder.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegDataDecoder_h__
|
||||
#define __FFmpegDataDecoder_h__
|
||||
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
|
||||
#include "FFmpegDecoderModule.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
#include "FFmpegCompat.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
class FFmpegDataDecoder : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
FFmpegDataDecoder(MediaTaskQueue* aTaskQueue, AVCodecID aCodecID);
|
||||
virtual ~FFmpegDataDecoder();
|
||||
|
||||
static bool Link();
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) = 0;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() = 0;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
MediaTaskQueue* mTaskQueue;
|
||||
AVCodecContext mCodecContext;
|
||||
|
||||
private:
|
||||
static bool sFFmpegInitDone;
|
||||
|
||||
AVCodecID mCodecID;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __FFmpegDataDecoder_h__
|
81
content/media/fmp4/ffmpeg/FFmpegDecoderModule.cpp
Normal file
81
content/media/fmp4/ffmpeg/FFmpegDecoderModule.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "FFmpegRuntimeLinker.h"
|
||||
#include "FFmpegAACDecoder.h"
|
||||
#include "FFmpegH264Decoder.h"
|
||||
|
||||
#include "FFmpegDecoderModule.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
PRLogModuleInfo* GetFFmpegDecoderLog()
|
||||
{
|
||||
static PRLogModuleInfo* sFFmpegDecoderLog = nullptr;
|
||||
if (!sFFmpegDecoderLog) {
|
||||
sFFmpegDecoderLog = PR_NewLogModule("FFmpegDecoderModule");
|
||||
}
|
||||
return sFFmpegDecoderLog;
|
||||
}
|
||||
|
||||
bool FFmpegDecoderModule::sFFmpegLinkDone = false;
|
||||
|
||||
FFmpegDecoderModule::FFmpegDecoderModule()
|
||||
{
|
||||
MOZ_COUNT_CTOR(FFmpegDecoderModule);
|
||||
}
|
||||
|
||||
FFmpegDecoderModule::~FFmpegDecoderModule() {
|
||||
MOZ_COUNT_DTOR(FFmpegDecoderModule);
|
||||
}
|
||||
|
||||
bool
|
||||
FFmpegDecoderModule::Link()
|
||||
{
|
||||
if (sFFmpegLinkDone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!FFmpegRuntimeLinker::Link()) {
|
||||
NS_WARNING("Failed to link FFmpeg shared libraries.");
|
||||
return false;
|
||||
}
|
||||
|
||||
sFFmpegLinkDone = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegDecoderModule::Shutdown()
|
||||
{
|
||||
// Nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
FFmpegDecoderModule::CreateH264Decoder(
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
FFMPEG_LOG("Creating FFmpeg H264 decoder.");
|
||||
return new FFmpegH264Decoder(aVideoTaskQueue, aCallback, aConfig,
|
||||
aImageContainer);
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
FFmpegDecoderModule::CreateAACDecoder(
|
||||
const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue, MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
FFMPEG_LOG("Creating FFmpeg AAC decoder.");
|
||||
return new FFmpegAACDecoder(aAudioTaskQueue, aCallback, aConfig);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
50
content/media/fmp4/ffmpeg/FFmpegDecoderModule.h
Normal file
50
content/media/fmp4/ffmpeg/FFmpegDecoderModule.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegDecoderModule_h__
|
||||
#define __FFmpegDecoderModule_h__
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetFFmpegDecoderLog();
|
||||
#define FFMPEG_LOG(...) PR_LOG(GetFFmpegDecoderLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define FFMPEG_LOG(...)
|
||||
#endif
|
||||
|
||||
class FFmpegDecoderModule : public PlatformDecoderModule
|
||||
{
|
||||
public:
|
||||
FFmpegDecoderModule();
|
||||
virtual ~FFmpegDecoderModule();
|
||||
|
||||
static bool Link();
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
virtual MediaDataDecoder* CreateH264Decoder(
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
virtual MediaDataDecoder* CreateAACDecoder(
|
||||
const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
static bool sFFmpegLinkDone;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __FFmpegDecoderModule_h__
|
25
content/media/fmp4/ffmpeg/FFmpegFunctionList.h
Normal file
25
content/media/fmp4/ffmpeg/FFmpegFunctionList.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* 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/. */
|
||||
|
||||
AV_FUNC(LIBAVCODEC, avcodec_align_dimensions2)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_alloc_frame)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_close)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_decode_audio4)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_decode_video2)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_default_get_buffer)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_default_release_buffer)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_find_decoder)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_flush_buffers)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_get_context_defaults3)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_get_edge_width)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_get_frame_defaults)
|
||||
AV_FUNC(LIBAVCODEC, avcodec_open2)
|
||||
AV_FUNC(LIBAVCODEC, av_init_packet)
|
||||
AV_FUNC(LIBAVCODEC, av_dict_get)
|
||||
|
||||
AV_FUNC(LIBAVFORMAT, av_register_all)
|
||||
|
||||
AV_FUNC(LIBAVUTIL, av_image_fill_linesizes)
|
||||
AV_FUNC(LIBAVUTIL, av_image_fill_pointers)
|
||||
AV_FUNC(LIBAVUTIL, av_log_set_level)
|
271
content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
Normal file
271
content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "MediaTaskQueue.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "ImageContainer.h"
|
||||
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
#include "FFmpegH264Decoder.h"
|
||||
|
||||
#define GECKO_FRAME_TYPE 0x00093CC0
|
||||
|
||||
typedef mozilla::layers::Image Image;
|
||||
typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
|
||||
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
FFmpegH264Decoder::FFmpegH264Decoder(
|
||||
MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
|
||||
const mp4_demuxer::VideoDecoderConfig &aConfig,
|
||||
ImageContainer* aImageContainer)
|
||||
: FFmpegDataDecoder(aTaskQueue, AV_CODEC_ID_H264)
|
||||
, mConfig(aConfig)
|
||||
, mCallback(aCallback)
|
||||
, mImageContainer(aImageContainer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(FFmpegH264Decoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegH264Decoder::Init()
|
||||
{
|
||||
nsresult rv = FFmpegDataDecoder::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCodecContext.get_buffer = AllocateBufferCb;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FFmpegH264Decoder::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
packet.data = &(*aSample->data)[0];
|
||||
packet.size = aSample->data->size();
|
||||
packet.dts = aSample->decode_timestamp;
|
||||
packet.pts = aSample->composition_timestamp;
|
||||
packet.flags = aSample->is_sync_point ? AV_PKT_FLAG_KEY : 0;
|
||||
packet.pos = aSample->byte_offset;
|
||||
|
||||
nsAutoPtr<AVFrame> frame(avcodec_alloc_frame());
|
||||
avcodec_get_frame_defaults(frame);
|
||||
|
||||
int decoded;
|
||||
int bytesConsumed =
|
||||
avcodec_decode_video2(&mCodecContext, frame, &decoded, &packet);
|
||||
|
||||
if (bytesConsumed < 0) {
|
||||
NS_WARNING("FFmpeg video decoder error.");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decoded) {
|
||||
// The decoder doesn't have enough data to decode a frame yet.
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoPtr<VideoData> data;
|
||||
|
||||
VideoInfo info;
|
||||
info.mDisplay = nsIntSize(mCodecContext.width, mCodecContext.height);
|
||||
info.mStereoMode = StereoMode::MONO;
|
||||
info.mHasVideo = true;
|
||||
|
||||
data = VideoData::CreateFromImage(
|
||||
info, mImageContainer, aSample->byte_offset, aSample->composition_timestamp,
|
||||
aSample->duration, mCurrentImage, aSample->is_sync_point, -1,
|
||||
gfx::IntRect(0, 0, mCodecContext.width, mCodecContext.height));
|
||||
|
||||
// Insert the frame into the heap for reordering.
|
||||
mDelayedFrames.Push(data.forget());
|
||||
|
||||
// Reorder video frames from decode order to presentation order. The minimum
|
||||
// size of the heap comes from one P frame + |max_b_frames| B frames, which
|
||||
// is the maximum number of frames in a row which will be out-of-order.
|
||||
if (mDelayedFrames.Length() > (uint32_t)mCodecContext.max_b_frames + 1) {
|
||||
VideoData* d = mDelayedFrames.Pop();
|
||||
mCallback->Output(d);
|
||||
}
|
||||
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
PlanarYCbCrDataFromAVFrame(mozilla::layers::PlanarYCbCrData &aData,
|
||||
AVFrame* aFrame)
|
||||
{
|
||||
aData.mPicX = aData.mPicY = 0;
|
||||
aData.mPicSize = mozilla::gfx::IntSize(aFrame->width, aFrame->height);
|
||||
aData.mStereoMode = StereoMode::MONO;
|
||||
|
||||
aData.mYChannel = aFrame->data[0];
|
||||
aData.mYStride = aFrame->linesize[0];
|
||||
aData.mYSize = aData.mPicSize;
|
||||
aData.mYSkip = 0;
|
||||
|
||||
aData.mCbChannel = aFrame->data[1];
|
||||
aData.mCrChannel = aFrame->data[2];
|
||||
aData.mCbCrStride = aFrame->linesize[1];
|
||||
aData.mCbSkip = aData.mCrSkip = 0;
|
||||
aData.mCbCrSize =
|
||||
mozilla::gfx::IntSize((aFrame->width + 1) / 2, (aFrame->height + 1) / 2);
|
||||
}
|
||||
|
||||
/* static */ int
|
||||
FFmpegH264Decoder::AllocateBufferCb(AVCodecContext* aCodecContext,
|
||||
AVFrame* aFrame)
|
||||
{
|
||||
MOZ_ASSERT(aCodecContext->codec_type == AVMEDIA_TYPE_VIDEO);
|
||||
|
||||
FFmpegH264Decoder* self =
|
||||
reinterpret_cast<FFmpegH264Decoder*>(aCodecContext->opaque);
|
||||
|
||||
switch (aCodecContext->pix_fmt) {
|
||||
case PIX_FMT_YUV420P:
|
||||
return self->AllocateYUV420PVideoBuffer(aCodecContext, aFrame);
|
||||
default:
|
||||
return avcodec_default_get_buffer(aCodecContext, aFrame);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
FFmpegH264Decoder::AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
|
||||
AVFrame* aFrame)
|
||||
{
|
||||
// Older versions of ffmpeg require that edges be allocated* around* the
|
||||
// actual image.
|
||||
int edgeWidth = avcodec_get_edge_width();
|
||||
int decodeWidth = aCodecContext->width + edgeWidth * 2;
|
||||
int decodeHeight = aCodecContext->height + edgeWidth * 2;
|
||||
|
||||
// Align width and height to possibly speed up decode.
|
||||
int stride_align[AV_NUM_DATA_POINTERS];
|
||||
avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight,
|
||||
stride_align);
|
||||
|
||||
// Get strides for each plane.
|
||||
av_image_fill_linesizes(aFrame->linesize, aCodecContext->pix_fmt,
|
||||
decodeWidth);
|
||||
|
||||
// Let FFmpeg set up its YUV plane pointers and tell us how much memory we
|
||||
// need.
|
||||
// Note that we're passing |nullptr| here as the base address as we haven't
|
||||
// allocated our image yet. We will adjust |aFrame->data| below.
|
||||
size_t allocSize =
|
||||
av_image_fill_pointers(aFrame->data, aCodecContext->pix_fmt, decodeHeight,
|
||||
nullptr /* base address */, aFrame->linesize);
|
||||
|
||||
nsRefPtr<Image> image =
|
||||
mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
|
||||
PlanarYCbCrImage* ycbcr = reinterpret_cast<PlanarYCbCrImage*>(image.get());
|
||||
uint8_t* buffer = ycbcr->AllocateAndGetNewBuffer(allocSize);
|
||||
|
||||
if (!buffer) {
|
||||
NS_WARNING("Failed to allocate buffer for FFmpeg video decoding");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now that we've allocated our image, we can add its address to the offsets
|
||||
// set by |av_image_fill_pointers| above. We also have to add |edgeWidth|
|
||||
// pixels of padding here.
|
||||
for (uint32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
||||
// The C planes are half the resolution of the Y plane, so we need to halve
|
||||
// the edge width here.
|
||||
uint32_t planeEdgeWidth = edgeWidth / (i ? 2 : 1);
|
||||
|
||||
// Add buffer offset, plus a horizontal bar |edgeWidth| pixels high at the
|
||||
// top of the frame, plus |edgeWidth| pixels from the left of the frame.
|
||||
aFrame->data[i] += reinterpret_cast<ptrdiff_t>(
|
||||
buffer + planeEdgeWidth * aFrame->linesize[i] + planeEdgeWidth);
|
||||
}
|
||||
|
||||
// Unused, but needs to be non-zero to keep ffmpeg happy.
|
||||
aFrame->type = GECKO_FRAME_TYPE;
|
||||
|
||||
aFrame->extended_data = aFrame->data;
|
||||
aFrame->width = aCodecContext->width;
|
||||
aFrame->height = aCodecContext->height;
|
||||
|
||||
mozilla::layers::PlanarYCbCrData data;
|
||||
PlanarYCbCrDataFromAVFrame(data, aFrame);
|
||||
ycbcr->SetDataNoCopy(data);
|
||||
|
||||
mCurrentImage.swap(image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegH264Decoder::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample> >(
|
||||
this, &FFmpegH264Decoder::DecodeFrame,
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FFmpegH264Decoder::OutputDelayedFrames()
|
||||
{
|
||||
while (!mDelayedFrames.IsEmpty()) {
|
||||
mCallback->Output(mDelayedFrames.Pop());
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegH264Decoder::Drain()
|
||||
{
|
||||
// The maximum number of frames that can be waiting to be decoded is
|
||||
// max_b_frames + 1: One P frame and max_b_frames B frames.
|
||||
for (int32_t i = 0; i <= mCodecContext.max_b_frames; i++) {
|
||||
// An empty frame tells FFmpeg to decode the next delayed frame it has in
|
||||
// its queue, if it has any.
|
||||
nsAutoPtr<MP4Sample> empty(new MP4Sample(0 /* dts */, 0 /* cts */,
|
||||
0 /* duration */, 0 /* offset */,
|
||||
new std::vector<uint8_t>(),
|
||||
mp4_demuxer::kVideo, nullptr,
|
||||
false));
|
||||
|
||||
nsresult rv = Input(empty.forget());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethod(this, &FFmpegH264Decoder::OutputDelayedFrames));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegH264Decoder::Flush()
|
||||
{
|
||||
nsresult rv = FFmpegDataDecoder::Flush();
|
||||
// Even if the above fails we may as well clear our frame queue.
|
||||
mDelayedFrames.Clear();
|
||||
return rv;
|
||||
}
|
||||
|
||||
FFmpegH264Decoder::~FFmpegH264Decoder() {
|
||||
MOZ_COUNT_DTOR(FFmpegH264Decoder);
|
||||
MOZ_ASSERT(mDelayedFrames.IsEmpty());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
84
content/media/fmp4/ffmpeg/FFmpegH264Decoder.h
Normal file
84
content/media/fmp4/ffmpeg/FFmpegH264Decoder.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegH264Decoder_h__
|
||||
#define __FFmpegH264Decoder_h__
|
||||
|
||||
#include "nsTPriorityQueue.h"
|
||||
|
||||
#include "FFmpegDataDecoder.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
class FFmpegH264Decoder : public FFmpegDataDecoder
|
||||
{
|
||||
typedef mozilla::layers::Image Image;
|
||||
typedef mozilla::layers::ImageContainer ImageContainer;
|
||||
|
||||
public:
|
||||
FFmpegH264Decoder(MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
const mp4_demuxer::VideoDecoderConfig &aConfig,
|
||||
ImageContainer* aImageContainer);
|
||||
virtual ~FFmpegH264Decoder();
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
void DecodeFrame(mp4_demuxer::MP4Sample* aSample);
|
||||
void OutputDelayedFrames();
|
||||
|
||||
/**
|
||||
* This method allocates a buffer for FFmpeg's decoder, wrapped in an Image.
|
||||
* Currently it only supports Planar YUV420, which appears to be the only
|
||||
* non-hardware accelerated image format that FFmpeg's H264 decoder is
|
||||
* capable of outputting.
|
||||
*/
|
||||
int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
|
||||
AVFrame* aFrame);
|
||||
|
||||
static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
|
||||
|
||||
mp4_demuxer::VideoDecoderConfig mConfig;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
nsRefPtr<ImageContainer> mImageContainer;
|
||||
|
||||
/**
|
||||
* Pass Image back from the allocator to our DoDecode method.
|
||||
* We *should* return the image back through ffmpeg wrapped in an AVFrame like
|
||||
* we're meant to. However, if avcodec_decode_video2 fails, it returns a
|
||||
* completely different frame from the one holding our image and it will be
|
||||
* leaked.
|
||||
* This could be handled in the future by wrapping our Image in a reference
|
||||
* counted AVBuffer and letting ffmpeg hold the nsAutoPtr<Image>, but
|
||||
* currently we have to support older versions of ffmpeg which lack
|
||||
* refcounting.
|
||||
*/
|
||||
nsRefPtr<Image> mCurrentImage;
|
||||
|
||||
struct VideoDataComparator
|
||||
{
|
||||
bool LessThan(VideoData* const &a, VideoData* const &b) const
|
||||
{
|
||||
return a->mTime < b->mTime;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* FFmpeg returns frames in DTS order, so we need to keep a heap of the
|
||||
* previous MAX_B_FRAMES + 1 frames (all B frames in a GOP + one P frame)
|
||||
* ordered by PTS to make sure we present video frames in the intended order.
|
||||
*/
|
||||
nsTPriorityQueue<VideoData*, VideoDataComparator> mDelayedFrames;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __FFmpegH264Decoder_h__
|
85
content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
Normal file
85
content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 <dlfcn.h>
|
||||
|
||||
#include "nsDebug.h"
|
||||
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
// For FFMPEG_LOG
|
||||
#include "FFmpegDecoderModule.h"
|
||||
|
||||
#define NUM_ELEMENTS(X) (sizeof(X) / sizeof((X)[0]))
|
||||
|
||||
#define LIBAVCODEC 0
|
||||
#define LIBAVFORMAT 1
|
||||
#define LIBAVUTIL 2
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
FFmpegRuntimeLinker::LinkStatus FFmpegRuntimeLinker::sLinkStatus =
|
||||
LinkStatus_INIT;
|
||||
|
||||
static const char * const sLibNames[] = {
|
||||
"libavcodec.so.53", "libavformat.so.53", "libavutil.so.51",
|
||||
};
|
||||
|
||||
void* FFmpegRuntimeLinker::sLinkedLibs[NUM_ELEMENTS(sLibNames)] = {
|
||||
nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
#define AV_FUNC(lib, func) typeof(func) func;
|
||||
#include "FFmpegFunctionList.h"
|
||||
#undef AV_FUNC
|
||||
|
||||
/* static */ bool
|
||||
FFmpegRuntimeLinker::Link()
|
||||
{
|
||||
if (sLinkStatus) {
|
||||
return sLinkStatus == LinkStatus_SUCCEEDED;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < NUM_ELEMENTS(sLinkedLibs); i++) {
|
||||
if (!(sLinkedLibs[i] = dlopen(sLibNames[i], RTLD_NOW | RTLD_LOCAL))) {
|
||||
NS_WARNING("Couldn't link ffmpeg libraries.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
#define AV_FUNC(lib, func) \
|
||||
func = (typeof(func))dlsym(sLinkedLibs[lib], #func); \
|
||||
if (!func) { \
|
||||
NS_WARNING("Couldn't load FFmpeg function " #func "."); \
|
||||
goto fail; \
|
||||
}
|
||||
#include "FFmpegFunctionList.h"
|
||||
#undef AV_FUNC
|
||||
|
||||
sLinkStatus = LinkStatus_SUCCEEDED;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
Unlink();
|
||||
|
||||
sLinkStatus = LinkStatus_FAILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FFmpegRuntimeLinker::Unlink()
|
||||
{
|
||||
FFMPEG_LOG("Unlinking ffmpeg libraries.");
|
||||
for (uint32_t i = 0; i < NUM_ELEMENTS(sLinkedLibs); i++) {
|
||||
if (sLinkedLibs[i]) {
|
||||
dlclose(sLinkedLibs[i]);
|
||||
sLinkedLibs[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
44
content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.h
Normal file
44
content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __FFmpegRuntimeLinker_h__
|
||||
#define __FFmpegRuntimeLinker_h__
|
||||
|
||||
extern "C" {
|
||||
#pragma GCC visibility push(default)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#pragma GCC visibility pop
|
||||
}
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
class FFmpegRuntimeLinker
|
||||
{
|
||||
public:
|
||||
static bool Link();
|
||||
static void Unlink();
|
||||
|
||||
private:
|
||||
static void* sLinkedLibs[];
|
||||
|
||||
static enum LinkStatus {
|
||||
LinkStatus_INIT = 0,
|
||||
LinkStatus_FAILED,
|
||||
LinkStatus_SUCCEEDED
|
||||
} sLinkStatus;
|
||||
};
|
||||
|
||||
#define AV_FUNC(lib, func) extern typeof(func)* func;
|
||||
#include "FFmpegFunctionList.h"
|
||||
#undef AV_FUNC
|
||||
}
|
||||
|
||||
#endif // __FFmpegRuntimeLinker_h__
|
@ -129,4 +129,9 @@ if CONFIG['MOZ_WEBSPEECH']:
|
||||
'/content/media/webspeech/synth',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_FFMPEG']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/media/fmp4/ffmpeg/include',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -96,6 +96,10 @@
|
||||
#include "GStreamerFormatHelper.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
#endif
|
||||
|
||||
#include "AudioStream.h"
|
||||
#include "Latency.h"
|
||||
#include "WebAudioUtils.h"
|
||||
@ -363,6 +367,10 @@ nsLayoutStatics::Shutdown()
|
||||
GStreamerFormatHelper::Shutdown();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_FFMPEG
|
||||
FFmpegRuntimeLinker::Unlink();
|
||||
#endif
|
||||
|
||||
AudioStream::ShutdownLibrary();
|
||||
AsyncLatencyLogger::ShutdownLogger();
|
||||
WebAudioUtils::Shutdown();
|
||||
|
@ -196,6 +196,7 @@ pref("media.directshow.enabled", true);
|
||||
#endif
|
||||
#ifdef MOZ_FMP4
|
||||
pref("media.fragmented-mp4.enabled", true);
|
||||
pref("media.fragmented-mp4.ffmpeg.enabled", false);
|
||||
// Denotes that the fragmented MP4 parser can be created by <video> elements.
|
||||
// This is for testing, since the parser can't yet handle non-fragmented MP4,
|
||||
// so it will fail to play most MP4 files.
|
||||
|
Loading…
Reference in New Issue
Block a user