1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "libavutil/dict.h"
9 #include "libavcodec/avcodec.h"
14 #include "FFmpegDataDecoder.h"
15 #include "FFmpegLog.h"
16 #include "mozilla/StaticPrefs_media.h"
17 #include "mozilla/TaskQueue.h"
19 #include "VideoUtils.h"
20 #include "FFmpegUtils.h"
22 #include "FFmpegLibs.h"
26 StaticMutex FFmpegDataDecoder
<LIBAV_VER
>::sMutex
;
28 FFmpegDataDecoder
<LIBAV_VER
>::FFmpegDataDecoder(FFmpegLibWrapper
* aLib
,
31 mCodecContext(nullptr),
32 mCodecParser(nullptr),
36 mVideoCodec(IsVideoCodec(aCodecID
)),
37 mTaskQueue(TaskQueue::Create(
38 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER
),
39 "FFmpegDataDecoder")),
40 mLastInputDts(media::TimeUnit::FromNegativeInfinity()) {
42 MOZ_COUNT_CTOR(FFmpegDataDecoder
);
45 FFmpegDataDecoder
<LIBAV_VER
>::~FFmpegDataDecoder() {
46 MOZ_COUNT_DTOR(FFmpegDataDecoder
);
48 mLib
->av_parser_close(mCodecParser
);
49 mCodecParser
= nullptr;
53 MediaResult FFmpegDataDecoder
<LIBAV_VER
>::AllocateExtraData() {
55 mCodecContext
->extradata_size
= mExtraData
->Length();
56 // FFmpeg may use SIMD instructions to access the data which reads the
57 // data in 32 bytes block. Must ensure we have enough data to read.
58 uint32_t padding_size
=
59 #if LIBAVCODEC_VERSION_MAJOR >= 58
60 AV_INPUT_BUFFER_PADDING_SIZE
;
62 FF_INPUT_BUFFER_PADDING_SIZE
;
64 mCodecContext
->extradata
= static_cast<uint8_t*>(
65 mLib
->av_malloc(mExtraData
->Length() + padding_size
));
66 if (!mCodecContext
->extradata
) {
67 return MediaResult(NS_ERROR_OUT_OF_MEMORY
,
68 RESULT_DETAIL("Couldn't init ffmpeg extradata"));
70 memcpy(mCodecContext
->extradata
, mExtraData
->Elements(),
71 mExtraData
->Length());
73 mCodecContext
->extradata_size
= 0;
79 // Note: This doesn't run on the ffmpeg TaskQueue, it runs on some other media
81 MediaResult FFmpegDataDecoder
<LIBAV_VER
>::InitDecoder(AVDictionary
** aOptions
) {
82 FFMPEG_LOG("Initialising FFmpeg decoder");
84 AVCodec
* codec
= FindAVCodec(mLib
, mCodecID
);
86 FFMPEG_LOG(" couldn't find ffmpeg decoder for codec id %d", mCodecID
);
87 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR
,
88 RESULT_DETAIL("unable to find codec"));
90 // openh264 has broken decoding of some h264 videos so
91 // don't use it unless explicitly allowed for now.
92 if (!strcmp(codec
->name
, "libopenh264") &&
93 !StaticPrefs::media_ffmpeg_allow_openh264()) {
94 FFMPEG_LOG(" unable to find codec (openh264 disabled by pref)");
96 NS_ERROR_DOM_MEDIA_FATAL_ERR
,
97 RESULT_DETAIL("unable to find codec (openh264 disabled by pref)"));
99 FFMPEG_LOG(" codec %s : %s", codec
->name
, codec
->long_name
);
101 StaticMutexAutoLock
mon(sMutex
);
103 if (!(mCodecContext
= mLib
->avcodec_alloc_context3(codec
))) {
104 FFMPEG_LOG(" couldn't allocate ffmpeg context for codec %s", codec
->name
);
105 return MediaResult(NS_ERROR_OUT_OF_MEMORY
,
106 RESULT_DETAIL("Couldn't init ffmpeg context"));
110 MOZ_ASSERT(mCodecParser
== nullptr);
111 mCodecParser
= mLib
->av_parser_init(mCodecID
);
113 mCodecParser
->flags
|= ParserFlags();
116 mCodecContext
->opaque
= this;
119 MediaResult ret
= AllocateExtraData();
120 if (NS_FAILED(ret
)) {
121 FFMPEG_LOG(" couldn't allocate ffmpeg extra data for codec %s",
123 mLib
->av_freep(&mCodecContext
);
127 #if LIBAVCODEC_VERSION_MAJOR < 57
128 if (codec
->capabilities
& CODEC_CAP_DR1
) {
129 mCodecContext
->flags
|= CODEC_FLAG_EMU_EDGE
;
133 if (mLib
->avcodec_open2(mCodecContext
, codec
, aOptions
) < 0) {
134 if (mCodecContext
->extradata
) {
135 mLib
->av_freep(&mCodecContext
->extradata
);
137 mLib
->av_freep(&mCodecContext
);
138 FFMPEG_LOG(" Couldn't open avcodec");
139 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR
,
140 RESULT_DETAIL("Couldn't open avcodec"));
143 FFMPEG_LOG(" FFmpeg decoder init successful.");
147 RefPtr
<ShutdownPromise
> FFmpegDataDecoder
<LIBAV_VER
>::Shutdown() {
148 RefPtr
<FFmpegDataDecoder
<LIBAV_VER
>> self
= this;
149 return InvokeAsync(mTaskQueue
, __func__
, [self
]() {
150 self
->ProcessShutdown();
151 return self
->mTaskQueue
->BeginShutdown();
155 RefPtr
<MediaDataDecoder::DecodePromise
> FFmpegDataDecoder
<LIBAV_VER
>::Decode(
156 MediaRawData
* aSample
) {
157 return InvokeAsync
<MediaRawData
*>(mTaskQueue
, this, __func__
,
158 &FFmpegDataDecoder::ProcessDecode
, aSample
);
161 RefPtr
<MediaDataDecoder::DecodePromise
>
162 FFmpegDataDecoder
<LIBAV_VER
>::ProcessDecode(MediaRawData
* aSample
) {
163 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
164 PROCESS_DECODE_LOG(aSample
);
165 bool gotFrame
= false;
167 MediaResult rv
= DoDecode(aSample
, &gotFrame
, results
);
169 return DecodePromise::CreateAndReject(rv
, __func__
);
171 return DecodePromise::CreateAndResolve(std::move(results
), __func__
);
174 MediaResult FFmpegDataDecoder
<LIBAV_VER
>::DoDecode(
175 MediaRawData
* aSample
, bool* aGotFrame
,
176 MediaDataDecoder::DecodedData
& aResults
) {
177 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
179 uint8_t* inputData
= const_cast<uint8_t*>(aSample
->Data());
180 size_t inputSize
= aSample
->Size();
182 mLastInputDts
= aSample
->mTimecode
;
184 if (inputData
&& mCodecParser
) { // inputData is null when draining.
189 uint8_t* data
= inputData
;
190 int size
= inputSize
;
191 int len
= mLib
->av_parser_parse2(
192 mCodecParser
, mCodecContext
, &data
, &size
, inputData
, inputSize
,
193 aSample
->mTime
.ToMicroseconds(), aSample
->mTimecode
.ToMicroseconds(),
195 if (size_t(len
) > inputSize
) {
196 return NS_ERROR_DOM_MEDIA_DECODE_ERR
;
199 bool gotFrame
= false;
200 MediaResult rv
= DoDecode(aSample
, data
, size
, &gotFrame
, aResults
);
204 if (gotFrame
&& aGotFrame
) {
213 return DoDecode(aSample
, inputData
, inputSize
, aGotFrame
, aResults
);
216 RefPtr
<MediaDataDecoder::FlushPromise
> FFmpegDataDecoder
<LIBAV_VER
>::Flush() {
217 return InvokeAsync(mTaskQueue
, this, __func__
,
218 &FFmpegDataDecoder
<LIBAV_VER
>::ProcessFlush
);
221 RefPtr
<MediaDataDecoder::DecodePromise
> FFmpegDataDecoder
<LIBAV_VER
>::Drain() {
222 return InvokeAsync(mTaskQueue
, this, __func__
,
223 &FFmpegDataDecoder
<LIBAV_VER
>::ProcessDrain
);
226 RefPtr
<MediaDataDecoder::DecodePromise
>
227 FFmpegDataDecoder
<LIBAV_VER
>::ProcessDrain() {
228 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
229 FFMPEG_LOG("FFmpegDataDecoder: draining buffers");
230 RefPtr
<MediaRawData
> empty(new MediaRawData());
231 empty
->mTimecode
= mLastInputDts
;
232 bool gotFrame
= false;
234 // When draining the FFmpeg decoder will return either a single frame at a
235 // time until gotFrame is set to false; or return a block of frames with
236 // NS_ERROR_DOM_MEDIA_END_OF_STREAM
237 while (NS_SUCCEEDED(DoDecode(empty
, &gotFrame
, results
)) && gotFrame
) {
239 return DecodePromise::CreateAndResolve(std::move(results
), __func__
);
242 RefPtr
<MediaDataDecoder::FlushPromise
>
243 FFmpegDataDecoder
<LIBAV_VER
>::ProcessFlush() {
244 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
246 FFMPEG_LOG("FFmpegDataDecoder: flushing buffers");
247 mLib
->avcodec_flush_buffers(mCodecContext
);
250 FFMPEG_LOG("FFmpegDataDecoder: reinitializing parser");
251 mLib
->av_parser_close(mCodecParser
);
252 mCodecParser
= mLib
->av_parser_init(mCodecID
);
254 return FlushPromise::CreateAndResolve(true, __func__
);
257 void FFmpegDataDecoder
<LIBAV_VER
>::ProcessShutdown() {
258 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
259 StaticMutexAutoLock
mon(sMutex
);
262 FFMPEG_LOG("FFmpegDataDecoder: shutdown");
263 if (mCodecContext
->extradata
) {
264 mLib
->av_freep(&mCodecContext
->extradata
);
266 mLib
->avcodec_close(mCodecContext
);
267 mLib
->av_freep(&mCodecContext
);
268 #if LIBAVCODEC_VERSION_MAJOR >= 55
269 mLib
->av_frame_free(&mFrame
);
270 #elif LIBAVCODEC_VERSION_MAJOR == 54
271 mLib
->avcodec_free_frame(&mFrame
);
273 mLib
->av_freep(&mFrame
);
278 AVFrame
* FFmpegDataDecoder
<LIBAV_VER
>::PrepareFrame() {
279 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
280 #if LIBAVCODEC_VERSION_MAJOR >= 55
282 mLib
->av_frame_unref(mFrame
);
284 mFrame
= mLib
->av_frame_alloc();
286 #elif LIBAVCODEC_VERSION_MAJOR == 54
288 mLib
->avcodec_get_frame_defaults(mFrame
);
290 mFrame
= mLib
->avcodec_alloc_frame();
293 mLib
->av_freep(&mFrame
);
294 mFrame
= mLib
->avcodec_alloc_frame();
299 /* static */ AVCodec
* FFmpegDataDecoder
<LIBAV_VER
>::FindAVCodec(
300 FFmpegLibWrapper
* aLib
, AVCodecID aCodec
) {
301 return aLib
->avcodec_find_decoder(aCodec
);
304 #ifdef MOZ_WIDGET_GTK
305 /* static */ AVCodec
* FFmpegDataDecoder
<LIBAV_VER
>::FindHardwareAVCodec(
306 FFmpegLibWrapper
* aLib
, AVCodecID aCodec
) {
307 void* opaque
= nullptr;
308 while (AVCodec
* codec
= aLib
->av_codec_iterate(&opaque
)) {
309 if (codec
->id
== aCodec
&& aLib
->av_codec_is_decoder(codec
) &&
310 aLib
->avcodec_get_hw_config(codec
, 0)) {
318 } // namespace mozilla