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 +}