content/media/webm/EbmlComposer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webm/EbmlComposer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,212 @@
     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 "EbmlComposer.h"
    1.10 +#include "libmkv/EbmlIDs.h"
    1.11 +#include "libmkv/EbmlWriter.h"
    1.12 +#include "libmkv/WebMElement.h"
    1.13 +#include "prtime.h"
    1.14 +
    1.15 +namespace mozilla {
    1.16 +
    1.17 +// Timecode scale in nanoseconds
    1.18 +static const unsigned long TIME_CODE_SCALE = 1000000;
    1.19 +// The WebM header size without audio CodecPrivateData
    1.20 +static const int32_t DEFAULT_HEADER_SIZE = 1024;
    1.21 +
    1.22 +void EbmlComposer::GenerateHeader()
    1.23 +{
    1.24 +  // Write the EBML header.
    1.25 +  EbmlGlobal ebml;
    1.26 +  // The WEbM header default size usually smaller than 1k.
    1.27 +  nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE +
    1.28 +                                             mCodecPrivateData.Length()]);
    1.29 +  ebml.buf = buffer.get();
    1.30 +  ebml.offset = 0;
    1.31 +  writeHeader(&ebml);
    1.32 +  {
    1.33 +    EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
    1.34 +    Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
    1.35 +    {
    1.36 +      Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
    1.37 +      // Todo: We don't know the exact sizes of encoded data and
    1.38 +      // ignore this section.
    1.39 +      Ebml_EndSubElement(&ebml, &ebmlLocseg);
    1.40 +      writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
    1.41 +      {
    1.42 +        EbmlLoc trackLoc;
    1.43 +        Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
    1.44 +        {
    1.45 +          // Video
    1.46 +          if (mWidth > 0 && mHeight > 0) {
    1.47 +            writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
    1.48 +                            mWidth, mHeight,
    1.49 +                            mDisplayWidth, mDisplayHeight, mFrameRate);
    1.50 +          }
    1.51 +          // Audio
    1.52 +          if (mCodecPrivateData.Length() > 0) {
    1.53 +            writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq,
    1.54 +                            mChannels, mCodecPrivateData.Elements(),
    1.55 +                            mCodecPrivateData.Length());
    1.56 +          }
    1.57 +        }
    1.58 +        Ebml_EndSubElement(&ebml, &trackLoc);
    1.59 +      }
    1.60 +    }
    1.61 +    // The Recording length is unknown and
    1.62 +    // ignore write the whole Segment element size
    1.63 +  }
    1.64 +  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
    1.65 +             "write more data > EBML_BUFFER_SIZE");
    1.66 +  auto block = mClusterBuffs.AppendElement();
    1.67 +  block->SetLength(ebml.offset);
    1.68 +  memcpy(block->Elements(), ebml.buf, ebml.offset);
    1.69 +  mFlushState |= FLUSH_METADATA;
    1.70 +}
    1.71 +
    1.72 +void EbmlComposer::FinishMetadata()
    1.73 +{
    1.74 +  if (mFlushState & FLUSH_METADATA) {
    1.75 +    // We don't remove the first element of mClusterBuffs because the
    1.76 +    // |mClusterHeaderIndex| may have value.
    1.77 +    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
    1.78 +    mFlushState &= ~FLUSH_METADATA;
    1.79 +  }
    1.80 +}
    1.81 +
    1.82 +void EbmlComposer::FinishCluster()
    1.83 +{
    1.84 +  FinishMetadata();
    1.85 +  if (!(mFlushState & FLUSH_CLUSTER)) {
    1.86 +    // No completed cluster available.
    1.87 +    return;
    1.88 +  }
    1.89 +
    1.90 +  MOZ_ASSERT(mClusterLengthLoc > 0);
    1.91 +  EbmlGlobal ebml;
    1.92 +  EbmlLoc ebmlLoc;
    1.93 +  ebmlLoc.offset = mClusterLengthLoc;
    1.94 +  ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length();
    1.95 +  ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
    1.96 +  Ebml_EndSubElement(&ebml, &ebmlLoc);
    1.97 +  // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
    1.98 +  // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
    1.99 +  for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
   1.100 +    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
   1.101 +  }
   1.102 +
   1.103 +  mClusterHeaderIndex = 0;
   1.104 +  mClusterLengthLoc = 0;
   1.105 +  mClusterBuffs.Clear();
   1.106 +  mFlushState &= ~FLUSH_CLUSTER;
   1.107 +}
   1.108 +
   1.109 +void
   1.110 +EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
   1.111 +{
   1.112 +  EbmlGlobal ebml;
   1.113 +  ebml.offset = 0;
   1.114 +
   1.115 +  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
   1.116 +    FinishCluster();
   1.117 +  }
   1.118 +
   1.119 +  auto block = mClusterBuffs.AppendElement();
   1.120 +  block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
   1.121 +  ebml.buf = block->Elements();
   1.122 +
   1.123 +  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
   1.124 +    EbmlLoc ebmlLoc;
   1.125 +    Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
   1.126 +    MOZ_ASSERT(mClusterBuffs.Length() > 0);
   1.127 +    // current cluster header array index
   1.128 +    mClusterHeaderIndex = mClusterBuffs.Length() - 1;
   1.129 +    mClusterLengthLoc = ebmlLoc.offset;
   1.130 +    mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
   1.131 +    Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
   1.132 +    mFlushState |= FLUSH_CLUSTER;
   1.133 +  }
   1.134 +
   1.135 +  if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
   1.136 +    short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
   1.137 +                     - mClusterTimecode;
   1.138 +    writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
   1.139 +                     EncodedFrame::FrameType::VP8_I_FRAME,
   1.140 +                     0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
   1.141 +                     aFrame->GetFrameData().Length());
   1.142 +  } else {
   1.143 +    writeSimpleBlock(&ebml, 0x2, 0, false,
   1.144 +                     0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
   1.145 +                     aFrame->GetFrameData().Length());
   1.146 +  }
   1.147 +  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
   1.148 +             aFrame->GetFrameData().Length(),
   1.149 +             "write more data > EBML_BUFFER_SIZE");
   1.150 +  block->SetLength(ebml.offset);
   1.151 +}
   1.152 +
   1.153 +void
   1.154 +EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
   1.155 +                             uint32_t aDisplayWidth, uint32_t aDisplayHeight,
   1.156 +                             float aFrameRate)
   1.157 +{
   1.158 +  MOZ_ASSERT(aWidth > 0, "Width should > 0");
   1.159 +  MOZ_ASSERT(aHeight > 0, "Height should > 0");
   1.160 +  MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
   1.161 +  MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
   1.162 +  MOZ_ASSERT(aFrameRate > 0, "FrameRate should > 0");
   1.163 +  mWidth = aWidth;
   1.164 +  mHeight = aHeight;
   1.165 +  mDisplayWidth = aDisplayWidth;
   1.166 +  mDisplayHeight = aDisplayHeight;
   1.167 +  mFrameRate = aFrameRate;
   1.168 +}
   1.169 +
   1.170 +void
   1.171 +EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels,
   1.172 +                             uint32_t aBitDepth)
   1.173 +{
   1.174 +  MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
   1.175 +  MOZ_ASSERT(aBitDepth > 0, "BitDepth should > 0");
   1.176 +  MOZ_ASSERT(aChannels > 0, "Channels should > 0");
   1.177 +  mSampleFreq = aSampleFreq;
   1.178 +  mBitDepth = aBitDepth;
   1.179 +  mChannels = aChannels;
   1.180 +}
   1.181 +
   1.182 +void
   1.183 +EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
   1.184 +                            uint32_t aFlag)
   1.185 +{
   1.186 +  if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
   1.187 +      (aFlag & ContainerWriter::GET_HEADER))
   1.188 +  {
   1.189 +    FinishMetadata();
   1.190 +  }
   1.191 +  if (aFlag & ContainerWriter::FLUSH_NEEDED)
   1.192 +  {
   1.193 +    FinishCluster();
   1.194 +  }
   1.195 +  // aDestBufs may have some element
   1.196 +  for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
   1.197 +    aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
   1.198 +  }
   1.199 +  mClusterCanFlushBuffs.Clear();
   1.200 +}
   1.201 +
   1.202 +EbmlComposer::EbmlComposer()
   1.203 +  : mFlushState(FLUSH_NONE)
   1.204 +  , mClusterHeaderIndex(0)
   1.205 +  , mClusterLengthLoc(0)
   1.206 +  , mClusterTimecode(0)
   1.207 +  , mWidth(0)
   1.208 +  , mHeight(0)
   1.209 +  , mFrameRate(0)
   1.210 +  , mSampleFreq(0)
   1.211 +  , mBitDepth(0)
   1.212 +  , mChannels(0)
   1.213 +{}
   1.214 +
   1.215 +}

mercurial