content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp

changeset 0
6474c204b198
     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

mercurial