/*
* (C) 2003-2006 Gabest
* (C) 2006-2015 see Authors.txt
*
* This file is part of MPC-HC.
*
* MPC-HC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* MPC-HC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "stdafx.h"
#include
#include "MatroskaFile.h"
#include "../../../DSUtil/DSUtil.h"
using namespace MatroskaWriter;
static void bswap(BYTE* s, int len)
{
for (BYTE* d = s + len - 1; s < d; s++, d--) {
*s ^= *d, *d ^= *s, *s ^= *d;
}
}
//
CID::CID(DWORD id)
: m_id(id)
{
}
QWORD CID::Size(bool fWithHeader)
{
return CUInt(0, m_id).Size(false);
}
HRESULT CID::Write(IStream* pStream)
{
QWORD len = CID::Size();
DWORD id = m_id;
bswap((BYTE*)&id, (int)len);
*(BYTE*)&id = ((*(BYTE*)&id) & (1 << (8 - len)) - 1) | (1 << (8 - len));
return pStream->Write(&id, (ULONG)len, nullptr);
}
QWORD CID::HeaderSize(QWORD len)
{
return CID::Size() + CLength(len).Size();
}
HRESULT CID::HeaderWrite(IStream* pStream)
{
CID::Write(pStream);
CLength(Size(false)).Write(pStream);
return S_OK;
}
QWORD CBinary::Size(bool fWithHeader)
{
if (IsEmpty()) {
return 0;
}
QWORD len = 0;
len += GetCount();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CBinary::Write(IStream* pStream)
{
if (IsEmpty()) {
return S_OK;
}
HeaderWrite(pStream);
return pStream->Write(GetData(), (ULONG)GetCount(), nullptr);
}
QWORD CANSI::Size(bool fWithHeader)
{
if (IsEmpty()) {
return 0;
}
QWORD len = 0;
len += GetLength();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CANSI::Write(IStream* pStream)
{
if (IsEmpty()) {
return S_OK;
}
HeaderWrite(pStream);
return pStream->Write((LPCSTR) * this, GetLength(), nullptr);
}
QWORD CUTF8::Size(bool fWithHeader)
{
if (IsEmpty()) {
return 0;
}
QWORD len = 0;
len += UTF16To8(*this).GetLength();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CUTF8::Write(IStream* pStream)
{
if (IsEmpty()) {
return S_OK;
}
HeaderWrite(pStream);
CStringA str = UTF16To8(*this);
return pStream->Write((BYTE*)(LPCSTR)str, str.GetLength(), nullptr);
}
template
QWORD CSimpleVar::Size(bool fWithHeader)
{
if (!m_fSet) {
return 0;
}
QWORD len = 0;
len += sizeof(T);
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
template
HRESULT CSimpleVar::Write(IStream* pStream)
{
if (!m_fSet) {
return S_OK;
}
HeaderWrite(pStream);
T val = m_val;
bswap((BYTE*)&val, sizeof(T));
return pStream->Write(&val, sizeof(T), nullptr);
}
QWORD CUInt::Size(bool fWithHeader)
{
if (!m_fSet) {
return 0;
}
QWORD len = 0;
if (m_val == 0) {
len++;
} else {
for (int i = 8; i > 0; i--) {
if (((0xffi64 << ((i - 1) * 8))&m_val)) {
len += i;
break;
}
}
}
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CUInt::Write(IStream* pStream)
{
if (!m_fSet) {
return S_OK;
}
CID::Write(pStream);
CLength l(Size(false));
l.Write(pStream);
UINT64 val = m_val;
bswap((BYTE*)&val, (int)l);
return pStream->Write(&val, (ULONG)l, nullptr);
}
QWORD CInt::Size(bool fWithHeader)
{
if (!m_fSet) {
return 0;
}
QWORD len = 0;
if (m_val == 0) {
len++;
} else {
UINT64 val = m_val >= 0 ? m_val : -m_val;
for (int i = 8; i > 0; i--) {
if (((0xffi64 << ((i - 1) * 8))&val)) {
len += i;
if (m_val < 0 && !(m_val & (0x80 << (i - 1)))) {
len++;
}
break;
}
}
}
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CInt::Write(IStream* pStream)
{
if (!m_fSet) {
return S_OK;
}
CID::Write(pStream);
CLength l(Size(false));
l.Write(pStream);
UINT64 val = m_val;
bswap((BYTE*)&val, (int)l);
return pStream->Write(&val, (ULONG)l, nullptr);
}
QWORD CLength::Size(bool fWithHeader)
{
if (m_len == 0x00FFFFFFFFFFFFFFi64) {
return 8;
}
QWORD len = 0;
for (int i = 1; i <= 8; i++) {
if (!(m_len & (~((QWORD(1) << (7 * i)) - 1))) && (m_len & ((QWORD(1) << (7 * i)) - 1)) != ((QWORD(1) << (7 * i)) - 1)) {
len += i;
break;
}
}
return len;
}
HRESULT CLength::Write(IStream* pStream)
{
QWORD len = Size(false);
UINT64 val = m_len;
bswap((BYTE*)&val, (int)len);
*(BYTE*)&val = ((*(BYTE*)&val) & (1 << (8 - len)) - 1) | (1 << (8 - len));
return pStream->Write(&val, (ULONG)len, nullptr);
}
//
EBML::EBML(DWORD id)
: CID(id)
, EBMLVersion(0x4286)
, EBMLReadVersion(0x42F7)
, EBMLMaxIDLength(0x42F2)
, EBMLMaxSizeLength(0x42F3)
, DocType(0x4282)
, DocTypeVersion(0x4287)
, DocTypeReadVersion(0x4285)
{
}
QWORD EBML::Size(bool fWithHeader)
{
QWORD len = 0;
len += EBMLVersion.Size();
len += EBMLReadVersion.Size();
len += EBMLMaxIDLength.Size();
len += EBMLMaxSizeLength.Size();
len += DocType.Size();
len += DocTypeVersion.Size();
len += DocTypeReadVersion.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT EBML::Write(IStream* pStream)
{
HeaderWrite(pStream);
EBMLVersion.Write(pStream);
EBMLReadVersion.Write(pStream);
EBMLMaxIDLength.Write(pStream);
EBMLMaxSizeLength.Write(pStream);
DocType.Write(pStream);
DocTypeVersion.Write(pStream);
DocTypeReadVersion.Write(pStream);
return S_OK;
}
Info::Info(DWORD id)
: CID(id)
, SegmentUID(0x73A4)
, SegmentFilename(0x7384)
, PrevUID(0x3CB923)
, PrevFilename(0x3C83AB)
, NextUID(0x3EB923)
, NextFilename(0x3E83BB)
, TimeCodeScale(0x2AD7B1, 1000000ui64)
, Duration(0x4489)
, DateUTC(0x4461)
, Title(0x7BA9)
, MuxingApp(0x4D80)
, WritingApp(0x5741)
{
}
QWORD Info::Size(bool fWithHeader)
{
QWORD len = 0;
len += SegmentUID.Size();
len += PrevUID.Size();
len += NextUID.Size();
len += SegmentFilename.Size();
len += PrevFilename.Size();
len += NextFilename.Size();
len += TimeCodeScale.Size();
len += Duration.Size();
len += DateUTC.Size();
len += Title.Size();
len += MuxingApp.Size();
len += WritingApp.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Info::Write(IStream* pStream)
{
HeaderWrite(pStream);
SegmentUID.Write(pStream);
PrevUID.Write(pStream);
NextUID.Write(pStream);
SegmentFilename.Write(pStream);
PrevFilename.Write(pStream);
NextFilename.Write(pStream);
TimeCodeScale.Write(pStream);
Duration.Write(pStream);
DateUTC.Write(pStream);
Title.Write(pStream);
MuxingApp.Write(pStream);
WritingApp.Write(pStream);
return S_OK;
}
Segment::Segment(DWORD id)
: CID(id)
{
}
QWORD Segment::Size(bool fWithHeader)
{
return 0x00FFFFFFFFFFFFFFi64;
/*
QWORD len = 0;
if (fWithHeader) len += HeaderSize(len);
return len;
*/
}
HRESULT Segment::Write(IStream* pStream)
{
HeaderWrite(pStream);
return S_OK;
}
Track::Track(DWORD id)
: CID(id)
{
}
QWORD Track::Size(bool fWithHeader)
{
QWORD len = 0;
len += TrackEntries.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Track::Write(IStream* pStream)
{
HeaderWrite(pStream);
TrackEntries.Write(pStream);
return S_OK;
}
TrackEntry::TrackEntry(DWORD id)
: CID(id)
, TrackNumber(0xD7)
, TrackUID(0x73C5)
, TrackType(0x83)
, FlagEnabled(0xB9)
, FlagDefault(0x88)
, FlagLacing(0x9C)
, MinCache(0x6DE7)
, MaxCache(0x6DF8)
, Name(0x536E)
, Language(0x22B59C)
, CodecID(0x86)
, CodecPrivate(0x63A2)
, CodecName(0x258688)
, CodecSettings(0x3A9697)
, CodecInfoURL(0x3B4040)
, CodecDownloadURL(0x26B240)
, CodecDecodeAll(0xAA)
, TrackOverlay(0x6FAB)
, DefaultDuration(0x23E383)
, v(0xE0)
, a(0xE1)
{
DescType = NoDesc;
}
QWORD TrackEntry::Size(bool fWithHeader)
{
QWORD len = 0;
len += TrackNumber.Size();
len += TrackUID.Size();
len += TrackType.Size();
len += FlagEnabled.Size();
len += FlagDefault.Size();
len += FlagLacing.Size();
len += MinCache.Size();
len += MaxCache.Size();
len += Name.Size();
len += Language.Size();
len += CodecID.Size();
len += CodecPrivate.Size();
len += CodecName.Size();
len += CodecSettings.Size();
len += CodecInfoURL.Size();
len += CodecDownloadURL.Size();
len += CodecDecodeAll.Size();
len += TrackOverlay.Size();
len += DefaultDuration.Size();
if (DescType == TypeVideo) {
len += v.Size();
}
if (DescType == TypeAudio) {
len += a.Size();
}
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT TrackEntry::Write(IStream* pStream)
{
HeaderWrite(pStream);
TrackNumber.Write(pStream);
TrackUID.Write(pStream);
TrackType.Write(pStream);
FlagEnabled.Write(pStream);
FlagDefault.Write(pStream);
FlagLacing.Write(pStream);
MinCache.Write(pStream);
MaxCache.Write(pStream);
Name.Write(pStream);
Language.Write(pStream);
CodecID.Write(pStream);
CodecPrivate.Write(pStream);
CodecName.Write(pStream);
CodecSettings.Write(pStream);
CodecInfoURL.Write(pStream);
CodecDownloadURL.Write(pStream);
CodecDecodeAll.Write(pStream);
TrackOverlay.Write(pStream);
DefaultDuration.Write(pStream);
if (DescType == TypeVideo) {
v.Write(pStream);
}
if (DescType == TypeAudio) {
a.Write(pStream);
}
return S_OK;
}
Video::Video(DWORD id)
: CID(id)
, FlagInterlaced(0x9A)
, StereoMode(0x53B8)
, PixelWidth(0xB0)
, PixelHeight(0xBA)
, DisplayWidth(0x54B0)
, DisplayHeight(0x54BA)
, DisplayUnit(0x54B2)
, AspectRatioType(0x54B3)
, ColourSpace(0x2EB524)
, GammaValue(0x2FB523)
, FramePerSec(0x2383E3)
{
}
QWORD Video::Size(bool fWithHeader)
{
QWORD len = 0;
len += FlagInterlaced.Size();
len += StereoMode.Size();
len += PixelWidth.Size();
len += PixelHeight.Size();
len += DisplayWidth.Size();
len += DisplayHeight.Size();
len += DisplayUnit.Size();
len += AspectRatioType.Size();
len += ColourSpace.Size();
len += GammaValue.Size();
len += FramePerSec.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Video::Write(IStream* pStream)
{
HeaderWrite(pStream);
FlagInterlaced.Write(pStream);
StereoMode.Write(pStream);
PixelWidth.Write(pStream);
PixelHeight.Write(pStream);
DisplayWidth.Write(pStream);
DisplayHeight.Write(pStream);
DisplayUnit.Write(pStream);
AspectRatioType.Write(pStream);
ColourSpace.Write(pStream);
GammaValue.Write(pStream);
FramePerSec.Write(pStream);
return S_OK;
}
Audio::Audio(DWORD id)
: CID(id)
, SamplingFrequency(0xB5)
, OutputSamplingFrequency(0x78B5)
, Channels(0x9F)
, ChannelPositions(0x7D7B)
, BitDepth(0x6264)
{
}
QWORD Audio::Size(bool fWithHeader)
{
QWORD len = 0;
len += SamplingFrequency.Size();
len += OutputSamplingFrequency.Size();
len += Channels.Size();
len += ChannelPositions.Size();
len += BitDepth.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Audio::Write(IStream* pStream)
{
HeaderWrite(pStream);
SamplingFrequency.Write(pStream);
OutputSamplingFrequency.Write(pStream);
Channels.Write(pStream);
ChannelPositions.Write(pStream);
BitDepth.Write(pStream);
return S_OK;
}
Cluster::Cluster(DWORD id)
: CID(id)
, TimeCode(0xE7)
, Position(0xA7)
, PrevSize(0xAB)
{
}
QWORD Cluster::Size(bool fWithHeader)
{
QWORD len = 0;
len += TimeCode.Size();
len += Position.Size();
len += PrevSize.Size();
len += BlockGroups.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Cluster::Write(IStream* pStream)
{
HeaderWrite(pStream);
TimeCode.Write(pStream);
Position.Write(pStream);
PrevSize.Write(pStream);
BlockGroups.Write(pStream);
return S_OK;
}
BlockGroup::BlockGroup(DWORD id)
: CID(id)
, BlockDuration(0x9B)
, ReferencePriority(0xFA)
, ReferenceBlock(0xFB)
, ReferenceVirtual(0xFD)
, CodecState(0xA4)
{
}
QWORD BlockGroup::Size(bool fWithHeader)
{
QWORD len = 0;
len += BlockDuration.Size();
len += ReferencePriority.Size();
len += ReferenceBlock.Size();
len += ReferenceVirtual.Size();
len += CodecState.Size();
len += Block.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT BlockGroup::Write(IStream* pStream)
{
HeaderWrite(pStream);
BlockDuration.Write(pStream);
ReferencePriority.Write(pStream);
ReferenceBlock.Write(pStream);
ReferenceVirtual.Write(pStream);
CodecState.Write(pStream);
Block.Write(pStream);
return S_OK;
}
CBlock::CBlock(DWORD id)
: CID(id)
, TimeCode(0)
, TimeCodeStop(0)
{
}
QWORD CBlock::Size(bool fWithHeader)
{
QWORD len = 0;
len += TrackNumber.Size() + 2 + 1; // TrackNumber + TimeCode + Lacing
if (BlockData.GetCount() > 1) {
len += 1; // nBlockData
POSITION pos = BlockData.GetHeadPosition();
while (pos) {
CBinary* b = BlockData.GetNext(pos);
if (pos) {
len += b->GetCount() / 255 + 1;
}
}
}
POSITION pos = BlockData.GetHeadPosition();
while (pos) {
CBinary* b = BlockData.GetNext(pos);
len += b->GetCount();
}
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CBlock::Write(IStream* pStream)
{
HeaderWrite(pStream);
TrackNumber.Write(pStream);
short t = (short)TimeCode;
bswap((BYTE*)&t, 2);
pStream->Write(&t, 2, nullptr);
BYTE Lacing = 0;
BYTE n = (BYTE)BlockData.GetCount();
if (n > 1) {
Lacing |= 2;
}
pStream->Write(&Lacing, 1, nullptr);
if (n > 1) {
pStream->Write(&n, 1, nullptr);
POSITION pos = BlockData.GetHeadPosition();
while (pos) {
CBinary* b = BlockData.GetNext(pos);
if (pos) {
INT_PTR len = b->GetCount();
while (len >= 0) {
n = (BYTE)std::min(len, 255);
pStream->Write(&n, 1, nullptr);
len -= 255;
}
}
}
}
POSITION pos = BlockData.GetHeadPosition();
while (pos) {
CBinary* b = BlockData.GetNext(pos);
pStream->Write(b->GetData(), (ULONG)b->GetCount(), nullptr);
}
return S_OK;
}
Cue::Cue(DWORD id)
: CID(id)
{
}
QWORD Cue::Size(bool fWithHeader)
{
QWORD len = 0;
len += CuePoints.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Cue::Write(IStream* pStream)
{
HeaderWrite(pStream);
CuePoints.Write(pStream);
return S_OK;
}
CuePoint::CuePoint(DWORD id)
: CID(id)
, CueTime(0xB3)
{
}
QWORD CuePoint::Size(bool fWithHeader)
{
QWORD len = 0;
len += CueTime.Size();
len += CueTrackPositions.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CuePoint::Write(IStream* pStream)
{
HeaderWrite(pStream);
CueTime.Write(pStream);
CueTrackPositions.Write(pStream);
return S_OK;
}
CueTrackPosition::CueTrackPosition(DWORD id)
: CID(id)
, CueTrack(0xF7)
, CueClusterPosition(0xF1)
, CueBlockNumber(0x5387)
, CueCodecState(0xEA)
{
}
QWORD CueTrackPosition::Size(bool fWithHeader)
{
QWORD len = 0;
len += CueTrack.Size();
len += CueClusterPosition.Size();
len += CueBlockNumber.Size();
len += CueCodecState.Size();
// len += CueReferences.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT CueTrackPosition::Write(IStream* pStream)
{
HeaderWrite(pStream);
CueTrack.Write(pStream);
CueClusterPosition.Write(pStream);
CueBlockNumber.Write(pStream);
CueCodecState.Write(pStream);
// CueReferences.Write(pStream);
return S_OK;
}
Seek::Seek(DWORD id)
: CID(id)
{
}
QWORD Seek::Size(bool fWithHeader)
{
QWORD len = 0;
len += SeekHeads.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Seek::Write(IStream* pStream)
{
HeaderWrite(pStream);
SeekHeads.Write(pStream);
return S_OK;
}
SeekID::SeekID(DWORD id)
: CID(id)
, m_cid(0)
{
}
QWORD SeekID::Size(bool fWithHeader)
{
QWORD len = 0;
len += m_cid.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT SeekID::Write(IStream* pStream)
{
HeaderWrite(pStream);
m_cid.Write(pStream);
return S_OK;
}
SeekHead::SeekHead(DWORD id)
: CID(id)
, Position(0x53AC)
{
}
QWORD SeekHead::Size(bool fWithHeader)
{
QWORD len = 0;
len += ID.Size();
len += Position.Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT SeekHead::Write(IStream* pStream)
{
HeaderWrite(pStream);
ID.Write(pStream);
Position.Write(pStream);
return S_OK;
}
Tags::Tags(DWORD id)
: CID(id)
{
}
QWORD Tags::Size(bool fWithHeader)
{
QWORD len = 0;
// len += .Size();
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Tags::Write(IStream* pStream)
{
HeaderWrite(pStream);
// .Write(pStream);
return S_OK;
}
Void::Void(QWORD len, DWORD id)
: CID(id)
, m_len(len)
{
}
QWORD Void::Size(bool fWithHeader)
{
QWORD len = 0;
len += m_len;
if (fWithHeader) {
len += HeaderSize(len);
}
return len;
}
HRESULT Void::Write(IStream* pStream)
{
HeaderWrite(pStream);
BYTE buff[64];
memset(buff, 0x80, sizeof(buff));
QWORD len = m_len;
for (; len >= sizeof(buff); len -= sizeof(buff)) {
pStream->Write(buff, sizeof(buff), nullptr);
}
if (len > 0) {
pStream->Write(buff, (ULONG)len, nullptr);
}
return S_OK;
}