summaryrefslogtreecommitdiffstats
path: root/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp')
-rw-r--r--dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp b/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
new file mode 100644
index 000000000..fa23616e9
--- /dev/null
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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 "ISOMediaWriter.h"
+#include "ISOControl.h"
+#include "ISOMediaBoxes.h"
+#include "ISOTrackMetadata.h"
+#include "nsThreadUtils.h"
+#include "MediaEncoder.h"
+#include "VideoUtils.h"
+#include "GeckoProfiler.h"
+
+#undef LOG
+#ifdef MOZ_WIDGET_GONK
+#include <android/log.h>
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
+#else
+#define LOG(args, ...)
+#endif
+
+namespace mozilla {
+
+const static uint32_t FRAG_DURATION = 2 * USECS_PER_S; // microsecond per unit
+
+ISOMediaWriter::ISOMediaWriter(uint32_t aType, uint32_t aHint)
+ : ContainerWriter()
+ , mState(MUXING_HEAD)
+ , mBlobReady(false)
+ , mType(0)
+{
+ if (aType & CREATE_AUDIO_TRACK) {
+ mType |= Audio_Track;
+ }
+ if (aType & CREATE_VIDEO_TRACK) {
+ mType |= Video_Track;
+ }
+ mControl = new ISOControl(aHint);
+ MOZ_COUNT_CTOR(ISOMediaWriter);
+}
+
+ISOMediaWriter::~ISOMediaWriter()
+{
+ MOZ_COUNT_DTOR(ISOMediaWriter);
+}
+
+nsresult
+ISOMediaWriter::RunState()
+{
+ nsresult rv;
+ switch (mState) {
+ case MUXING_HEAD:
+ {
+ rv = mControl->GenerateFtyp();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = mControl->GenerateMoov();
+ NS_ENSURE_SUCCESS(rv, rv);
+ mState = MUXING_FRAG;
+ break;
+ }
+ case MUXING_FRAG:
+ {
+ rv = mControl->GenerateMoof(mType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool EOS;
+ if (ReadyToRunState(EOS) && EOS) {
+ mState = MUXING_DONE;
+ }
+ break;
+ }
+ case MUXING_DONE:
+ {
+ break;
+ }
+ }
+ mBlobReady = true;
+ return NS_OK;
+}
+
+nsresult
+ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
+ uint32_t aFlags)
+{
+ PROFILER_LABEL("ISOMediaWriter", "WriteEncodedTrack",
+ js::ProfileEntry::Category::OTHER);
+ // Muxing complete, it doesn't allowed to reentry again.
+ if (mState == MUXING_DONE) {
+ MOZ_ASSERT(false);
+ return NS_ERROR_FAILURE;
+ }
+
+ FragmentBuffer* frag = nullptr;
+ uint32_t len = aData.GetEncodedFrames().Length();
+
+ if (!len) {
+ // no frame? why bother to WriteEncodedTrack
+ return NS_OK;
+ }
+ for (uint32_t i = 0; i < len; i++) {
+ RefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]);
+ EncodedFrame::FrameType type = frame->GetFrameType();
+ if (type == EncodedFrame::AAC_AUDIO_FRAME ||
+ type == EncodedFrame::AAC_CSD ||
+ type == EncodedFrame::AMR_AUDIO_FRAME ||
+ type == EncodedFrame::AMR_AUDIO_CSD ||
+ type == EncodedFrame::EVRC_AUDIO_FRAME ||
+ type == EncodedFrame::EVRC_AUDIO_CSD) {
+ frag = mAudioFragmentBuffer;
+ } else if (type == EncodedFrame::AVC_I_FRAME ||
+ type == EncodedFrame::AVC_P_FRAME ||
+ type == EncodedFrame::AVC_B_FRAME ||
+ type == EncodedFrame::AVC_CSD) {
+ frag = mVideoFragmentBuffer;
+ } else {
+ MOZ_ASSERT(0);
+ return NS_ERROR_FAILURE;
+ }
+
+ frag->AddFrame(frame);
+ }
+
+ // Encoder should send CSD (codec specific data) frame before sending the
+ // audio/video frames. When CSD data is ready, it is sufficient to generate a
+ // moov data. If encoder doesn't send CSD yet, muxer needs to wait before
+ // generating anything.
+ if (mType & Audio_Track && (!mAudioFragmentBuffer ||
+ !mAudioFragmentBuffer->HasCSD())) {
+ return NS_OK;
+ }
+ if (mType & Video_Track && (!mVideoFragmentBuffer ||
+ !mVideoFragmentBuffer->HasCSD())) {
+ return NS_OK;
+ }
+
+ // Only one FrameType in EncodedFrameContainer so it doesn't need to be
+ // inside the for-loop.
+ if (frag && (aFlags & END_OF_STREAM)) {
+ frag->SetEndOfStream();
+ }
+
+ nsresult rv;
+ bool EOS;
+ if (ReadyToRunState(EOS)) {
+ // Because track encoder won't generate new data after EOS, it needs to make
+ // sure the state reaches MUXING_DONE when EOS is signaled.
+ do {
+ rv = RunState();
+ } while (EOS && mState != MUXING_DONE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+bool
+ISOMediaWriter::ReadyToRunState(bool& aEOS)
+{
+ aEOS = false;
+ bool bReadyToMux = true;
+ if ((mType & Audio_Track) && (mType & Video_Track)) {
+ if (!mAudioFragmentBuffer->HasEnoughData()) {
+ bReadyToMux = false;
+ }
+ if (!mVideoFragmentBuffer->HasEnoughData()) {
+ bReadyToMux = false;
+ }
+
+ if (mAudioFragmentBuffer->EOS() && mVideoFragmentBuffer->EOS()) {
+ aEOS = true;
+ bReadyToMux = true;
+ }
+ } else if (mType == Audio_Track) {
+ if (!mAudioFragmentBuffer->HasEnoughData()) {
+ bReadyToMux = false;
+ }
+ if (mAudioFragmentBuffer->EOS()) {
+ aEOS = true;
+ bReadyToMux = true;
+ }
+ } else if (mType == Video_Track) {
+ if (!mVideoFragmentBuffer->HasEnoughData()) {
+ bReadyToMux = false;
+ }
+ if (mVideoFragmentBuffer->EOS()) {
+ aEOS = true;
+ bReadyToMux = true;
+ }
+ }
+
+ return bReadyToMux;
+}
+
+nsresult
+ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
+ uint32_t aFlags)
+{
+ PROFILER_LABEL("ISOMediaWriter", "GetContainerData",
+ js::ProfileEntry::Category::OTHER);
+ if (mBlobReady) {
+ if (mState == MUXING_DONE) {
+ mIsWritingComplete = true;
+ }
+ mBlobReady = false;
+ return mControl->GetBufs(aOutputBufs);
+ }
+ return NS_OK;
+}
+
+nsresult
+ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
+{
+ PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
+ js::ProfileEntry::Category::OTHER);
+ if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
+ aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
+ aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
+ mControl->SetMetadata(aMetadata);
+ mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
+ mControl->SetFragment(mAudioFragmentBuffer);
+ return NS_OK;
+ }
+ if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) {
+ mControl->SetMetadata(aMetadata);
+ mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION);
+ mControl->SetFragment(mVideoFragmentBuffer);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+} // namespace mozilla