LCOV - code coverage report
Current view: top level - dom/media/webm - EbmlComposer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 115 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include "EbmlComposer.h"
       7             : #include "mozilla/UniquePtr.h"
       8             : #include "mozilla/EndianUtils.h"
       9             : #include "libmkv/EbmlIDs.h"
      10             : #include "libmkv/EbmlWriter.h"
      11             : #include "libmkv/WebMElement.h"
      12             : #include "prtime.h"
      13             : #include "limits.h"
      14             : 
      15             : namespace mozilla {
      16             : 
      17             : // Timecode scale in nanoseconds
      18             : static const unsigned long TIME_CODE_SCALE = 1000000;
      19             : // The WebM header size without audio CodecPrivateData
      20             : static const int32_t DEFAULT_HEADER_SIZE = 1024;
      21             : 
      22           0 : void EbmlComposer::GenerateHeader()
      23             : {
      24             :   // Write the EBML header.
      25             :   EbmlGlobal ebml;
      26             :   // The WEbM header default size usually smaller than 1k.
      27             :   auto buffer = MakeUnique<uint8_t[]>(DEFAULT_HEADER_SIZE +
      28           0 :                                       mCodecPrivateData.Length());
      29           0 :   ebml.buf = buffer.get();
      30           0 :   ebml.offset = 0;
      31           0 :   writeHeader(&ebml);
      32             :   {
      33             :     EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
      34           0 :     Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
      35             :     {
      36           0 :       Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
      37             :       // Todo: We don't know the exact sizes of encoded data and
      38             :       // ignore this section.
      39           0 :       Ebml_EndSubElement(&ebml, &ebmlLocseg);
      40           0 :       writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
      41             :       {
      42             :         EbmlLoc trackLoc;
      43           0 :         Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
      44             :         {
      45             :           // Video
      46           0 :           if (mWidth > 0 && mHeight > 0) {
      47           0 :             writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
      48           0 :                             mWidth, mHeight,
      49           0 :                             mDisplayWidth, mDisplayHeight);
      50             :           }
      51             :           // Audio
      52           0 :           if (mCodecPrivateData.Length() > 0) {
      53             :             // Extract the pre-skip from mCodecPrivateData
      54             :             // then convert it to nanoseconds.
      55             :             // Details in OpusTrackEncoder.cpp.
      56           0 :             mCodecDelay =
      57           0 :               (uint64_t)LittleEndian::readUint16(mCodecPrivateData.Elements() + 10)
      58           0 :               * PR_NSEC_PER_SEC / 48000;
      59             :             // Fixed 80ms, convert into nanoseconds.
      60           0 :             uint64_t seekPreRoll = 80 * PR_NSEC_PER_MSEC;
      61           0 :             writeAudioTrack(&ebml, 0x2, 0x0, "A_OPUS", mSampleFreq,
      62           0 :                             mChannels, mCodecDelay, seekPreRoll,
      63             :                             mCodecPrivateData.Elements(),
      64           0 :                             mCodecPrivateData.Length());
      65             :           }
      66             :         }
      67           0 :         Ebml_EndSubElement(&ebml, &trackLoc);
      68             :       }
      69             :     }
      70             :     // The Recording length is unknown and
      71             :     // ignore write the whole Segment element size
      72             :   }
      73           0 :   MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
      74             :              "write more data > EBML_BUFFER_SIZE");
      75           0 :   auto block = mClusterBuffs.AppendElement();
      76           0 :   block->SetLength(ebml.offset);
      77           0 :   memcpy(block->Elements(), ebml.buf, ebml.offset);
      78           0 :   mFlushState |= FLUSH_METADATA;
      79           0 : }
      80             : 
      81           0 : void EbmlComposer::FinishMetadata()
      82             : {
      83           0 :   if (mFlushState & FLUSH_METADATA) {
      84             :     // We don't remove the first element of mClusterBuffs because the
      85             :     // |mClusterHeaderIndex| may have value.
      86           0 :     mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
      87           0 :     mFlushState &= ~FLUSH_METADATA;
      88             :   }
      89           0 : }
      90             : 
      91           0 : void EbmlComposer::FinishCluster()
      92             : {
      93           0 :   FinishMetadata();
      94           0 :   if (!(mFlushState & FLUSH_CLUSTER)) {
      95             :     // No completed cluster available.
      96           0 :     return;
      97             :   }
      98             : 
      99           0 :   MOZ_ASSERT(mClusterLengthLoc > 0);
     100             :   EbmlGlobal ebml;
     101             :   EbmlLoc ebmlLoc;
     102           0 :   ebmlLoc.offset = mClusterLengthLoc;
     103           0 :   ebml.offset = 0;
     104           0 :   for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
     105           0 :     ebml.offset += mClusterBuffs[i].Length();
     106             :   }
     107           0 :   ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
     108           0 :   Ebml_EndSubElement(&ebml, &ebmlLoc);
     109             :   // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
     110             :   // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
     111           0 :   for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
     112           0 :     mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
     113             :   }
     114             : 
     115           0 :   mClusterHeaderIndex = 0;
     116           0 :   mClusterLengthLoc = 0;
     117           0 :   mClusterBuffs.Clear();
     118           0 :   mFlushState &= ~FLUSH_CLUSTER;
     119             : }
     120             : 
     121             : void
     122           0 : EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
     123             : {
     124             :   EbmlGlobal ebml;
     125           0 :   ebml.offset = 0;
     126             : 
     127           0 :   auto frameType = aFrame->GetFrameType();
     128           0 :   bool flush = false;
     129           0 :   bool isVP8IFrame = (frameType == EncodedFrame::FrameType::VP8_I_FRAME);
     130           0 :   if (isVP8IFrame) {
     131           0 :     FinishCluster();
     132           0 :     flush = true;
     133             :   } else {
     134             :     // Force it to calculate timecode using signed math via cast
     135           0 :     int64_t timeCode = (aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode) +
     136           0 :                        (mCodecDelay / PR_NSEC_PER_MSEC);
     137           0 :     if (timeCode < SHRT_MIN || timeCode > SHRT_MAX ) {
     138             :       // We're probably going to overflow (or underflow) the timeCode value later!
     139           0 :       FinishCluster();
     140           0 :       flush = true;
     141             :     }
     142             :   }
     143             : 
     144           0 :   auto block = mClusterBuffs.AppendElement();
     145           0 :   block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
     146           0 :   ebml.buf = block->Elements();
     147             : 
     148           0 :   if (flush) {
     149             :     EbmlLoc ebmlLoc;
     150           0 :     Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
     151           0 :     MOZ_ASSERT(mClusterBuffs.Length() > 0);
     152             :     // current cluster header array index
     153           0 :     mClusterHeaderIndex = mClusterBuffs.Length() - 1;
     154           0 :     mClusterLengthLoc = ebmlLoc.offset;
     155             :     // if timeCode didn't under/overflow before, it shouldn't after this
     156           0 :     mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
     157           0 :     Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
     158           0 :     mFlushState |= FLUSH_CLUSTER;
     159             :   }
     160             : 
     161           0 :   bool isOpus = (frameType == EncodedFrame::FrameType::OPUS_AUDIO_FRAME);
     162             :   // Can't underflow/overflow now
     163           0 :   int64_t timeCode = aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode;
     164           0 :   if (isOpus) {
     165           0 :     timeCode += mCodecDelay / PR_NSEC_PER_MSEC;
     166             :   }
     167           0 :   MOZ_ASSERT(timeCode >= SHRT_MIN && timeCode <= SHRT_MAX);
     168           0 :   writeSimpleBlock(&ebml, isOpus ? 0x2 : 0x1, static_cast<short>(timeCode), isVP8IFrame,
     169           0 :                    0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
     170           0 :                    aFrame->GetFrameData().Length());
     171           0 :   MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
     172             :              aFrame->GetFrameData().Length(),
     173             :              "write more data > EBML_BUFFER_SIZE");
     174           0 :   block->SetLength(ebml.offset);
     175           0 : }
     176             : 
     177             : void
     178           0 : EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
     179             :                              uint32_t aDisplayWidth, uint32_t aDisplayHeight)
     180             : {
     181           0 :   MOZ_ASSERT(aWidth > 0, "Width should > 0");
     182           0 :   MOZ_ASSERT(aHeight > 0, "Height should > 0");
     183           0 :   MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
     184           0 :   MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
     185           0 :   mWidth = aWidth;
     186           0 :   mHeight = aHeight;
     187           0 :   mDisplayWidth = aDisplayWidth;
     188           0 :   mDisplayHeight = aDisplayHeight;
     189           0 : }
     190             : 
     191             : void
     192           0 : EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels)
     193             : {
     194           0 :   MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
     195           0 :   MOZ_ASSERT(aChannels > 0, "Channels should > 0");
     196           0 :   mSampleFreq = aSampleFreq;
     197           0 :   mChannels = aChannels;
     198           0 : }
     199             : 
     200             : void
     201           0 : EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
     202             :                             uint32_t aFlag)
     203             : {
     204           0 :   if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
     205           0 :       (aFlag & ContainerWriter::GET_HEADER))
     206             :   {
     207           0 :     FinishMetadata();
     208             :   }
     209           0 :   if (aFlag & ContainerWriter::FLUSH_NEEDED)
     210             :   {
     211           0 :     FinishCluster();
     212             :   }
     213             :   // aDestBufs may have some element
     214           0 :   for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
     215           0 :     aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
     216             :   }
     217           0 :   mClusterCanFlushBuffs.Clear();
     218           0 : }
     219             : 
     220           0 : EbmlComposer::EbmlComposer()
     221             :   : mFlushState(FLUSH_NONE)
     222             :   , mClusterHeaderIndex(0)
     223             :   , mClusterLengthLoc(0)
     224             :   , mCodecDelay(0)
     225             :   , mClusterTimecode(0)
     226             :   , mWidth(0)
     227             :   , mHeight(0)
     228             :   , mSampleFreq(0)
     229           0 :   , mChannels(0)
     230           0 : {}
     231             : 
     232             : } // namespace mozilla

Generated by: LCOV version 1.13