content/media/webm/EbmlComposer.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "EbmlComposer.h"
     7 #include "libmkv/EbmlIDs.h"
     8 #include "libmkv/EbmlWriter.h"
     9 #include "libmkv/WebMElement.h"
    10 #include "prtime.h"
    12 namespace mozilla {
    14 // Timecode scale in nanoseconds
    15 static const unsigned long TIME_CODE_SCALE = 1000000;
    16 // The WebM header size without audio CodecPrivateData
    17 static const int32_t DEFAULT_HEADER_SIZE = 1024;
    19 void EbmlComposer::GenerateHeader()
    20 {
    21   // Write the EBML header.
    22   EbmlGlobal ebml;
    23   // The WEbM header default size usually smaller than 1k.
    24   nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE +
    25                                              mCodecPrivateData.Length()]);
    26   ebml.buf = buffer.get();
    27   ebml.offset = 0;
    28   writeHeader(&ebml);
    29   {
    30     EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
    31     Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
    32     {
    33       Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
    34       // Todo: We don't know the exact sizes of encoded data and
    35       // ignore this section.
    36       Ebml_EndSubElement(&ebml, &ebmlLocseg);
    37       writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
    38       {
    39         EbmlLoc trackLoc;
    40         Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
    41         {
    42           // Video
    43           if (mWidth > 0 && mHeight > 0) {
    44             writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
    45                             mWidth, mHeight,
    46                             mDisplayWidth, mDisplayHeight, mFrameRate);
    47           }
    48           // Audio
    49           if (mCodecPrivateData.Length() > 0) {
    50             writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq,
    51                             mChannels, mCodecPrivateData.Elements(),
    52                             mCodecPrivateData.Length());
    53           }
    54         }
    55         Ebml_EndSubElement(&ebml, &trackLoc);
    56       }
    57     }
    58     // The Recording length is unknown and
    59     // ignore write the whole Segment element size
    60   }
    61   MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
    62              "write more data > EBML_BUFFER_SIZE");
    63   auto block = mClusterBuffs.AppendElement();
    64   block->SetLength(ebml.offset);
    65   memcpy(block->Elements(), ebml.buf, ebml.offset);
    66   mFlushState |= FLUSH_METADATA;
    67 }
    69 void EbmlComposer::FinishMetadata()
    70 {
    71   if (mFlushState & FLUSH_METADATA) {
    72     // We don't remove the first element of mClusterBuffs because the
    73     // |mClusterHeaderIndex| may have value.
    74     mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
    75     mFlushState &= ~FLUSH_METADATA;
    76   }
    77 }
    79 void EbmlComposer::FinishCluster()
    80 {
    81   FinishMetadata();
    82   if (!(mFlushState & FLUSH_CLUSTER)) {
    83     // No completed cluster available.
    84     return;
    85   }
    87   MOZ_ASSERT(mClusterLengthLoc > 0);
    88   EbmlGlobal ebml;
    89   EbmlLoc ebmlLoc;
    90   ebmlLoc.offset = mClusterLengthLoc;
    91   ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length();
    92   ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
    93   Ebml_EndSubElement(&ebml, &ebmlLoc);
    94   // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
    95   // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
    96   for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
    97     mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
    98   }
   100   mClusterHeaderIndex = 0;
   101   mClusterLengthLoc = 0;
   102   mClusterBuffs.Clear();
   103   mFlushState &= ~FLUSH_CLUSTER;
   104 }
   106 void
   107 EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
   108 {
   109   EbmlGlobal ebml;
   110   ebml.offset = 0;
   112   if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
   113     FinishCluster();
   114   }
   116   auto block = mClusterBuffs.AppendElement();
   117   block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
   118   ebml.buf = block->Elements();
   120   if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
   121     EbmlLoc ebmlLoc;
   122     Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
   123     MOZ_ASSERT(mClusterBuffs.Length() > 0);
   124     // current cluster header array index
   125     mClusterHeaderIndex = mClusterBuffs.Length() - 1;
   126     mClusterLengthLoc = ebmlLoc.offset;
   127     mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
   128     Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
   129     mFlushState |= FLUSH_CLUSTER;
   130   }
   132   if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
   133     short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
   134                      - mClusterTimecode;
   135     writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
   136                      EncodedFrame::FrameType::VP8_I_FRAME,
   137                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
   138                      aFrame->GetFrameData().Length());
   139   } else {
   140     writeSimpleBlock(&ebml, 0x2, 0, false,
   141                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
   142                      aFrame->GetFrameData().Length());
   143   }
   144   MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
   145              aFrame->GetFrameData().Length(),
   146              "write more data > EBML_BUFFER_SIZE");
   147   block->SetLength(ebml.offset);
   148 }
   150 void
   151 EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
   152                              uint32_t aDisplayWidth, uint32_t aDisplayHeight,
   153                              float aFrameRate)
   154 {
   155   MOZ_ASSERT(aWidth > 0, "Width should > 0");
   156   MOZ_ASSERT(aHeight > 0, "Height should > 0");
   157   MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
   158   MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
   159   MOZ_ASSERT(aFrameRate > 0, "FrameRate should > 0");
   160   mWidth = aWidth;
   161   mHeight = aHeight;
   162   mDisplayWidth = aDisplayWidth;
   163   mDisplayHeight = aDisplayHeight;
   164   mFrameRate = aFrameRate;
   165 }
   167 void
   168 EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels,
   169                              uint32_t aBitDepth)
   170 {
   171   MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
   172   MOZ_ASSERT(aBitDepth > 0, "BitDepth should > 0");
   173   MOZ_ASSERT(aChannels > 0, "Channels should > 0");
   174   mSampleFreq = aSampleFreq;
   175   mBitDepth = aBitDepth;
   176   mChannels = aChannels;
   177 }
   179 void
   180 EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
   181                             uint32_t aFlag)
   182 {
   183   if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
   184       (aFlag & ContainerWriter::GET_HEADER))
   185   {
   186     FinishMetadata();
   187   }
   188   if (aFlag & ContainerWriter::FLUSH_NEEDED)
   189   {
   190     FinishCluster();
   191   }
   192   // aDestBufs may have some element
   193   for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
   194     aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
   195   }
   196   mClusterCanFlushBuffs.Clear();
   197 }
   199 EbmlComposer::EbmlComposer()
   200   : mFlushState(FLUSH_NONE)
   201   , mClusterHeaderIndex(0)
   202   , mClusterLengthLoc(0)
   203   , mClusterTimecode(0)
   204   , mWidth(0)
   205   , mHeight(0)
   206   , mFrameRate(0)
   207   , mSampleFreq(0)
   208   , mBitDepth(0)
   209   , mChannels(0)
   210 {}
   212 }

mercurial