summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp')
-rw-r--r--dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp204
1 files changed, 204 insertions, 0 deletions
diff --git a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
new file mode 100644
index 000000000..0b31fb0f9
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -0,0 +1,204 @@
+/* -*- 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 "mozilla/SyncRunnable.h"
+#include "mozilla/TaskQueue.h"
+
+#include <string.h>
+#ifdef __GNUC__
+#include <unistd.h>
+#endif
+
+#include "FFmpegLog.h"
+#include "FFmpegDataDecoder.h"
+#include "prsystem.h"
+
+namespace mozilla
+{
+
+StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
+
+ FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FFmpegLibWrapper* aLib,
+ TaskQueue* aTaskQueue,
+ MediaDataDecoderCallback* aCallback,
+ AVCodecID aCodecID)
+ : mLib(aLib)
+ , mCallback(aCallback)
+ , mCodecContext(nullptr)
+ , mFrame(NULL)
+ , mExtraData(nullptr)
+ , mCodecID(aCodecID)
+ , mTaskQueue(aTaskQueue)
+ , mIsFlushing(false)
+{
+ MOZ_ASSERT(aLib);
+ MOZ_COUNT_CTOR(FFmpegDataDecoder);
+}
+
+FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
+{
+ MOZ_COUNT_DTOR(FFmpegDataDecoder);
+}
+
+nsresult
+FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
+{
+ FFMPEG_LOG("Initialising FFmpeg decoder.");
+
+ AVCodec* codec = FindAVCodec(mLib, mCodecID);
+ if (!codec) {
+ NS_WARNING("Couldn't find ffmpeg decoder");
+ return NS_ERROR_FAILURE;
+ }
+
+ StaticMutexAutoLock mon(sMonitor);
+
+ if (!(mCodecContext = mLib->avcodec_alloc_context3(codec))) {
+ NS_WARNING("Couldn't init ffmpeg context");
+ return NS_ERROR_FAILURE;
+ }
+
+ mCodecContext->opaque = this;
+
+ InitCodecContext();
+
+ if (mExtraData) {
+ mCodecContext->extradata_size = mExtraData->Length();
+ // FFmpeg may use SIMD instructions to access the data which reads the
+ // data in 32 bytes block. Must ensure we have enough data to read.
+ mExtraData->AppendElements(FF_INPUT_BUFFER_PADDING_SIZE);
+ mCodecContext->extradata = mExtraData->Elements();
+ } else {
+ mCodecContext->extradata_size = 0;
+ }
+
+ if (codec->capabilities & CODEC_CAP_DR1) {
+ mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
+ }
+
+ if (mLib->avcodec_open2(mCodecContext, codec, nullptr) < 0) {
+ NS_WARNING("Couldn't initialise ffmpeg decoder");
+ mLib->avcodec_close(mCodecContext);
+ mLib->av_freep(&mCodecContext);
+ return NS_ERROR_FAILURE;
+ }
+
+ FFMPEG_LOG("FFmpeg init successful.");
+ return NS_OK;
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::Shutdown()
+{
+ if (mTaskQueue) {
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown);
+ mTaskQueue->Dispatch(runnable.forget());
+ } else {
+ ProcessShutdown();
+ }
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessDecode(MediaRawData* aSample)
+{
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ if (mIsFlushing) {
+ return;
+ }
+ MediaResult rv = DoDecode(aSample);
+ if (NS_FAILED(rv)) {
+ mCallback->Error(rv);
+ } else {
+ mCallback->InputExhausted();
+ }
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
+{
+ mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
+ this, &FFmpegDataDecoder::ProcessDecode, aSample));
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::Flush()
+{
+ MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+ mIsFlushing = true;
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessFlush);
+ SyncRunnable::DispatchToThread(mTaskQueue, runnable);
+ mIsFlushing = false;
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::Drain()
+{
+ MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessDrain);
+ mTaskQueue->Dispatch(runnable.forget());
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
+{
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ if (mCodecContext) {
+ mLib->avcodec_flush_buffers(mCodecContext);
+ }
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
+{
+ StaticMutexAutoLock mon(sMonitor);
+
+ if (mCodecContext) {
+ mLib->avcodec_close(mCodecContext);
+ mLib->av_freep(&mCodecContext);
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+ mLib->av_frame_free(&mFrame);
+#elif LIBAVCODEC_VERSION_MAJOR == 54
+ mLib->avcodec_free_frame(&mFrame);
+#else
+ mLib->av_freep(&mFrame);
+#endif
+ }
+}
+
+AVFrame*
+FFmpegDataDecoder<LIBAV_VER>::PrepareFrame()
+{
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+ if (mFrame) {
+ mLib->av_frame_unref(mFrame);
+ } else {
+ mFrame = mLib->av_frame_alloc();
+ }
+#elif LIBAVCODEC_VERSION_MAJOR == 54
+ if (mFrame) {
+ mLib->avcodec_get_frame_defaults(mFrame);
+ } else {
+ mFrame = mLib->avcodec_alloc_frame();
+ }
+#else
+ mLib->av_freep(&mFrame);
+ mFrame = mLib->avcodec_alloc_frame();
+#endif
+ return mFrame;
+}
+
+/* static */ AVCodec*
+FFmpegDataDecoder<LIBAV_VER>::FindAVCodec(FFmpegLibWrapper* aLib,
+ AVCodecID aCodec)
+{
+ return aLib->avcodec_find_decoder(aCodec);
+}
+
+} // namespace mozilla