blob: 15c398042822512627fd353b3603aa3e7c7c1f7b [file]
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include <strmif.h>
#include "webmconstants.h"
#include "webmmuxstream.h"
#include "webmmuxcontext.h"
#include <cassert>
#include <climits>
namespace WebmMuxLib
{
Stream::Frame::Frame()
{
}
Stream::Frame::~Frame()
{
}
void Stream::Frame::Release()
{
delete this;
}
Stream::Stream(Context& c) :
m_context(c),
m_trackNumber(0)
{
}
Stream::~Stream()
{
}
void Stream::Final()
{
}
void Stream::SetTrackNumber(int tn)
{
m_trackNumber = tn;
}
int Stream::GetTrackNumber() const
{
return m_trackNumber;
}
void Stream::WriteTrackEntry(int tn)
{
WebmUtil::EbmlScratchBuf& entry_buf = m_context.m_buf;
// we need the starting length of |entry_buf| to properly calculate
// |num_bytes_to_ignore|
const uint64 buf_start_len = entry_buf.GetBufferLength();
entry_buf.WriteID1(WebmUtil::kEbmlTrackEntryID);
// store entry offset for patching later
const uint64 entry_len_offset = entry_buf.GetBufferLength();
// We must exclude |num_bytes_to_ignore| from the size we obtain from
// |entry_buf|. The value from |entry_buf| includes the bytes storing
// all preceding data in the track entry -- using it as-is would result
// in an invalid tracks element.
const uint64 num_bytes_to_ignore = buf_start_len + 1 + sizeof(uint16);
// reserve 2 bytes for patching in the size...
entry_buf.Serialize2UInt(0);
WriteTrackNumber(tn);
WriteTrackUID();
WriteTrackType();
WriteTrackName();
WriteTrackCodecID();
WriteTrackCodecPrivate();
WriteTrackCodecName();
WriteTrackSettings();
const uint64 entry_len = entry_buf.GetBufferLength() - num_bytes_to_ignore;
entry_buf.RewriteUInt(entry_len_offset, entry_len, sizeof(uint16));
}
void Stream::WriteTrackNumber(int tn_)
{
WebmUtil::EbmlScratchBuf& buf = m_context.m_buf;
assert(tn_ > 0);
assert(tn_ < 128);
m_trackNumber = tn_;
const uint8 track_num = static_cast<uint8>(tn_);
buf.WriteID1(WebmUtil::kEbmlTrackNumberID);
buf.Write1UInt(1);
buf.Serialize1UInt(track_num);
}
void Stream::WriteTrackUID()
{
WebmUtil::EbmlScratchBuf& buf = m_context.m_buf;
const TrackUID_t uid = CreateTrackUID();
buf.WriteID2(WebmUtil::kEbmlTrackUIDID);
buf.Write1UInt(8);
buf.Serialize8UInt(uid);
}
void Stream::WriteTrackName()
{
}
void Stream::WriteTrackCodecPrivate()
{
}
void Stream::WriteTrackSettings()
{
}
Stream::TrackUID_t Stream::CreateTrackUID()
{
TrackUID_t result;
//TODO: Do we need to do this?
//NOTE: The TrackUID is serialized in the normal way (the same
//as for any other integer that is the payload of an EBML tag),
//but the Matroska spec does say that this is an unsigned
//integer. In order to allow this integer value to be used
//as an EBML varying-size integer, we restrict its value so
//that it satifies the constraints for a varying size integer
//that is streamed out using 8 bytes. That means the upper
//byte (the first in the stream) is 0 (the upper byte in the
//stream is reserved for indicating that this integer
//occupies 8 bytes total in the stream), and the low order
//byte (the last in the stream) is even, which prevents
//the integer from ever having a value with all bits set
//(because then it would look like a signed integer).
BYTE* p = reinterpret_cast<BYTE*>(&result);
BYTE* const q = p + 7;
{
const int n = rand();
BYTE& b0 = *p++;
b0 = static_cast<BYTE>(n >> 4); //throw away low-order bits
b0 &= 0xFE; //ensure low order bit is not set
}
while (p != q)
{
const int n = rand();
*p++ = static_cast<BYTE>(n >> 4); //throw away low-order bits
}
*p = 0;
return result;
}
int Stream::Frame::GetLacing() const
{
return 0;
}
ULONG Stream::Frame::GetBlockSize() const
{
const ULONG result = 1 + 2 + 1 + GetSize(); //tn, tc, flg, f
return result;
}
void Stream::Frame::WriteSimpleBlock(
const Stream& s,
ULONG cluster_tc) const
{
WriteBlock(s, cluster_tc, true, GetBlockSize()); //SimpleBlock
}
void Stream::Frame::WriteBlockGroup(
const Stream& s,
ULONG cluster_tc,
LONG prev_tc,
ULONG duration) const
{
EbmlIO::File& file = s.m_context.m_file;
const ULONG block_size = GetBlockSize();
ULONG block_group_size = 5 + block_size;
const bool bKey = IsKey();
if (!bKey)
block_group_size += 1 + 1 + 2;
if (duration > 0)
block_group_size += 1 + 1 + 4;
//begin block group
file.WriteID1(WebmUtil::kEbmlBlockGroupID);
file.WriteUInt(block_group_size);
#ifdef _DEBUG
const __int64 pos = file.GetPosition();
#endif
WriteBlock(s, cluster_tc, false, block_size);
if (!bKey)
{
assert(prev_tc >= 0);
const ULONG curr_tc = GetTimecode();
assert(curr_tc <= LONG_MAX);
const LONG tc = prev_tc - LONG(curr_tc);
assert(tc < 0);
assert(tc >= SHRT_MIN);
const SHORT val = static_cast<SHORT>(tc);
file.WriteID1(WebmUtil::kEbmlReferenceBlockID);
file.Write1UInt(2);
file.Serialize2SInt(val);
}
if (duration > 0)
{
file.WriteID1(WebmUtil::kEbmlBlockDurationID);
file.Write1UInt(4); //TODO: use min size
file.Serialize4UInt(duration);
}
//end block group
#ifdef _DEBUG
const __int64 newpos = file.GetPosition();
assert((newpos - pos) == block_group_size);
#endif
}
void Stream::Frame::WriteBlock(
const Stream& s,
ULONG cluster_timecode,
bool simple_block,
ULONG block_size) const
{
EbmlIO::File& file = s.m_context.m_file;
//begin block
const BYTE id = simple_block ? 0xA3 : 0xA1; //SimpleBlock vs. Block
file.WriteID1(id);
file.WriteUInt(block_size);
#ifdef _DEBUG
const __int64 pos = file.GetPosition();
#endif
const int tn_ = s.GetTrackNumber();
assert(tn_ > 0);
assert(tn_ <= 255);
const BYTE tn = static_cast<BYTE>(tn_);
file.Write1UInt(tn); //track number
{
const ULONG ft = GetTimecode();
assert(ft <= LONG_MAX);
const LONG tc_ = LONG(ft) - LONG(cluster_timecode);
assert(tc_ >= SHRT_MIN);
assert(tc_ <= SHRT_MAX);
const SHORT tc = static_cast<SHORT>(tc_);
file.Serialize2SInt(tc); //relative timecode
}
BYTE flags = 0;
if (simple_block & IsKey())
flags |= BYTE(1 << 7);
const int lacing = GetLacing();
assert(lacing >= 0);
assert(lacing <= 3);
const BYTE fLacing = static_cast<BYTE>(lacing << 1);
flags |= fLacing;
file.Write(&flags, 1); //written as binary, not uint
file.Write(GetData(), GetSize()); //frame
//end block
#ifdef _DEBUG
const __int64 newpos = file.GetPosition();
assert((newpos - pos) == block_size);
#endif
}
} //end namespace WebmMuxLib