michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "EbmlComposer.h" michael@0: #include "libmkv/EbmlIDs.h" michael@0: #include "libmkv/EbmlWriter.h" michael@0: #include "libmkv/WebMElement.h" michael@0: #include "prtime.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Timecode scale in nanoseconds michael@0: static const unsigned long TIME_CODE_SCALE = 1000000; michael@0: // The WebM header size without audio CodecPrivateData michael@0: static const int32_t DEFAULT_HEADER_SIZE = 1024; michael@0: michael@0: void EbmlComposer::GenerateHeader() michael@0: { michael@0: // Write the EBML header. michael@0: EbmlGlobal ebml; michael@0: // The WEbM header default size usually smaller than 1k. michael@0: nsAutoArrayPtr buffer(new uint8_t[DEFAULT_HEADER_SIZE + michael@0: mCodecPrivateData.Length()]); michael@0: ebml.buf = buffer.get(); michael@0: ebml.offset = 0; michael@0: writeHeader(&ebml); michael@0: { michael@0: EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc; michael@0: Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment); michael@0: { michael@0: Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead); michael@0: // Todo: We don't know the exact sizes of encoded data and michael@0: // ignore this section. michael@0: Ebml_EndSubElement(&ebml, &ebmlLocseg); michael@0: writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0); michael@0: { michael@0: EbmlLoc trackLoc; michael@0: Ebml_StartSubElement(&ebml, &trackLoc, Tracks); michael@0: { michael@0: // Video michael@0: if (mWidth > 0 && mHeight > 0) { michael@0: writeVideoTrack(&ebml, 0x1, 0, "V_VP8", michael@0: mWidth, mHeight, michael@0: mDisplayWidth, mDisplayHeight, mFrameRate); michael@0: } michael@0: // Audio michael@0: if (mCodecPrivateData.Length() > 0) { michael@0: writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq, michael@0: mChannels, mCodecPrivateData.Elements(), michael@0: mCodecPrivateData.Length()); michael@0: } michael@0: } michael@0: Ebml_EndSubElement(&ebml, &trackLoc); michael@0: } michael@0: } michael@0: // The Recording length is unknown and michael@0: // ignore write the whole Segment element size michael@0: } michael@0: MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(), michael@0: "write more data > EBML_BUFFER_SIZE"); michael@0: auto block = mClusterBuffs.AppendElement(); michael@0: block->SetLength(ebml.offset); michael@0: memcpy(block->Elements(), ebml.buf, ebml.offset); michael@0: mFlushState |= FLUSH_METADATA; michael@0: } michael@0: michael@0: void EbmlComposer::FinishMetadata() michael@0: { michael@0: if (mFlushState & FLUSH_METADATA) { michael@0: // We don't remove the first element of mClusterBuffs because the michael@0: // |mClusterHeaderIndex| may have value. michael@0: mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]); michael@0: mFlushState &= ~FLUSH_METADATA; michael@0: } michael@0: } michael@0: michael@0: void EbmlComposer::FinishCluster() michael@0: { michael@0: FinishMetadata(); michael@0: if (!(mFlushState & FLUSH_CLUSTER)) { michael@0: // No completed cluster available. michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(mClusterLengthLoc > 0); michael@0: EbmlGlobal ebml; michael@0: EbmlLoc ebmlLoc; michael@0: ebmlLoc.offset = mClusterLengthLoc; michael@0: ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length(); michael@0: ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements(); michael@0: Ebml_EndSubElement(&ebml, &ebmlLoc); michael@0: // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip michael@0: // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED. michael@0: for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) { michael@0: mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]); michael@0: } michael@0: michael@0: mClusterHeaderIndex = 0; michael@0: mClusterLengthLoc = 0; michael@0: mClusterBuffs.Clear(); michael@0: mFlushState &= ~FLUSH_CLUSTER; michael@0: } michael@0: michael@0: void michael@0: EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame) michael@0: { michael@0: EbmlGlobal ebml; michael@0: ebml.offset = 0; michael@0: michael@0: if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) { michael@0: FinishCluster(); michael@0: } michael@0: michael@0: auto block = mClusterBuffs.AppendElement(); michael@0: block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE); michael@0: ebml.buf = block->Elements(); michael@0: michael@0: if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) { michael@0: EbmlLoc ebmlLoc; michael@0: Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster); michael@0: MOZ_ASSERT(mClusterBuffs.Length() > 0); michael@0: // current cluster header array index michael@0: mClusterHeaderIndex = mClusterBuffs.Length() - 1; michael@0: mClusterLengthLoc = ebmlLoc.offset; michael@0: mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC; michael@0: Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode); michael@0: mFlushState |= FLUSH_CLUSTER; michael@0: } michael@0: michael@0: if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) { michael@0: short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC michael@0: - mClusterTimecode; michael@0: writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() == michael@0: EncodedFrame::FrameType::VP8_I_FRAME, michael@0: 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(), michael@0: aFrame->GetFrameData().Length()); michael@0: } else { michael@0: writeSimpleBlock(&ebml, 0x2, 0, false, michael@0: 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(), michael@0: aFrame->GetFrameData().Length()); michael@0: } michael@0: MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + michael@0: aFrame->GetFrameData().Length(), michael@0: "write more data > EBML_BUFFER_SIZE"); michael@0: block->SetLength(ebml.offset); michael@0: } michael@0: michael@0: void michael@0: EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight, michael@0: uint32_t aDisplayWidth, uint32_t aDisplayHeight, michael@0: float aFrameRate) michael@0: { michael@0: MOZ_ASSERT(aWidth > 0, "Width should > 0"); michael@0: MOZ_ASSERT(aHeight > 0, "Height should > 0"); michael@0: MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0"); michael@0: MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0"); michael@0: MOZ_ASSERT(aFrameRate > 0, "FrameRate should > 0"); michael@0: mWidth = aWidth; michael@0: mHeight = aHeight; michael@0: mDisplayWidth = aDisplayWidth; michael@0: mDisplayHeight = aDisplayHeight; michael@0: mFrameRate = aFrameRate; michael@0: } michael@0: michael@0: void michael@0: EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels, michael@0: uint32_t aBitDepth) michael@0: { michael@0: MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0"); michael@0: MOZ_ASSERT(aBitDepth > 0, "BitDepth should > 0"); michael@0: MOZ_ASSERT(aChannels > 0, "Channels should > 0"); michael@0: mSampleFreq = aSampleFreq; michael@0: mBitDepth = aBitDepth; michael@0: mChannels = aChannels; michael@0: } michael@0: michael@0: void michael@0: EbmlComposer::ExtractBuffer(nsTArray >* aDestBufs, michael@0: uint32_t aFlag) michael@0: { michael@0: if ((aFlag & ContainerWriter::FLUSH_NEEDED) || michael@0: (aFlag & ContainerWriter::GET_HEADER)) michael@0: { michael@0: FinishMetadata(); michael@0: } michael@0: if (aFlag & ContainerWriter::FLUSH_NEEDED) michael@0: { michael@0: FinishCluster(); michael@0: } michael@0: // aDestBufs may have some element michael@0: for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) { michael@0: aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]); michael@0: } michael@0: mClusterCanFlushBuffs.Clear(); michael@0: } michael@0: michael@0: EbmlComposer::EbmlComposer() michael@0: : mFlushState(FLUSH_NONE) michael@0: , mClusterHeaderIndex(0) michael@0: , mClusterLengthLoc(0) michael@0: , mClusterTimecode(0) michael@0: , mWidth(0) michael@0: , mHeight(0) michael@0: , mFrameRate(0) michael@0: , mSampleFreq(0) michael@0: , mBitDepth(0) michael@0: , mChannels(0) michael@0: {} michael@0: michael@0: }