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

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

mercurial