From f9d9824047d5d94f0e7312815e99f8347ec09581 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Wed, 20 Oct 2010 11:06:48 -0400 Subject: [PATCH] Import webmquicktime webm writer Initial import of the libmkv directory from the webmquicktime[1] project, at commit fedbda1. [1]: git://review.webmproject.org/webmquicktime.git commit fedbda18de899ff94855cb334de7e471036fbf1d Change-Id: I1564a0ebfa72293fc296ee02178196530dfd90e4 --- libmkv/EbmlBufferWriter.c | 60 ++++++++++++ libmkv/EbmlBufferWriter.h | 21 +++++ libmkv/EbmlIDs.h | 231 ++++++++++++++++++++++++++++++++++++++++++++++ libmkv/EbmlWriter.c | 160 ++++++++++++++++++++++++++++++++ libmkv/EbmlWriter.h | 38 ++++++++ libmkv/Makefile | 25 +++++ libmkv/WebMElement.c | 220 +++++++++++++++++++++++++++++++++++++++++++ libmkv/WebMElement.h | 35 +++++++ libmkv/testlibmkv.c | 63 +++++++++++++ 9 files changed, 853 insertions(+) create mode 100644 libmkv/EbmlBufferWriter.c create mode 100644 libmkv/EbmlBufferWriter.h create mode 100644 libmkv/EbmlIDs.h create mode 100644 libmkv/EbmlWriter.c create mode 100644 libmkv/EbmlWriter.h create mode 100644 libmkv/Makefile create mode 100644 libmkv/WebMElement.c create mode 100644 libmkv/WebMElement.h create mode 100644 libmkv/testlibmkv.c diff --git a/libmkv/EbmlBufferWriter.c b/libmkv/EbmlBufferWriter.c new file mode 100644 index 0000000..d9b04a8 --- /dev/null +++ b/libmkv/EbmlBufferWriter.c @@ -0,0 +1,60 @@ +//#include +#include "EbmlBufferWriter.h" +#include "EbmlWriter.h" +//#include +//#include +//#include //_alloca +#include +#include +#include + +void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) +{ + unsigned char *src = glob->buf; + src += glob->offset; + memcpy(src, buffer_in, len); + glob->offset += len; +} + +static void _Serialize(EbmlGlobal *glob, const unsigned char *p, const unsigned char *q) +{ + while (q != p) + { + --q; + + unsigned long cbWritten; + memcpy(&(glob->buf[glob->offset]), q, 1); + glob->offset ++; + } +} + +void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, unsigned long len) +{ + //assert(buf); + + const unsigned char *const p = (const unsigned char *)(buffer_in); + const unsigned char *const q = p + len; + + _Serialize(glob, p, q); +} + + +void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id) +{ + Ebml_WriteID(glob, class_id); + ebmlLoc->offset = glob->offset; + //todo this is always taking 8 bytes, this may need later optimization + unsigned long long unknownLen = 0x01FFFFFFFFFFFFFFLLU; + Ebml_Serialize(glob, (void *)&unknownLen, 8); //this is a key that says lenght unknown +} + +void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) +{ + unsigned long long size = glob->offset - ebmlLoc->offset - 8; + unsigned long long curOffset = glob->offset; + glob->offset = ebmlLoc->offset; + size |= 0x0100000000000000LLU; + Ebml_Serialize(glob, &size, 8); + glob->offset = curOffset; +} + diff --git a/libmkv/EbmlBufferWriter.h b/libmkv/EbmlBufferWriter.h new file mode 100644 index 0000000..ba0a9b3 --- /dev/null +++ b/libmkv/EbmlBufferWriter.h @@ -0,0 +1,21 @@ +#ifndef EBMLBUFFERWRITER_HPP +#define EBMLBUFFERWRITER_HPP + +typedef struct +{ + unsigned long long offset; +} EbmlLoc; + +typedef struct +{ + unsigned char *buf; + unsigned int length; + unsigned int offset; +} EbmlGlobal; + + +void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id); +void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc); + + +#endif diff --git a/libmkv/EbmlIDs.h b/libmkv/EbmlIDs.h new file mode 100644 index 0000000..2719908 --- /dev/null +++ b/libmkv/EbmlIDs.h @@ -0,0 +1,231 @@ +// 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. + + +#ifndef MKV_DEFS_HPP +#define MKV_DEFS_HPP 1 + +//Commenting out values not available in webm, but available in matroska + +enum mkv +{ + EBML = 0x1A45DFA3, + EBMLVersion = 0x4286, + EBMLReadVersion = 0x42F7, + EBMLMaxIDLength = 0x42F2, + EBMLMaxSizeLength = 0x42F3, + DocType = 0x4282, + DocTypeVersion = 0x4287, + DocTypeReadVersion = 0x4285, +// CRC_32 = 0xBF, + Void = 0xEC, + SignatureSlot = 0x1B538667, + SignatureAlgo = 0x7E8A, + SignatureHash = 0x7E9A, + SignaturePublicKey = 0x7EA5, + Signature = 0x7EB5, + SignatureElements = 0x7E5B, + SignatureElementList = 0x7E7B, + SignedElement = 0x6532, + //segment + Segment = 0x18538067, + //Meta Seek Information + SeekHead = 0x114D9B74, + Seek = 0x4DBB, + SeekID = 0x53AB, + SeekPosition = 0x53AC, + //Segment Information + Info = 0x1549A966, +// SegmentUID = 0x73A4, +// SegmentFilename = 0x7384, +// PrevUID = 0x3CB923, +// PrevFilename = 0x3C83AB, +// NextUID = 0x3EB923, +// NextFilename = 0x3E83BB, +// SegmentFamily = 0x4444, +// ChapterTranslate = 0x6924, +// ChapterTranslateEditionUID = 0x69FC, +// ChapterTranslateCodec = 0x69BF, +// ChapterTranslateID = 0x69A5, + TimecodeScale = 0x2AD7B1, + Segment_Duration = 0x4489, + DateUTC = 0x4461, +// Title = 0x7BA9, + MuxingApp = 0x4D80, + WritingApp = 0x5741, + //Cluster + Cluster = 0x1F43B675, + Timecode = 0xE7, +// SilentTracks = 0x5854, +// SilentTrackNumber = 0x58D7, +// Position = 0xA7, + PrevSize = 0xAB, + BlockGroup = 0xA0, + Block = 0xA1, +// BlockVirtual = 0xA2, +// BlockAdditions = 0x75A1, +// BlockMore = 0xA6, +// BlockAddID = 0xEE, +// BlockAdditional = 0xA5, + BlockDuration = 0x9B, +// ReferencePriority = 0xFA, + ReferenceBlock = 0xFB, +// ReferenceVirtual = 0xFD, +// CodecState = 0xA4, +// Slices = 0x8E, +// TimeSlice = 0xE8, + LaceNumber = 0xCC, +// FrameNumber = 0xCD, +// BlockAdditionID = 0xCB, +// MkvDelay = 0xCE, +// Cluster_Duration = 0xCF, + SimpleBlock = 0xA3, +// EncryptedBlock = 0xAF, + //Track + Tracks = 0x1654AE6B, + TrackEntry = 0xAE, + TrackNumber = 0xD7, + TrackUID = 0x73C5, + TrackType = 0x83, + FlagEnabled = 0xB9, + FlagDefault = 0x88, + FlagForced = 0x55AA, + FlagLacing = 0x9C, +// MinCache = 0x6DE7, +// MaxCache = 0x6DF8, + DefaultDuration = 0x23E383, +// TrackTimecodeScale = 0x23314F, +// TrackOffset = 0x537F, +// MaxBlockAdditionID = 0x55EE, + Name = 0x536E, + Language = 0x22B59C, + CodecID = 0x86, + CodecPrivate = 0x63A2, + CodecName = 0x258688, +// AttachmentLink = 0x7446, +// CodecSettings = 0x3A9697, +// CodecInfoURL = 0x3B4040, +// CodecDownloadURL = 0x26B240, +// CodecDecodeAll = 0xAA, +// TrackOverlay = 0x6FAB, +// TrackTranslate = 0x6624, +// TrackTranslateEditionUID = 0x66FC, +// TrackTranslateCodec = 0x66BF, +// TrackTranslateTrackID = 0x66A5, + //video + Video = 0xE0, + FlagInterlaced = 0x9A, +// StereoMode = 0x53B8, + PixelWidth = 0xB0, + PixelHeight = 0xBA, + PixelCropBottom = 0x54AA, + PixelCropTop = 0x54BB, + PixelCropLeft = 0x54CC, + PixelCropRight = 0x54DD, + DisplayWidth = 0x54B0, + DisplayHeight = 0x54BA, + DisplayUnit = 0x54B2, + AspectRatioType = 0x54B3, +// ColourSpace = 0x2EB524, +// GammaValue = 0x2FB523, + FrameRate = 0x2383E3, + //end video + //audio + Audio = 0xE1, + SamplingFrequency = 0xB5, + OutputSamplingFrequency = 0x78B5, + Channels = 0x9F, +// ChannelPositions = 0x7D7B, + BitDepth = 0x6264, + //end audio + //content encoding +// ContentEncodings = 0x6d80, +// ContentEncoding = 0x6240, +// ContentEncodingOrder = 0x5031, +// ContentEncodingScope = 0x5032, +// ContentEncodingType = 0x5033, +// ContentCompression = 0x5034, +// ContentCompAlgo = 0x4254, +// ContentCompSettings = 0x4255, +// ContentEncryption = 0x5035, +// ContentEncAlgo = 0x47e1, +// ContentEncKeyID = 0x47e2, +// ContentSignature = 0x47e3, +// ContentSigKeyID = 0x47e4, +// ContentSigAlgo = 0x47e5, +// ContentSigHashAlgo = 0x47e6, + //end content encoding + //Cueing Data + Cues = 0x1C53BB6B, + CuePoint = 0xBB, + CueTime = 0xB3, + CueTrackPositions = 0xB7, + CueTrack = 0xF7, + CueClusterPosition = 0xF1, + CueBlockNumber = 0x5378, +// CueCodecState = 0xEA, +// CueReference = 0xDB, +// CueRefTime = 0x96, +// CueRefCluster = 0x97, +// CueRefNumber = 0x535F, +// CueRefCodecState = 0xEB, + //Attachment +// Attachments = 0x1941A469, +// AttachedFile = 0x61A7, +// FileDescription = 0x467E, +// FileName = 0x466E, +// FileMimeType = 0x4660, +// FileData = 0x465C, +// FileUID = 0x46AE, +// FileReferral = 0x4675, + //Chapters +// Chapters = 0x1043A770, +// EditionEntry = 0x45B9, +// EditionUID = 0x45BC, +// EditionFlagHidden = 0x45BD, +// EditionFlagDefault = 0x45DB, +// EditionFlagOrdered = 0x45DD, +// ChapterAtom = 0xB6, +// ChapterUID = 0x73C4, +// ChapterTimeStart = 0x91, +// ChapterTimeEnd = 0x92, +// ChapterFlagHidden = 0x98, +// ChapterFlagEnabled = 0x4598, +// ChapterSegmentUID = 0x6E67, +// ChapterSegmentEditionUID = 0x6EBC, +// ChapterPhysicalEquiv = 0x63C3, +// ChapterTrack = 0x8F, +// ChapterTrackNumber = 0x89, +// ChapterDisplay = 0x80, +// ChapString = 0x85, +// ChapLanguage = 0x437C, +// ChapCountry = 0x437E, +// ChapProcess = 0x6944, +// ChapProcessCodecID = 0x6955, +// ChapProcessPrivate = 0x450D, +// ChapProcessCommand = 0x6911, +// ChapProcessTime = 0x6922, +// ChapProcessData = 0x6933, + //Tagging +// Tags = 0x1254C367, +// Tag = 0x7373, +// Targets = 0x63C0, +// TargetTypeValue = 0x68CA, +// TargetType = 0x63CA, +// Tagging_TrackUID = 0x63C5, +// Tagging_EditionUID = 0x63C9, +// Tagging_ChapterUID = 0x63C4, +// AttachmentUID = 0x63C6, +// SimpleTag = 0x67C8, +// TagName = 0x45A3, +// TagLanguage = 0x447A, +// TagDefault = 0x4484, +// TagString = 0x4487, +// TagBinary = 0x4485, +}; +#endif \ No newline at end of file diff --git a/libmkv/EbmlWriter.c b/libmkv/EbmlWriter.c new file mode 100644 index 0000000..46d2dd8 --- /dev/null +++ b/libmkv/EbmlWriter.c @@ -0,0 +1,160 @@ +// 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 "EbmlWriter.h" +#include +#include +#include + + +void Ebml_WriteLen(EbmlGlobal *glob, long long val) +{ + //TODO check and make sure we are not > than 0x0100000000000000LLU + unsigned char size = 8; //size in bytes to output + unsigned long long minVal = 0x00000000000000ffLLU; //mask to compare for byte size + + for (size = 1; size < 8; size ++) + { + if (val < minVal) + break; + + minVal = (minVal << 7); + } + + val |= (0x000000000000080LLU << ((size - 1) * 7)); + + Ebml_Serialize(glob, (void *) &val, size); +} + +void Ebml_WriteString(EbmlGlobal *glob, const char *str) +{ + const size_t size_ = strlen(str); + const unsigned long long size = size_; + Ebml_WriteLen(glob, size); + //TODO: it's not clear from the spec whether the nul terminator + //should be serialized too. For now we omit the null terminator. + Ebml_Write(glob, str, size); +} + +void Ebml_WriteUTF8(EbmlGlobal *glob, const wchar_t *wstr) +{ + const size_t strlen = wcslen(wstr); + + //TODO: it's not clear from the spec whether the nul terminator + //should be serialized too. For now we include it. + const unsigned long long size = strlen; + + Ebml_WriteLen(glob, size); + Ebml_Write(glob, wstr, size); +} + +void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id) +{ + if (class_id >= 0x01000000) + Ebml_Serialize(glob, (void *)&class_id, 4); + else if (class_id >= 0x00010000) + Ebml_Serialize(glob, (void *)&class_id, 3); + else if (class_id >= 0x00000100) + Ebml_Serialize(glob, (void *)&class_id, 2); + else + Ebml_Serialize(glob, (void *)&class_id, 1); +} +void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, UInt64 ui) +{ + unsigned char sizeSerialized = 8 | 0x80; + Ebml_WriteID(glob, class_id); + Ebml_Serialize(glob, &sizeSerialized, 1); + Ebml_Serialize(glob, &ui, 8); +} + +void Ebml_SerializeUnsigned(EbmlGlobal *glob, unsigned long class_id, unsigned long ui) +{ + unsigned char size = 8; //size in bytes to output + unsigned char sizeSerialized = 0; + Ebml_WriteID(glob, class_id); + unsigned long minVal; + minVal = 0x7fLU; //mask to compare for byte size + + for (size = 1; size < 4; size ++) + { + if (ui < minVal) + { + break; + } + + minVal <<= 7; + } + + sizeSerialized = 0x80 | size; + Ebml_Serialize(glob, &sizeSerialized, 1); + unsigned int _s = size; + Ebml_Serialize(glob, &ui, size); +} +//TODO: perhaps this is a poor name for this id serializer helper function +void Ebml_SerializeBinary(EbmlGlobal *glob, unsigned long class_id, unsigned long bin) +{ + int size; + for (size=4; size > 1; size--) + { + if (bin & 0x000000ff << ((size-1) * 8)) + break; + } + Ebml_WriteID(glob, class_id); + Ebml_WriteLen(glob, size); + Ebml_WriteID(glob, bin); +} + +void Ebml_SerializeFloat(EbmlGlobal *glob, unsigned long class_id, double d) +{ + Ebml_WriteID(glob, class_id); + unsigned char len = 0x88; + Ebml_Serialize(glob, &len, 1); + Ebml_Serialize(glob, &d, 8); +} + +void Ebml_WriteSigned16(EbmlGlobal *glob, short val) +{ + signed long out = ((val & 0x003FFFFF) | 0x00200000) << 8; + Ebml_Serialize(glob, &out, 3); +} + +void Ebml_SerializeString(EbmlGlobal *glob, unsigned long class_id, const char *s) +{ + Ebml_WriteID(glob, class_id); + Ebml_WriteString(glob, s); +} + +void Ebml_SerializeUTF8(EbmlGlobal *glob, unsigned long class_id, wchar_t *s) +{ + Ebml_WriteID(glob, class_id); + Ebml_WriteUTF8(glob, s); +} + +void Ebml_SerializeData(EbmlGlobal *glob, unsigned long class_id, unsigned char *data, unsigned long data_length) +{ + unsigned char size = 4; + Ebml_WriteID(glob, class_id); + Ebml_WriteLen(glob, data_length); + Ebml_Write(glob, data, data_length); +} + +void Ebml_WriteVoid(EbmlGlobal *glob, unsigned long vSize) +{ + Ebml_WriteID(glob, 0xEC); + unsigned char tmp = 0; + unsigned long i = 0; + Ebml_WriteLen(glob, vSize); + + for (i = 0; i < vSize; i++) + { + Ebml_Write(glob, &tmp, 1); + } +} + +//TODO Serialize Date diff --git a/libmkv/EbmlWriter.h b/libmkv/EbmlWriter.h new file mode 100644 index 0000000..e149f39 --- /dev/null +++ b/libmkv/EbmlWriter.h @@ -0,0 +1,38 @@ +#ifndef EBMLWRITER_HPP +#define EBMLWRITER_HPP + +// 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. + + +//If you wish a different writer simply replace this +//note: you must define write and serialize functions as well as your own EBML_GLOBAL +#include +#include "EbmlBufferWriter.h" +//These functions MUST be implemented +void Ebml_Serialize(EbmlGlobal *glob, const void *, unsigned long); +void Ebml_Write(EbmlGlobal *glob, const void *, unsigned long); +///// + + +void Ebml_WriteLen(EbmlGlobal *glob, long long val); +void Ebml_WriteString(EbmlGlobal *glob, const char *str); +void Ebml_WriteUTF8(EbmlGlobal *glob, const wchar_t *wstr); +void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id); +void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, UInt64 ui); +void Ebml_SerializeUnsigned(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); +void Ebml_SerializeBinary(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); +void Ebml_SerializeFloat(EbmlGlobal *glob, unsigned long class_id, double d); +//TODO make this more generic to signed +void Ebml_WriteSigned16(EbmlGlobal *glob, short val); +void Ebml_SerializeString(EbmlGlobal *glob, unsigned long class_id, const char *s); +void Ebml_SerializeUTF8(EbmlGlobal *glob, unsigned long class_id, wchar_t *s); +void Ebml_SerializeData(EbmlGlobal *glob, unsigned long class_id, unsigned char *data, unsigned long data_length); +void Ebml_WriteVoid(EbmlGlobal *glob, unsigned long vSize); +//TODO need date function +#endif diff --git a/libmkv/Makefile b/libmkv/Makefile new file mode 100644 index 0000000..b53377b --- /dev/null +++ b/libmkv/Makefile @@ -0,0 +1,25 @@ +#Variables +CC=gcc +LINKER=gcc +FLAGS= + + +#Build Targets +EbmlWriter.o: EbmlWriter.c EbmlWriter.h + $(CC) $(FLAGS) -c EbmlWriter.c + +EbmlBufferWriter.o: EbmlBufferWriter.c EbmlBufferWriter.h + $(CC) $(FLAGS) -c EbmlBufferWriter.c + +MkvElement.o: MkvElement.c WebMElement.h + $(CC) $(FLAGS) -c MkvElement.c + +testlibmkv.o: testlibmkv.c + $(CC) $(FLAGS) -c testlibmkv.c + +testlibmkv: testlibmkv.o MkvElement.o EbmlBufferWriter.o EbmlWriter.o + $(LINKER) $(FLAGS) testlibmkv.o MkvElement.o EbmlBufferWriter.o EbmlWriter.o -o testlibmkv + +clean: + rm -rf *.o testlibmkv + \ No newline at end of file diff --git a/libmkv/WebMElement.c b/libmkv/WebMElement.c new file mode 100644 index 0000000..25a9024 --- /dev/null +++ b/libmkv/WebMElement.c @@ -0,0 +1,220 @@ +// 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 "EbmlBufferWriter.h" +#include "EbmlIDs.h" +#include "WebMElement.h" +#include + +#define kVorbisPrivateMaxSize 4000 + +void writeHeader(EbmlGlobal *glob) +{ + EbmlLoc start; + Ebml_StartSubElement(glob, &start, EBML); + Ebml_SerializeUnsigned(glob, EBMLVersion, 1); + Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); //EBML Read Version + Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); //EBML Max ID Length + Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); //EBML Max Size Length + Ebml_SerializeString(glob, DocType, "webm"); //Doc Type + Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); //Doc Type Version + Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); //Doc Type Read Version + Ebml_EndSubElement(glob, &start); +} + +void writeSimpleBlock(EbmlGlobal *glob, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength) +{ + Ebml_WriteID(glob, SimpleBlock); + unsigned long blockLength = 4 + dataLength; + blockLength |= 0x10000000; //TODO check length < 0x0FFFFFFFF + Ebml_Serialize(glob, &blockLength, 4); + trackNumber |= 0x80; //TODO check track nubmer < 128 + Ebml_Write(glob, &trackNumber, 1); + //Ebml_WriteSigned16(glob, timeCode,2); //this is 3 bytes + Ebml_Serialize(glob, &timeCode, 2); + unsigned char flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; + Ebml_Write(glob, &flags, 1); + Ebml_Write(glob, data, dataLength); +} + +static UInt64 generateTrackID(unsigned int trackNumber) +{ + UInt64 t = time(NULL) * trackNumber; + UInt64 r = rand(); + r = r << 32; + r += rand(); + UInt64 rval = t ^ r; + return rval; +} + +void writeVideoTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, + double frameRate) +{ + EbmlLoc start; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); + UInt64 trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeString(glob, CodecName, "VP8"); //TODO shouldn't be fixed + + Ebml_SerializeUnsigned(glob, TrackType, 1); //video is always 1 + Ebml_SerializeString(glob, CodecID, codecId); + { + EbmlLoc videoStart; + Ebml_StartSubElement(glob, &videoStart, Video); + Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); + Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); + Ebml_SerializeFloat(glob, FrameRate, frameRate); + Ebml_EndSubElement(glob, &videoStart); //Video + } + Ebml_EndSubElement(glob, &start); //Track Entry +} +void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, + unsigned char *private, unsigned long privateSize) +{ + EbmlLoc start; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); + UInt64 trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeUnsigned(glob, TrackType, 2); //audio is always 2 + //I am using defaults for thesed required fields + /* Ebml_SerializeUnsigned(glob, FlagEnabled, 1); + Ebml_SerializeUnsigned(glob, FlagDefault, 1); + Ebml_SerializeUnsigned(glob, FlagForced, 1); + Ebml_SerializeUnsigned(glob, FlagLacing, flagLacing);*/ + Ebml_SerializeString(glob, CodecID, codecId); + Ebml_SerializeData(glob, CodecPrivate, private, privateSize); + + Ebml_SerializeString(glob, CodecName, "VORBIS"); //fixed for now + { + EbmlLoc AudioStart; + Ebml_StartSubElement(glob, &AudioStart, Audio); + Ebml_SerializeFloat(glob, SamplingFrequency, samplingFrequency); + Ebml_SerializeUnsigned(glob, Channels, channels); + Ebml_EndSubElement(glob, &AudioStart); + } + Ebml_EndSubElement(glob, &start); +} +void writeSegmentInformation(EbmlGlobal *ebml, EbmlLoc* startInfo, unsigned long timeCodeScale, double duration) +{ + Ebml_StartSubElement(ebml, startInfo, Info); + Ebml_SerializeUnsigned(ebml, TimecodeScale, timeCodeScale); + Ebml_SerializeFloat(ebml, Segment_Duration, duration * 1000.0); //Currently fixed to using milliseconds + Ebml_SerializeString(ebml, 0x4D80, "QTmuxingAppLibWebM-0.0.1"); + Ebml_SerializeString(ebml, 0x5741, "QTwritingAppLibWebM-0.0.1"); + Ebml_EndSubElement(ebml, startInfo); +} + +/* +void Mkv_InitializeSegment(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x18538067); +} + +void Mkv_InitializeSeek(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x114d9b74); +} +void Mkv_WriteSeekInformation(Ebml& ebml_out, SeekStruct& seekInformation) +{ + EbmlLoc ebmlLoc; + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x4dbb); + Ebml_SerializeString(ebml_out, 0x53ab, seekInformation.SeekID); + Ebml_SerializeUnsigned(ebml_out, 0x53ac, seekInformation.SeekPosition); + Ebml_EndSubElement(ebml_out, ebmlLoc); +} + +void Mkv_WriteSegmentInformation(Ebml& ebml_out, SegmentInformationStruct& segmentInformation) +{ + Ebml_SerializeUnsigned(ebml_out, 0x73a4, segmentInformation.segmentUID); + if (segmentInformation.filename != 0) + Ebml_SerializeString(ebml_out, 0x7384, segmentInformation.filename); + Ebml_SerializeUnsigned(ebml_out, 0x2AD7B1, segmentInformation.TimecodeScale); + Ebml_SerializeUnsigned(ebml_out, 0x4489, segmentInformation.Duration); + //TODO date + Ebml_SerializeWString(ebml_out, 0x4D80, L"MKVMUX"); + Ebml_SerializeWString(ebml_out, 0x5741, segmentInformation.WritingApp); +} + +void Mkv_InitializeTrack(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x1654AE6B); +} + +static void Mkv_WriteGenericTrackData(Ebml& ebml_out, TrackStruct& track) +{ + Ebml_SerializeUnsigned(ebml_out, 0xD7, track.TrackNumber); + Ebml_SerializeUnsigned(ebml_out, 0x73C5, track.TrackUID); + Ebml_SerializeUnsigned(ebml_out, 0x83, track.TrackType); + Ebml_SerializeUnsigned(ebml_out, 0xB9, track.FlagEnabled ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0x88, track.FlagDefault ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0x55AA, track.FlagForced ? 1 :0); + if (track.Language != 0) + Ebml_SerializeString(ebml_out, 0x22B59C, track.Language); + if (track.CodecID != 0) + Ebml_SerializeString(ebml_out, 0x86, track.CodecID); + if (track.CodecPrivate != 0) + Ebml_SerializeData(ebml_out, 0x63A2, track.CodecPrivate, track.CodecPrivateLength); + if (track.CodecName != 0) + Ebml_SerializeWString(ebml_out, 0x258688, track.CodecName); +} + +void Mkv_WriteVideoTrack(Ebml& ebml_out, TrackStruct & track, VideoTrackStruct& video) +{ + EbmlLoc trackHeadLoc, videoHeadLoc; + Ebml_StartSubElement(ebml_out, trackHeadLoc, 0xAE); //start Track + Mkv_WriteGenericTrackData(ebml_out, track); + Ebml_StartSubElement(ebml_out, videoHeadLoc, 0xE0); //start Video + Ebml_SerializeUnsigned(ebml_out, 0x9A, video.FlagInterlaced ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0xB0, video.PixelWidth); + Ebml_SerializeUnsigned(ebml_out, 0xBA, video.PixelHeight); + Ebml_SerializeUnsigned(ebml_out, 0x54B0, video.PixelDisplayWidth); + Ebml_SerializeUnsigned(ebml_out, 0x54BA, video.PixelDisplayHeight); + Ebml_SerializeUnsigned(ebml_out, 0x54B2, video.displayUnit); + Ebml_SerializeFloat(ebml_out, 0x2383E3, video.FrameRate); + Ebml_EndSubElement(ebml_out, videoHeadLoc); + Ebml_EndSubElement(ebml_out, trackHeadLoc); + +} + +void Mkv_WriteAudioTrack(Ebml& ebml_out, TrackStruct & track, AudioTrackStruct& video) +{ + EbmlLoc trackHeadLoc, audioHeadLoc; + Ebml_StartSubElement(ebml_out, trackHeadLoc, 0xAE); + Mkv_WriteGenericTrackData(ebml_out, track); + Ebml_StartSubElement(ebml_out, audioHeadLoc, 0xE0); //start Audio + Ebml_SerializeFloat(ebml_out, 0xB5, video.SamplingFrequency); + Ebml_SerializeUnsigned(ebml_out, 0x9F, video.Channels); + Ebml_SerializeUnsigned(ebml_out, 0x6264, video.BitDepth); + Ebml_EndSubElement(ebml_out, audioHeadLoc); // end audio + Ebml_EndSubElement(ebml_out, trackHeadLoc); +} + +void Mkv_WriteEbmlClusterHead(Ebml& ebml_out, EbmlLoc& ebmlLoc, ClusterHeadStruct & clusterHead) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x1F43B675); + Ebml_SerializeUnsigned(ebml_out, 0x6264, clusterHead.TimeCode); +} + +void Mkv_WriteSimpleBlockHead(Ebml& ebml_out, EbmlLoc& ebmlLoc, SimpleBlockStruct& block) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0xA3); + Ebml_Write1UInt(ebml_out, block.TrackNumber); + Ebml_WriteSigned16(ebml_out,block.TimeCode); + unsigned char flags = 0x00 | (block.iskey ? 0x80:0x00) | (block.lacing << 1) | block.discardable; + Ebml_Write1UInt(ebml_out, flags); //TODO this may be the wrong function + Ebml_Serialize(ebml_out, block.data, block.dataLength); + Ebml_EndSubElement(ebml_out,ebmlLoc); +} +*/ diff --git a/libmkv/WebMElement.h b/libmkv/WebMElement.h new file mode 100644 index 0000000..b4208f2 --- /dev/null +++ b/libmkv/WebMElement.h @@ -0,0 +1,35 @@ +// 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. + + +#ifndef MKV_CONTEXT_HPP +#define MKV_CONTEXT_HPP 1 + +void writeSimpleBock(EbmlGlobal *ebml, unsigned char trackNumber, unsigned short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength); + + +// these are helper functions +void writeHeader(EbmlGlobal *ebml); +void writeSegmentInformation(EbmlGlobal *ebml, EbmlLoc* startInfo , unsigned long timeCodeScale, double duration); +//this function is a helper only, it assumes a lot of defaults +void writeVideoTrack(EbmlGlobal *ebml, unsigned int trackNumber, int flagLacing, + char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, + double frameRate); +void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, + unsigned char *private, unsigned long privateSize); + +void writeSimpleBlock(EbmlGlobal *ebml, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength); + + + +#endif \ No newline at end of file diff --git a/libmkv/testlibmkv.c b/libmkv/testlibmkv.c new file mode 100644 index 0000000..7edfc43 --- /dev/null +++ b/libmkv/testlibmkv.c @@ -0,0 +1,63 @@ +// 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 "EbmlIDs.h" +#include "EbmlBufferWriter.h" +#include "WebMElement.h" + +#include +int main(int argc, char *argv[]) +{ + //init the datatype we're using for ebml output + unsigned char data[8192]; + EbmlGlobal ebml; + ebml.buf = data; + ebml.offset = 0; + ebml.length = 8192; + + writeHeader(&ebml); + { + EbmlLoc startSegment; + Ebml_StartSubElement(&ebml, &startSegment, Segment); //segment + { + //segment info + EbmlLoc startInfo; + Ebml_StartSubElement(&ebml, &startInfo, Info); + Ebml_SerializeString(&ebml, 0x4D80, "muxingAppLibMkv"); + Ebml_SerializeString(&ebml, 0x5741, "writingAppLibMkv"); + Ebml_EndSubElement(&ebml, &startInfo); + } + + { + EbmlLoc trackStart; + Ebml_StartSubElement(&ebml, &trackStart, Tracks); + writeVideoTrack(&ebml, 1, 1, "V_MS/VFW/FOURCC", 320, 240, 29.97); + //writeAudioTrack(&ebml,2,1, "A_VORBIS", 32000, 1, NULL, 0); + Ebml_EndSubElement(&ebml, &trackStart); + } + + { + EbmlLoc clusterStart; + Ebml_StartSubElement(&ebml, &clusterStart, Cluster); //cluster + Ebml_SerializeUnsigned(&ebml, Timecode, 0); + + unsigned char someData[4] = {1, 2, 3, 4}; + writeSimpleBlock(&ebml, 1, 0, 1, 0, 0, someData, 4); + Ebml_EndSubElement(&ebml, &clusterStart); + } //end cluster + Ebml_EndSubElement(&ebml, &startSegment); + } + + //dump ebml stuff to the file + FILE *file_out = fopen("test.mkv", "wb"); + size_t bytesWritten = fwrite(data, 1, ebml.offset, file_out); + fclose(file_out); + return 0; +} \ No newline at end of file -- 2.7.4