Fri, 16 Jan 2015 04:50:19 +0100
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 | } |