Backed out 7 changesets (bug 1839993) for causing build bustages on DecoderTemplate...
[gecko.git] / dom / media / platforms / ffmpeg / FFmpegDataDecoder.cpp
blob30422987cf2a772a4c26e2c6129be9cffbfa5a68
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/. */
7 #include <string.h>
8 #include "libavutil/dict.h"
9 #include "libavcodec/avcodec.h"
10 #ifdef __GNUC__
11 # include <unistd.h>
12 #endif
14 #include "FFmpegDataDecoder.h"
15 #include "FFmpegLog.h"
16 #include "mozilla/StaticPrefs_media.h"
17 #include "mozilla/TaskQueue.h"
18 #include "prsystem.h"
19 #include "VideoUtils.h"
20 #include "FFmpegUtils.h"
22 #include "FFmpegLibs.h"
24 namespace mozilla {
26 StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMutex;
28 FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FFmpegLibWrapper* aLib,
29 AVCodecID aCodecID)
30 : mLib(aLib),
31 mCodecContext(nullptr),
32 mCodecParser(nullptr),
33 mFrame(nullptr),
34 mExtraData(nullptr),
35 mCodecID(aCodecID),
36 mVideoCodec(IsVideoCodec(aCodecID)),
37 mTaskQueue(TaskQueue::Create(
38 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
39 "FFmpegDataDecoder")),
40 mLastInputDts(media::TimeUnit::FromNegativeInfinity()) {
41 MOZ_ASSERT(aLib);
42 MOZ_COUNT_CTOR(FFmpegDataDecoder);
45 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder() {
46 MOZ_COUNT_DTOR(FFmpegDataDecoder);
47 if (mCodecParser) {
48 mLib->av_parser_close(mCodecParser);
49 mCodecParser = nullptr;
53 MediaResult FFmpegDataDecoder<LIBAV_VER>::AllocateExtraData() {
54 if (mExtraData) {
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;
61 #else
62 FF_INPUT_BUFFER_PADDING_SIZE;
63 #endif
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());
72 } else {
73 mCodecContext->extradata_size = 0;
76 return NS_OK;
79 // Note: This doesn't run on the ffmpeg TaskQueue, it runs on some other media
80 // taskqueue
81 MediaResult FFmpegDataDecoder<LIBAV_VER>::InitDecoder(AVDictionary** aOptions) {
82 FFMPEG_LOG("Initialising FFmpeg decoder");
84 AVCodec* codec = FindAVCodec(mLib, mCodecID);
85 if (!codec) {
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)");
95 return MediaResult(
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"));
109 if (NeedParser()) {
110 MOZ_ASSERT(mCodecParser == nullptr);
111 mCodecParser = mLib->av_parser_init(mCodecID);
112 if (mCodecParser) {
113 mCodecParser->flags |= ParserFlags();
116 mCodecContext->opaque = this;
118 InitCodecContext();
119 MediaResult ret = AllocateExtraData();
120 if (NS_FAILED(ret)) {
121 FFMPEG_LOG(" couldn't allocate ffmpeg extra data for codec %s",
122 codec->name);
123 mLib->av_freep(&mCodecContext);
124 return ret;
127 #if LIBAVCODEC_VERSION_MAJOR < 57
128 if (codec->capabilities & CODEC_CAP_DR1) {
129 mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
131 #endif
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.");
144 return NS_OK;
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;
166 DecodedData results;
167 MediaResult rv = DoDecode(aSample, &gotFrame, results);
168 if (NS_FAILED(rv)) {
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.
185 if (aGotFrame) {
186 *aGotFrame = false;
188 while (inputSize) {
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(),
194 aSample->mOffset);
195 if (size_t(len) > inputSize) {
196 return NS_ERROR_DOM_MEDIA_DECODE_ERR;
198 if (size) {
199 bool gotFrame = false;
200 MediaResult rv = DoDecode(aSample, data, size, &gotFrame, aResults);
201 if (NS_FAILED(rv)) {
202 return rv;
204 if (gotFrame && aGotFrame) {
205 *aGotFrame = true;
208 inputData += len;
209 inputSize -= len;
211 return NS_OK;
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;
233 DecodedData results;
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());
245 if (mCodecContext) {
246 FFMPEG_LOG("FFmpegDataDecoder: flushing buffers");
247 mLib->avcodec_flush_buffers(mCodecContext);
249 if (mCodecParser) {
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);
261 if (mCodecContext) {
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);
272 #else
273 mLib->av_freep(&mFrame);
274 #endif
278 AVFrame* FFmpegDataDecoder<LIBAV_VER>::PrepareFrame() {
279 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread());
280 #if LIBAVCODEC_VERSION_MAJOR >= 55
281 if (mFrame) {
282 mLib->av_frame_unref(mFrame);
283 } else {
284 mFrame = mLib->av_frame_alloc();
286 #elif LIBAVCODEC_VERSION_MAJOR == 54
287 if (mFrame) {
288 mLib->avcodec_get_frame_defaults(mFrame);
289 } else {
290 mFrame = mLib->avcodec_alloc_frame();
292 #else
293 mLib->av_freep(&mFrame);
294 mFrame = mLib->avcodec_alloc_frame();
295 #endif
296 return mFrame;
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)) {
311 return codec;
314 return nullptr;
316 #endif
318 } // namespace mozilla