1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,226 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ISOMediaWriter.h" 1.10 +#include "ISOControl.h" 1.11 +#include "ISOMediaBoxes.h" 1.12 +#include "ISOTrackMetadata.h" 1.13 +#include "nsThreadUtils.h" 1.14 +#include "MediaEncoder.h" 1.15 +#include "VideoUtils.h" 1.16 + 1.17 +#undef LOG 1.18 +#ifdef MOZ_WIDGET_GONK 1.19 +#include <android/log.h> 1.20 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args); 1.21 +#else 1.22 +#define LOG(args, ...) 1.23 +#endif 1.24 + 1.25 +namespace mozilla { 1.26 + 1.27 +const static uint32_t FRAG_DURATION = 2 * USECS_PER_S; // microsecond per unit 1.28 + 1.29 +ISOMediaWriter::ISOMediaWriter(uint32_t aType, uint32_t aHint) 1.30 + : ContainerWriter() 1.31 + , mState(MUXING_HEAD) 1.32 + , mBlobReady(false) 1.33 + , mType(0) 1.34 +{ 1.35 + if (aType & CREATE_AUDIO_TRACK) { 1.36 + mType |= Audio_Track; 1.37 + } 1.38 + if (aType & CREATE_VIDEO_TRACK) { 1.39 + mType |= Video_Track; 1.40 + } 1.41 + mControl = new ISOControl(aHint); 1.42 + MOZ_COUNT_CTOR(ISOMediaWriter); 1.43 +} 1.44 + 1.45 +ISOMediaWriter::~ISOMediaWriter() 1.46 +{ 1.47 + MOZ_COUNT_DTOR(ISOMediaWriter); 1.48 +} 1.49 + 1.50 +nsresult 1.51 +ISOMediaWriter::RunState() 1.52 +{ 1.53 + nsresult rv; 1.54 + switch (mState) { 1.55 + case MUXING_HEAD: 1.56 + { 1.57 + rv = mControl->GenerateFtyp(); 1.58 + NS_ENSURE_SUCCESS(rv, rv); 1.59 + rv = mControl->GenerateMoov(); 1.60 + NS_ENSURE_SUCCESS(rv, rv); 1.61 + mState = MUXING_FRAG; 1.62 + break; 1.63 + } 1.64 + case MUXING_FRAG: 1.65 + { 1.66 + rv = mControl->GenerateMoof(mType); 1.67 + NS_ENSURE_SUCCESS(rv, rv); 1.68 + 1.69 + bool EOS; 1.70 + if (ReadyToRunState(EOS) && EOS) { 1.71 + mState = MUXING_DONE; 1.72 + } 1.73 + break; 1.74 + } 1.75 + case MUXING_DONE: 1.76 + { 1.77 + break; 1.78 + } 1.79 + } 1.80 + mBlobReady = true; 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +nsresult 1.85 +ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData, 1.86 + uint32_t aFlags) 1.87 +{ 1.88 + // Muxing complete, it doesn't allowed to reentry again. 1.89 + if (mState == MUXING_DONE) { 1.90 + MOZ_ASSERT(false); 1.91 + return NS_ERROR_FAILURE; 1.92 + } 1.93 + 1.94 + FragmentBuffer* frag = nullptr; 1.95 + uint32_t len = aData.GetEncodedFrames().Length(); 1.96 + 1.97 + if (!len) { 1.98 + // no frame? why bother to WriteEncodedTrack 1.99 + return NS_OK; 1.100 + } 1.101 + for (uint32_t i = 0; i < len; i++) { 1.102 + nsRefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]); 1.103 + EncodedFrame::FrameType type = frame->GetFrameType(); 1.104 + if (type == EncodedFrame::AAC_AUDIO_FRAME || 1.105 + type == EncodedFrame::AAC_CSD || 1.106 + type == EncodedFrame::AMR_AUDIO_FRAME || 1.107 + type == EncodedFrame::AMR_AUDIO_CSD) { 1.108 + frag = mAudioFragmentBuffer; 1.109 + } else if (type == EncodedFrame::AVC_I_FRAME || 1.110 + type == EncodedFrame::AVC_P_FRAME || 1.111 + type == EncodedFrame::AVC_B_FRAME || 1.112 + type == EncodedFrame::AVC_CSD) { 1.113 + frag = mVideoFragmentBuffer; 1.114 + } else { 1.115 + MOZ_ASSERT(0); 1.116 + return NS_ERROR_FAILURE; 1.117 + } 1.118 + 1.119 + frag->AddFrame(frame); 1.120 + } 1.121 + 1.122 + // Encoder should send CSD (codec specific data) frame before sending the 1.123 + // audio/video frames. When CSD data is ready, it is sufficient to generate a 1.124 + // moov data. If encoder doesn't send CSD yet, muxer needs to wait before 1.125 + // generating anything. 1.126 + if (mType & Audio_Track && (!mAudioFragmentBuffer || 1.127 + !mAudioFragmentBuffer->HasCSD())) { 1.128 + return NS_OK; 1.129 + } 1.130 + if (mType & Video_Track && (!mVideoFragmentBuffer || 1.131 + !mVideoFragmentBuffer->HasCSD())) { 1.132 + return NS_OK; 1.133 + } 1.134 + 1.135 + // Only one FrameType in EncodedFrameContainer so it doesn't need to be 1.136 + // inside the for-loop. 1.137 + if (frag && (aFlags & END_OF_STREAM)) { 1.138 + frag->SetEndOfStream(); 1.139 + } 1.140 + 1.141 + nsresult rv; 1.142 + bool EOS; 1.143 + if (ReadyToRunState(EOS)) { 1.144 + // TODO: 1.145 + // The MediaEncoder doesn't use nsRunnable, so thread will be 1.146 + // stocked on that part and the new added nsRunnable won't get to run 1.147 + // before MediaEncoder completing. Before MediaEncoder change, it needs 1.148 + // to call RunState directly. 1.149 + // https://bugzilla.mozilla.org/show_bug.cgi?id=950429 1.150 + rv = RunState(); 1.151 + NS_ENSURE_SUCCESS(rv, rv); 1.152 + } 1.153 + 1.154 + return NS_OK; 1.155 +} 1.156 + 1.157 +bool 1.158 +ISOMediaWriter::ReadyToRunState(bool& aEOS) 1.159 +{ 1.160 + aEOS = false; 1.161 + bool bReadyToMux = true; 1.162 + if ((mType & Audio_Track) && (mType & Video_Track)) { 1.163 + if (!mAudioFragmentBuffer->HasEnoughData()) { 1.164 + bReadyToMux = false; 1.165 + } 1.166 + if (!mVideoFragmentBuffer->HasEnoughData()) { 1.167 + bReadyToMux = false; 1.168 + } 1.169 + 1.170 + if (mAudioFragmentBuffer->EOS() && mVideoFragmentBuffer->EOS()) { 1.171 + aEOS = true; 1.172 + bReadyToMux = true; 1.173 + } 1.174 + } else if (mType == Audio_Track) { 1.175 + if (!mAudioFragmentBuffer->HasEnoughData()) { 1.176 + bReadyToMux = false; 1.177 + } 1.178 + if (mAudioFragmentBuffer->EOS()) { 1.179 + aEOS = true; 1.180 + bReadyToMux = true; 1.181 + } 1.182 + } else if (mType == Video_Track) { 1.183 + if (!mVideoFragmentBuffer->HasEnoughData()) { 1.184 + bReadyToMux = false; 1.185 + } 1.186 + if (mVideoFragmentBuffer->EOS()) { 1.187 + aEOS = true; 1.188 + bReadyToMux = true; 1.189 + } 1.190 + } 1.191 + 1.192 + return bReadyToMux; 1.193 +} 1.194 + 1.195 +nsresult 1.196 +ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs, 1.197 + uint32_t aFlags) 1.198 +{ 1.199 + if (mBlobReady) { 1.200 + if (mState == MUXING_DONE) { 1.201 + mIsWritingComplete = true; 1.202 + } 1.203 + mBlobReady = false; 1.204 + return mControl->GetBufs(aOutputBufs); 1.205 + } 1.206 + return NS_OK; 1.207 +} 1.208 + 1.209 +nsresult 1.210 +ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata) 1.211 +{ 1.212 + if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC || 1.213 + aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) { 1.214 + mControl->SetMetadata(aMetadata); 1.215 + mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION); 1.216 + mControl->SetFragment(mAudioFragmentBuffer); 1.217 + return NS_OK; 1.218 + } 1.219 + if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) { 1.220 + mControl->SetMetadata(aMetadata); 1.221 + mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION); 1.222 + mControl->SetFragment(mVideoFragmentBuffer); 1.223 + return NS_OK; 1.224 + } 1.225 + 1.226 + return NS_ERROR_FAILURE; 1.227 +} 1.228 + 1.229 +} // namespace mozilla