From a1096d05e058983f9d30b139e837c02dc9ac5e6a Mon Sep 17 00:00:00 2001 From: Nathan Caldwell Date: Tue, 22 Apr 2008 01:29:40 -0600 Subject: [PATCH] MASSIVE formatting cleanups. -Use an (slightly) modified K&R style for most of the code. --- include/libmkv.h | 138 ++++--- src/chapters.c | 159 ++++---- src/ebml.c | 743 ++++++++++++++++++------------------ src/ebml.h | 73 ++-- src/matroska.c | 1105 ++++++++++++++++++++++++++++-------------------------- src/matroska.h | 534 +++++++++++++------------- src/md5.c | 897 ++++++++++++++++++++++---------------------- src/md5.h | 45 ++- src/tracks.c | 369 +++++++++--------- 9 files changed, 2093 insertions(+), 1970 deletions(-) rewrite src/chapters.c (64%) rewrite src/ebml.c (86%) rewrite src/matroska.c (90%) rewrite src/matroska.h (78%) rewrite src/md5.c (77%) rewrite src/tracks.c (80%) diff --git a/include/libmkv.h b/include/libmkv.h index 37fdb83..b144538 100644 --- a/include/libmkv.h +++ b/include/libmkv.h @@ -2,7 +2,6 @@ * libmkv.h: ***************************************************************************** * Copyright (C) 2005 x264 project - * $Id: $ * * Authors: Mike Matsnev * Nathan Caldwell @@ -26,7 +25,7 @@ #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ +#endif /* __cplusplus */ #include #include @@ -97,26 +96,26 @@ extern "C" { #define MK_SUBTITLE_BMP "S_IMAGE/BMP" typedef enum { - MK_TRACK_VIDEO = 0x01, - MK_TRACK_AUDIO = 0x02, - MK_TRACK_COMPLEX = 0x03, - MK_TRACK_LOGO = 0x10, - MK_TRACK_SUBTITLE = 0x11, - MK_TRACK_BUTTONS = 0x12, - MK_TRACK_CONTROL = 0x20 + MK_TRACK_VIDEO = 0x01, + MK_TRACK_AUDIO = 0x02, + MK_TRACK_COMPLEX = 0x03, + MK_TRACK_LOGO = 0x10, + MK_TRACK_SUBTITLE = 0x11, + MK_TRACK_BUTTONS = 0x12, + MK_TRACK_CONTROL = 0x20 } mk_TrackType; typedef enum { - MK_LACING_NONE = 0x00, - MK_LACING_XIPH, - MK_LACING_FIXED, - MK_LACING_EBML + MK_LACING_NONE = 0x00, + MK_LACING_XIPH, + MK_LACING_FIXED, + MK_LACING_EBML } mk_LacingType; typedef enum { - MK_ASPECTRATIO_FREE = 0x00, - MK_ASPECTRATIO_KEEP, - MK_ASPECTRATIO_FIXED + MK_ASPECTRATIO_FREE = 0x00, + MK_ASPECTRATIO_KEEP, + MK_ASPECTRATIO_FIXED } mk_AspectType; typedef struct mk_Writer_s mk_Writer; @@ -124,56 +123,73 @@ typedef struct mk_Track_s mk_Track; typedef struct mk_TrackConfig_s mk_TrackConfig; struct mk_TrackConfig_s { - uint64_t trackUID; // Optional: Unique identifier for the track. - mk_TrackType trackType; // Required: 1 = Video, 2 = Audio. - int8_t flagEnabled; // Required: Set 1 if the track is used, 0 if unused. (Default: enabled) - int8_t flagDefault; // Required: Set 1 if this track is default, 0 if not default, -1 is undefined. - int8_t flagForced; // Optional: Set 1 if the track MUST be shown during playback (Default: disabled) - int8_t flagLacing; // Required: Set 1 if the track may contain blocks using lacing. - uint8_t minCache; // Optional: See Matroska spec. (Default: cache disabled) - uint8_t maxCache; - int64_t defaultDuration; // Optional: Number of nanoseconds per frame. - char *name; - char *language; - char *codecID; // Required: See codecs above. - void *codecPrivate; - unsigned codecPrivateSize; - char *codecName; - union { - struct { - char flagInterlaced; - unsigned pixelWidth; // Pixel width - unsigned pixelHeight; // Pixel height - unsigned pixelCrop[4]; // Pixel crop - 0 = bottom, 1 = top, 2 = left, 3 = right - unsigned displayWidth; // Display width - unsigned displayHeight; // Display height - char displayUnit; // Display Units - 0 = pixels, 1 = cm, 2 = in - mk_AspectType aspectRatioType; /* Specifies the possible modifications to the aspect ratio */ - } video; - struct { - float samplingFreq; // Sampling Frequency in Hz - unsigned channels; // Number of channels for this track - unsigned bitDepth; // Bits per sample (PCM) - } audio; - } extra; + uint64_t trackUID; /* Optional: Unique identifier for the track. */ + mk_TrackType trackType; /* Required: 1 = Video, 2 = Audio. */ + int8_t flagEnabled; /* Required: Set 1 if the track is used, + * 0 if unused. (Default: enabled) + */ + int8_t flagDefault; /* Required: Set 1 if this track is default, + * 0 if not default, -1 is undefined. + */ + int8_t flagForced; /* Optional: Set 1 if the track MUST be shown during playback + * (Default: disabled) + */ + int8_t flagLacing; /* Required: Set 1 if the track may contain blocks using lacing. */ + uint8_t minCache; /* Optional: See Matroska spec. (Default: cache disabled) */ + uint8_t maxCache; + int64_t defaultDuration; /* Optional: Number of nanoseconds per frame. */ + char *name; + char *language; + char *codecID; /* Required: See codecs above. */ + void *codecPrivate; + unsigned codecPrivateSize; + char *codecName; + union { + struct { + char flagInterlaced; + unsigned pixelWidth; /* Pixel width */ + unsigned pixelHeight; /* Pixel height */ + unsigned pixelCrop[4]; /* Pixel crop - 0 = bottom, 1 = top, 2 = left, 3 = right */ + unsigned displayWidth; /* Display width */ + unsigned displayHeight; /* Display height */ + char displayUnit; /* Display Units - 0 = pixels, 1 = cm, 2 = in */ + mk_AspectType aspectRatioType; /* Specifies the allowed + * modifications to the aspect ratio + */ + } video; + struct { + float samplingFreq; /* Sampling Frequency in Hz */ + unsigned channels; /* Number of channels for this track */ + unsigned bitDepth; /* Bits per sample (PCM) */ + } audio; + } extra; }; -mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat); - /* vlc_compat writes the Seek entries at the top of the file because VLC stops parsing * - * once it finds the first cluster. However, this creates extra overhead in the file. */ +/* vlc_compat writes the Seek entries at the top of the file because VLC + * stops parsing once it finds the first cluster. However, this creates + * extra overhead in the file. + */ +mk_Writer *mk_createWriter(const char *filename, int64_t timescale, + uint8_t vlc_compat); mk_Track *mk_createTrack(mk_Writer *w, mk_TrackConfig *tc); -int mk_writeHeader(mk_Writer *w, const char *writingApp); -int mk_startFrame( mk_Writer *w, mk_Track *track ); -int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size); -int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe); -int mk_setFrameLacing(mk_Writer *w, mk_Track *track, mk_LacingType lacing, uint8_t num_frames, uint64_t sizes[]); -int mk_createChapterSimple(mk_Writer *w, uint64_t start, uint64_t end, char *name); -int mk_close( mk_Writer *w ); +int mk_writeHeader(mk_Writer *w, const char *writingApp); +int mk_startFrame(mk_Writer *w, mk_Track *track); +int mk_addFrameData(mk_Writer * w, mk_Track *track, const void *data, + unsigned size); +int mk_setFrameFlags(mk_Writer * w, mk_Track *track, int64_t timestamp, + unsigned keyframe); +int mk_setFrameLacing(mk_Writer *w, mk_Track * track, + mk_LacingType lacing, uint8_t num_frames, + uint64_t sizes[]); +int mk_createChapterSimple(mk_Writer *w, uint64_t start, uint64_t end, + char *name); +int mk_close(mk_Writer *w); -char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, uint64_t *output_size); +char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, + uint64_t *output_size); #ifdef __cplusplus } -#endif /* __cplusplus */ +#endif /* __cplusplus */ -#endif /* _LIBMKV_H */ +#endif /* _LIBMKV_H */ diff --git a/src/chapters.c b/src/chapters.c dissimilarity index 64% index 70f4d56..77d7d68 100644 --- a/src/chapters.c +++ b/src/chapters.c @@ -1,76 +1,83 @@ -/***************************************************************************** - * chapters.c: - ***************************************************************************** - * Copyright (C) 2007 libmkv - * $Id: $ - * - * Authors: Nathan Caldwell - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ -#include "config.h" -#include "libmkv.h" -#include "matroska.h" - -int mk_createChapterSimple(mk_Writer *w, uint64_t start, uint64_t end, char *name) -{ - mk_Context *ca, *cd; - unsigned long chapter_uid; - - /* - * Generate a random UID for this Chapter. - * NOTE: This probably should be a CRC32 of some unique chapter information. - * In place of being completely random. - */ - chapter_uid = random(); - - if (w->chapters == NULL) - { - unsigned long edition_uid; - edition_uid = random(); - - if ((w->chapters = mk_createContext(w, w->root, MATROSKA_ID_CHAPTERS)) == NULL) // Chapters - return -1; - if ((w->edition_entry = mk_createContext(w, w->chapters, MATROSKA_ID_EDITIONENTRY)) == NULL) // EditionEntry - return -1; - CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONUID, edition_uid)); /* EditionUID - See note above about Chapter UID. */ - CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONFLAGDEFAULT, 1)); // EditionFlagDefault - Force this to be the default. - CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONFLAGORDERED, 0)); // EditionFlagOrdered - Force simple chapters. - } - if ((ca = mk_createContext(w, w->edition_entry, MATROSKA_ID_CHAPTERATOM)) == NULL) // ChapterAtom - return -1; - CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERUID, chapter_uid)); /* ChapterUID */ - CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERTIMESTART, start)); // ChapterTimeStart - if (end != start) // Just create a StartTime if chapter length would be 0. - CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERTIMEEND, end)); // ChapterTimeEnd - if (name != NULL) { - if ((cd = mk_createContext(w, ca, MATROSKA_ID_CHAPTERDISPLAY)) == NULL) // ChapterDisplay - return -1; - CHECK(mk_writeStr(cd, MATROSKA_ID_CHAPTERSTRING, name)); // ChapterString - CHECK(mk_closeContext(cd, 0)); - } - CHECK(mk_closeContext(ca, 0)); - - return 0; -} - -int mk_writeChapters(mk_Writer *w) { - if ((w->chapters == NULL) || (w->edition_entry == NULL)) - return -1; - CHECK(mk_closeContext(w->edition_entry, 0)); - CHECK(mk_closeContext(w->chapters, 0)); - - return 0; -} +/***************************************************************************** + * chapters.c: + ***************************************************************************** + * Copyright (C) 2007 libmkv + * + * Authors: Nathan Caldwell + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ +#include "config.h" +#include "libmkv.h" +#include "matroska.h" + +int mk_createChapterSimple(mk_Writer *w, uint64_t start, uint64_t end, + char *name) +{ + mk_Context *ca, *cd; + unsigned long chapter_uid; + + /* + * Generate a random UID for this Chapter. + * NOTE: This probably should be a CRC32 of some unique chapter information. + * In place of being completely random. + */ + chapter_uid = random(); + + if (w->chapters == NULL) { + unsigned long edition_uid; + edition_uid = random(); + + /* Chapters */ + if ((w->chapters = mk_createContext(w, w->root, MATROSKA_ID_CHAPTERS)) == NULL) + return -1; + /* EditionEntry */ + if ((w->edition_entry = mk_createContext(w, w->chapters, MATROSKA_ID_EDITIONENTRY)) == NULL) + return -1; + /* EditionUID - See note above about Chapter UID. */ + CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONUID, edition_uid)); + /* EditionFlagDefault - This is set to 1 to force this Edition to be the default one. */ + CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONFLAGDEFAULT, 1)); + /* EditionFlagOrdered - Force simple chapters. */ + CHECK(mk_writeUInt(w->edition_entry, MATROSKA_ID_EDITIONFLAGORDERED, 0)); + } + /* ChapterAtom */ + if ((ca = mk_createContext(w, w->edition_entry, MATROSKA_ID_CHAPTERATOM)) == NULL) + return -1; + CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERUID, chapter_uid)); /* ChapterUID */ + CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERTIMESTART, start)); /* ChapterTimeStart */ + if (end != start) /* Only create a StartTime if chapter length would be 0. */ + CHECK(mk_writeUInt(ca, MATROSKA_ID_CHAPTERTIMEEND, end)); /* ChapterTimeEnd */ + if (name != NULL) { + /* ChapterDisplay */ + if ((cd = mk_createContext(w, ca, MATROSKA_ID_CHAPTERDISPLAY)) == NULL) + return -1; + CHECK(mk_writeStr(cd, MATROSKA_ID_CHAPTERSTRING, name)); /* ChapterString */ + CHECK(mk_closeContext(cd, 0)); + } + CHECK(mk_closeContext(ca, 0)); + + return 0; +} + +int mk_writeChapters(mk_Writer *w) +{ + if ((w->chapters == NULL) || (w->edition_entry == NULL)) + return -1; + CHECK(mk_closeContext(w->edition_entry, 0)); + CHECK(mk_closeContext(w->chapters, 0)); + + return 0; +} diff --git a/src/ebml.c b/src/ebml.c dissimilarity index 86% index 2a461f3..8443fc3 100644 --- a/src/ebml.c +++ b/src/ebml.c @@ -1,356 +1,387 @@ -/***************************************************************************** - * matroska.c: - ***************************************************************************** - * Copyright (C) 2005 x264 project - * $Id: $ - * - * Authors: Mike Matsnev - * Nathan Caldwell - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ -#include "config.h" -#include "libmkv.h" -#include "matroska.h" - -mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) { - mk_Context *c; - - if (w->freelist) { - c = w->freelist; - w->freelist = w->freelist->next; - } else { - c = malloc(sizeof(*c)); - memset(c, 0, sizeof(*c)); - } - - if (c == NULL) - return NULL; - - c->parent = parent; - c->owner = w; - c->id = id; - - if (c->owner->actlist) - c->owner->actlist->prev = &c->next; - c->next = c->owner->actlist; - c->prev = &c->owner->actlist; - c->owner->actlist = c; - - return c; -} - -int mk_appendContextData(mk_Context *c, const void *data, uint64_t size) { - uint64_t ns = c->d_cur + size; - - if (ns > c->d_max) { - void *dp; - uint64_t dn = c->d_max ? c->d_max << 1 : 16; - while (ns > dn) - dn <<= 1; - - dp = realloc(c->data, dn); - if (dp == NULL) - return -1; - - c->data = dp; - c->d_max = dn; - } - - memcpy((char*)c->data + c->d_cur, data, size); - - c->d_cur = ns; - - return 0; -} - -int mk_writeEbmlHeader(mk_Writer *w, const char *doctype, uint64_t doctype_version, uint64_t doctype_readversion) { - mk_Context *c = NULL; - - if ((c = mk_createContext(w, w->root, EBML_ID_HEADER)) == NULL) // EBML - return -1; - CHECK(mk_writeUInt(c, EBML_ID_EBMLVERSION, EBML_VERSION)); // EBMLVersion - CHECK(mk_writeUInt(c, EBML_ID_EBMLREADVERSION, EBML_VERSION)); // EBMLReadVersion - CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXIDLENGTH, 4)); // EBMLMaxIDLength - CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXSIZELENGTH, 8)); // EBMLMaxSizeLength - CHECK(mk_writeStr(c, EBML_ID_DOCTYPE, doctype)); // DocType - CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEVERSION, doctype_version)); // DocTypeVersion - CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEREADVERSION, doctype_readversion)); // DocTypeReadversion - CHECK(mk_closeContext(c, 0)); - - return 0; -} - -int mk_writeID(mk_Context *c, unsigned id) { - unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id }; - - if (c_id[0]) - return mk_appendContextData(c, c_id, 4); - if (c_id[1]) - return mk_appendContextData(c, c_id+1, 3); - if (c_id[2]) - return mk_appendContextData(c, c_id+2, 2); - return mk_appendContextData(c, c_id+3, 1); -} - -int mk_writeSize(mk_Context *c, uint64_t size) { - unsigned char c_size[8] = { 0x01, size >> 48, size >> 40, size >> 32, size >> 24, size >> 16, size >> 8, size }; - - if (size < 0x7fll) { - c_size[7] |= 0x80; - return mk_appendContextData(c, c_size+7, 1); - } - if (size < 0x3fffll) { - c_size[6] |= 0x40; - return mk_appendContextData(c, c_size+6, 2); - } - if (size < 0x1fffffll) { - c_size[5] |= 0x20; - return mk_appendContextData(c, c_size+5, 3); - } - if (size < 0x0fffffffll) { - c_size[4] |= 0x10; - return mk_appendContextData(c, c_size+4, 4); - } - if (size < 0x07ffffffffll) { - c_size[3] |= 0x08; - return mk_appendContextData(c, c_size+3, 5); - } - if (size < 0x03ffffffffffll) { - c_size[2] |= 0x04; - return mk_appendContextData(c, c_size+2, 6); - } - if (size < 0x01ffffffffffffll) { - c_size[1] |= 0x02; - return mk_appendContextData(c, c_size+1, 7); - } - return mk_appendContextData(c, c_size, 8); -} - -int mk_writeSSize(mk_Context *c, int64_t size) { - uint64_t u_size = (uint64_t)llabs(size); - unsigned size_size = mk_ebmlSizeSize( u_size << 1 ); // We need to shift by one to get the correct size here. - - switch (size_size) - { - case 1: - size += 0x3fll; - break; - case 2: - size += 0x1fffll; - break; - case 3: - size += 0x0fffffll; - break; - case 4: - size += 0x07ffffffll; - break; - case 5: - size += 0x03ffffffffll; - break; - case 6: - size += 0x01ffffffffffll; - break; - case 7: - size += 0x00ffffffffffffll; - break; - default: // Matroska currently doesn't support any int > 56-bit. - return -1; - } - - return mk_writeSize(c, size); -} - -int mk_flushContextID(mk_Context *c) { - unsigned char size_undf[8] = {0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - if (c->id == 0) - return 0; - - CHECK(mk_writeID(c->parent, c->id)); - CHECK(mk_appendContextData(c->parent, &size_undf, 8)); - - c->id = 0; - - return 0; -} - -int mk_flushContextData(mk_Context *c) { - mk_Writer *w = c->owner; - - if (c->d_cur == 0) - return 0; - - if (c->parent) - CHECK(mk_appendContextData(c->parent, c->data, c->d_cur)); - else { - if (fwrite(c->data, c->d_cur, 1, w->fp) != 1) - return -1; - w->f_pos += c->d_cur; - if (w->f_pos > w->f_eof) - w->f_eof = w->f_pos; - } - - c->d_cur = 0; - - return 0; -} - -int mk_closeContext(mk_Context *c, int64_t *off) { - if (c->id) { - CHECK(mk_writeID(c->parent, c->id)); - CHECK(mk_writeSize(c->parent, c->d_cur)); - } - - if (c->parent && off != NULL) - *off += c->parent->d_cur; - - CHECK(mk_flushContextData(c)); - - if (c->next) - c->next->prev = c->prev; - *(c->prev) = c->next; - c->next = c->owner->freelist; - c->owner->freelist = c; - - return 0; -} - -void mk_destroyContexts(mk_Writer *w) { - mk_Context *cur, *next; - - for (cur = w->freelist; cur; cur = next) { - next = cur->next; - free(cur->data); - free(cur); - } - - for (cur = w->actlist; cur; cur = next) { - next = cur->next; - free(cur->data); - free(cur); - } - - w->freelist = w->actlist = w->root = NULL; -} - -int mk_writeStr(mk_Context *c, unsigned id, const char *str) { - size_t len = strlen(str); - - CHECK(mk_writeID(c, id)); - CHECK(mk_writeSize(c, len)); - CHECK(mk_appendContextData(c, str, len)); - return 0; -} - -int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) { - CHECK(mk_writeID(c, id)); - CHECK(mk_writeSize(c, size)); - CHECK(mk_appendContextData(c, data, size)); - return 0; -} - -int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui) { - unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui }; - unsigned i = 0; - - CHECK(mk_writeID(c, id)); - while (i < 7 && c_ui[i] == 0) - ++i; - CHECK(mk_writeSize(c, 8 - i)); - CHECK(mk_appendContextData(c, c_ui+i, 8 - i)); - return 0; -} - -int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) { - unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si }; - unsigned i = 0; - - CHECK(mk_writeID(c, id)); - if (si < 0) - while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80) - ++i; - else - while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80)) - ++i; - CHECK(mk_writeSize(c, 8 - i)); - CHECK(mk_appendContextData(c, c_si+i, 8 - i)); - return 0; -} - -int mk_writeFloatRaw(mk_Context *c, float f) { - union { - float f; - unsigned u; - } u; - unsigned char c_f[4]; - - u.f = f; - c_f[0] = u.u >> 24; - c_f[1] = u.u >> 16; - c_f[2] = u.u >> 8; - c_f[3] = u.u; - - return mk_appendContextData(c, c_f, 4); -} - -int mk_writeFloat(mk_Context *c, unsigned id, float f) { - CHECK(mk_writeID(c, id)); - CHECK(mk_writeSize(c, 4)); - CHECK(mk_writeFloatRaw(c, f)); - return 0; -} - -int mk_writeVoid(mk_Context *c, uint64_t length) { - char *c_void = calloc(length, sizeof(char)); - - CHECK(mk_writeID(c, EBML_ID_VOID)); - CHECK(mk_writeSize(c, length)); - CHECK(mk_appendContextData(c, c_void, length)); - free(c_void); - return 0; -} - -unsigned mk_ebmlSizeSize(uint64_t s) { - if (s < 0x7fll) - return 1; - if (s < 0x3fffll) - return 2; - if (s < 0x1fffffll) - return 3; - if (s < 0x0fffffffll) - return 4; - if (s < 0x07ffffffffll) - return 5; - if (s < 0x03ffffffffffll) - return 6; - if (s < 0x01ffffffffffffll) - return 7; - return 8; -} - -unsigned mk_ebmlSIntSize(int64_t si) { - unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si }; - unsigned i = 0; - - if (si < 0) - while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80) - ++i; - else - while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80)) - ++i; - - return 8 - i; -} +/***************************************************************************** + * matroska.c: + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Authors: Mike Matsnev + * Nathan Caldwell + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ +#include "config.h" +#include "libmkv.h" +#include "matroska.h" + +mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, + unsigned id) +{ + mk_Context *c; + + if (w->freelist) { + c = w->freelist; + w->freelist = w->freelist->next; + } else { + c = malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + } + + if (c == NULL) + return NULL; + + c->parent = parent; + c->owner = w; + c->id = id; + + if (c->owner->actlist) + c->owner->actlist->prev = &c->next; + c->next = c->owner->actlist; + c->prev = &c->owner->actlist; + c->owner->actlist = c; + + return c; +} + +int mk_appendContextData(mk_Context *c, const void *data, uint64_t size) +{ + uint64_t ns = c->d_cur + size; + + if (ns > c->d_max) { + void *dp; + uint64_t dn = c->d_max ? c->d_max << 1 : 16; + while (ns > dn) + dn <<= 1; + + dp = realloc(c->data, dn); + if (dp == NULL) + return -1; + + c->data = dp; + c->d_max = dn; + } + + memcpy((char *) c->data + c->d_cur, data, size); + + c->d_cur = ns; + + return 0; +} + +int mk_writeEbmlHeader(mk_Writer *w, const char *doctype, + uint64_t doctype_version, + uint64_t doctype_readversion) +{ + mk_Context *c = NULL; + + if ((c = mk_createContext(w, w->root, EBML_ID_HEADER)) == NULL) /* EBML */ + return -1; + CHECK(mk_writeUInt(c, EBML_ID_EBMLVERSION, EBML_VERSION)); /* EBMLVersion */ + CHECK(mk_writeUInt(c, EBML_ID_EBMLREADVERSION, EBML_VERSION)); /* EBMLReadVersion */ + CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXIDLENGTH, 4)); /* EBMLMaxIDLength */ + CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXSIZELENGTH, 8)); /* EBMLMaxSizeLength */ + CHECK(mk_writeStr(c, EBML_ID_DOCTYPE, doctype)); /* DocType */ + CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEVERSION, doctype_version)); /* DocTypeVersion */ + /* DocTypeReadversion */ + CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEREADVERSION, doctype_readversion)); + CHECK(mk_closeContext(c, 0)); + + return 0; +} + +int mk_writeID(mk_Context *c, unsigned id) +{ + unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id }; + + if (c_id[0]) + return mk_appendContextData(c, c_id, 4); + if (c_id[1]) + return mk_appendContextData(c, c_id + 1, 3); + if (c_id[2]) + return mk_appendContextData(c, c_id + 2, 2); + return mk_appendContextData(c, c_id + 3, 1); +} + +int mk_writeSize(mk_Context *c, uint64_t size) +{ + unsigned char c_size[8] = { 0x01, size >> 48, size >> 40, size >> 32, + size >> 24, size >> 16, size >> 8, size }; + + if (size < 0x7fll) { + c_size[7] |= 0x80; + return mk_appendContextData(c, c_size + 7, 1); + } + if (size < 0x3fffll) { + c_size[6] |= 0x40; + return mk_appendContextData(c, c_size + 6, 2); + } + if (size < 0x1fffffll) { + c_size[5] |= 0x20; + return mk_appendContextData(c, c_size + 5, 3); + } + if (size < 0x0fffffffll) { + c_size[4] |= 0x10; + return mk_appendContextData(c, c_size + 4, 4); + } + if (size < 0x07ffffffffll) { + c_size[3] |= 0x08; + return mk_appendContextData(c, c_size + 3, 5); + } + if (size < 0x03ffffffffffll) { + c_size[2] |= 0x04; + return mk_appendContextData(c, c_size + 2, 6); + } + if (size < 0x01ffffffffffffll) { + c_size[1] |= 0x02; + return mk_appendContextData(c, c_size + 1, 7); + } + return mk_appendContextData(c, c_size, 8); +} + +int mk_writeSSize(mk_Context *c, int64_t size) +{ + uint64_t u_size = (uint64_t) llabs(size); + /* Need to shift by one below so ebmlSizeSize returns the correct size. */ + unsigned size_size = mk_ebmlSizeSize(u_size << 1); + + switch (size_size) { + case 1: + size += 0x3fll; + break; + case 2: + size += 0x1fffll; + break; + case 3: + size += 0x0fffffll; + break; + case 4: + size += 0x07ffffffll; + break; + case 5: + size += 0x03ffffffffll; + break; + case 6: + size += 0x01ffffffffffll; + break; + case 7: + size += 0x00ffffffffffffll; + break; + default: /* Matroska currently doesn't support any int > 56-bit. */ + return -1; + } + + return mk_writeSize(c, size); +} + +int mk_flushContextID(mk_Context *c) +{ + unsigned char size_undf[8] = { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (c->id == 0) + return 0; + + CHECK(mk_writeID(c->parent, c->id)); + CHECK(mk_appendContextData(c->parent, &size_undf, 8)); + + c->id = 0; + + return 0; +} + +int mk_flushContextData(mk_Context *c) +{ + mk_Writer *w = c->owner; + + if (c->d_cur == 0) + return 0; + + if (c->parent) { + CHECK(mk_appendContextData(c->parent, c->data, c->d_cur)); + } else { + if (fwrite(c->data, c->d_cur, 1, w->fp) != 1) + return -1; + w->f_pos += c->d_cur; + if (w->f_pos > w->f_eof) + w->f_eof = w->f_pos; + } + + c->d_cur = 0; + + return 0; +} + +int mk_closeContext(mk_Context *c, int64_t *off) +{ + if (c->id) { + CHECK(mk_writeID(c->parent, c->id)); + CHECK(mk_writeSize(c->parent, c->d_cur)); + } + + if (c->parent && off != NULL) + *off += c->parent->d_cur; + + CHECK(mk_flushContextData(c)); + + if (c->next) + c->next->prev = c->prev; + *(c->prev) = c->next; + c->next = c->owner->freelist; + c->owner->freelist = c; + + return 0; +} + +void mk_destroyContexts(mk_Writer *w) +{ + mk_Context *cur, *next; + + for (cur = w->freelist; cur; cur = next) { + next = cur->next; + free(cur->data); + free(cur); + } + + for (cur = w->actlist; cur; cur = next) { + next = cur->next; + free(cur->data); + free(cur); + } + + w->freelist = w->actlist = w->root = NULL; +} + +int mk_writeStr(mk_Context *c, unsigned id, const char *str) +{ + size_t len = strlen(str); + + CHECK(mk_writeID(c, id)); + CHECK(mk_writeSize(c, len)); + CHECK(mk_appendContextData(c, str, len)); + return 0; +} + +int mk_writeBin(mk_Context *c, unsigned id, const void *data, + unsigned size) +{ + CHECK(mk_writeID(c, id)); + CHECK(mk_writeSize(c, size)); + CHECK(mk_appendContextData(c, data, size)); + return 0; +} + +int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui) +{ + unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, + ui >> 24, ui >> 16, ui >> 8, ui }; + unsigned i = 0; + + CHECK(mk_writeID(c, id)); + while (i < 7 && c_ui[i] == 0) + ++i; + CHECK(mk_writeSize(c, 8 - i)); + CHECK(mk_appendContextData(c, c_ui + i, 8 - i)); + return 0; +} + +int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) +{ + unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, + si >> 24, si >> 16, si >> 8, si }; + unsigned i = 0; + + CHECK(mk_writeID(c, id)); + if (si < 0) { + while (i < 7 && c_si[i] == 0xff && c_si[i + 1] & 0x80) + ++i; + } else { + while (i < 7 && c_si[i] == 0 && !(c_si[i + 1] & 0x80)) + ++i; + } + CHECK(mk_writeSize(c, 8 - i)); + CHECK(mk_appendContextData(c, c_si + i, 8 - i)); + return 0; +} + +int mk_writeFloatRaw(mk_Context *c, float f) +{ + union { + float f; + unsigned u; + } u; + unsigned char c_f[4]; + + u.f = f; + c_f[0] = u.u >> 24; + c_f[1] = u.u >> 16; + c_f[2] = u.u >> 8; + c_f[3] = u.u; + + return mk_appendContextData(c, c_f, 4); +} + +int mk_writeFloat(mk_Context *c, unsigned id, float f) +{ + CHECK(mk_writeID(c, id)); + CHECK(mk_writeSize(c, 4)); + CHECK(mk_writeFloatRaw(c, f)); + return 0; +} + +int mk_writeVoid(mk_Context *c, uint64_t length) +{ + /* It would probably be faster to do this on the stack. */ + char *c_void = calloc(length, sizeof(*c_void)); + + CHECK(mk_writeID(c, EBML_ID_VOID)); + CHECK(mk_writeSize(c, length)); + CHECK(mk_appendContextData(c, c_void, length)); + free(c_void); + + return 0; +} + +unsigned mk_ebmlSizeSize(uint64_t s) +{ + if (s < 0x7fll) + return 1; + if (s < 0x3fffll) + return 2; + if (s < 0x1fffffll) + return 3; + if (s < 0x0fffffffll) + return 4; + if (s < 0x07ffffffffll) + return 5; + if (s < 0x03ffffffffffll) + return 6; + if (s < 0x01ffffffffffffll) + return 7; + return 8; +} + +unsigned mk_ebmlSIntSize(int64_t si) +{ + unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, + si >> 24, si >> 16, si >> 8, si }; + unsigned i = 0; + + if (si < 0) { + while (i < 7 && c_si[i] == 0xff && c_si[i + 1] & 0x80) + ++i; + } else { + while (i < 7 && c_si[i] == 0 && !(c_si[i + 1] & 0x80)) + ++i; + } + + return 8 - i; +} diff --git a/src/ebml.h b/src/ebml.h index 624384c..3270184 100644 --- a/src/ebml.h +++ b/src/ebml.h @@ -2,7 +2,6 @@ * ebml.h: ***************************************************************************** * Copyright (C) 2005 x264 project - * $Id: $ * * Authors: Mike Matsnev * Nathan Caldwell @@ -29,55 +28,59 @@ #define EBML_VERSION 1 /* top-level master-IDs */ -#define EBML_ID_HEADER 0x1A45DFA3 /* sub-elements */ +#define EBML_ID_HEADER 0x1A45DFA3 /* sub-elements */ /* IDs in the HEADER master */ -#define EBML_ID_EBMLVERSION 0x4286 /* u-integer */ -#define EBML_ID_EBMLREADVERSION 0x42F7 /* u-integer */ -#define EBML_ID_EBMLMAXIDLENGTH 0x42F2 /* u-integer */ -#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3 /* u-integer */ -#define EBML_ID_DOCTYPE 0x4282 /* string */ -#define EBML_ID_DOCTYPEVERSION 0x4287 /* u-integer */ -#define EBML_ID_DOCTYPEREADVERSION 0x4285 /* u-integer */ +#define EBML_ID_EBMLVERSION 0x4286 /* u-integer */ +#define EBML_ID_EBMLREADVERSION 0x42F7 /* u-integer */ +#define EBML_ID_EBMLMAXIDLENGTH 0x42F2 /* u-integer */ +#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3 /* u-integer */ +#define EBML_ID_DOCTYPE 0x4282 /* string */ +#define EBML_ID_DOCTYPEVERSION 0x4287 /* u-integer */ +#define EBML_ID_DOCTYPEREADVERSION 0x4285 /* u-integer */ /* general EBML types */ -#define EBML_ID_VOID 0xEC /* binary */ +#define EBML_ID_VOID 0xEC /* binary */ /* Above copied from ffmpeg */ typedef struct mk_Context_s mk_Context; struct mk_Context_s { - mk_Context *next, **prev, *parent; - mk_Writer *owner; - unsigned id; + mk_Context *next, **prev, *parent; + mk_Writer *owner; + unsigned id; - void *data; - unsigned d_cur, d_max; + void *data; + unsigned d_cur, d_max; }; /* Contexts */ -mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id); -int mk_appendContextData(mk_Context *c, const void *data, uint64_t size); -int mk_flushContextID(mk_Context *c); -int mk_flushContextData(mk_Context *c); -int mk_closeContext(mk_Context *c, int64_t *off); -void mk_destroyContexts(mk_Writer *w); +mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, + unsigned id); +int mk_appendContextData(mk_Context *c, const void *data, uint64_t size); +int mk_flushContextID(mk_Context *c); +int mk_flushContextData(mk_Context *c); +int mk_closeContext(mk_Context *c, int64_t *off); +void mk_destroyContexts(mk_Writer *w); /* Contexts */ /* EBML */ -int mk_writeID(mk_Context *c, unsigned id); -int mk_writeSize(mk_Context *c, uint64_t size); -int mk_writeSSize(mk_Context *c, int64_t size); -int mk_writeStr(mk_Context *c, unsigned id, const char *str); -int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size); -int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui); -int mk_writeSInt(mk_Context *c, unsigned id, int64_t si); -int mk_writeFloatRaw(mk_Context *c, float f); -int mk_writeFloat(mk_Context *c, unsigned id, float f); -int mk_writeVoid(mk_Context *c, uint64_t length); -unsigned mk_ebmlSizeSize(uint64_t s); -unsigned mk_ebmlSIntSize(int64_t si); -int mk_writeEbmlHeader(mk_Writer *w, const char * doctype, uint64_t doctype_version, uint64_t doctype_readversion ); +int mk_writeID(mk_Context *c, unsigned id); +int mk_writeSize(mk_Context *c, uint64_t size); +int mk_writeSSize(mk_Context *c, int64_t size); +int mk_writeStr(mk_Context *c, unsigned id, const char *str); +int mk_writeBin(mk_Context *c, unsigned id, const void *data, + unsigned size); +int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui); +int mk_writeSInt(mk_Context *c, unsigned id, int64_t si); +int mk_writeFloatRaw(mk_Context *c, float f); +int mk_writeFloat(mk_Context *c, unsigned id, float f); +int mk_writeVoid(mk_Context *c, uint64_t length); +unsigned mk_ebmlSizeSize(uint64_t s); +unsigned mk_ebmlSIntSize(int64_t si); +int mk_writeEbmlHeader(mk_Writer *w, const char *doctype, + uint64_t doctype_version, + uint64_t doctype_readversion); /* EBML */ -#endif /* _EBML_H */ +#endif /* _EBML_H */ diff --git a/src/matroska.c b/src/matroska.c dissimilarity index 90% index 221e176..1234461 100644 --- a/src/matroska.c +++ b/src/matroska.c @@ -1,530 +1,575 @@ -/***************************************************************************** - * matroska.c: - ***************************************************************************** - * Copyright (C) 2005 x264 project - * $Id: $ - * - * Authors: Mike Matsnev - * Nathan Caldwell - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ -#include "config.h" -#include "libmkv.h" -#include "matroska.h" -#include "md5.h" - -#define RESERVED_SEEKHEAD 0x100 -/* 256 bytes should be enough room for our Seek entries. */ -#define RESERVED_CHAPTERS 0x800 -/* 2048 bytes, hopefully enough for Chapters. */ - -int mk_seekFile(mk_Writer *w, uint64_t pos) { - if (fseek(w->fp, pos, SEEK_SET)) - return -1; - - w->f_pos = pos; - - if (pos > w->f_eof) - w->f_eof = pos; - - return 0; -} - -char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, uint64_t *output_size) { - unsigned i, j; - uint64_t offset = 0; - uint64_t alloc_size = num_frames * 6; // Complete guess. We'll realloc if we need more space, though. - char *laced = calloc(alloc_size, sizeof(char)); - if (laced == NULL) - return NULL; - - laced[offset++] = num_frames; - for (i = 0; i < num_frames; i++) - { - for (j = sizes[i]; j >= 255 ; j -= 255) - { - laced[offset++] = 255; - if (offset + 1 >= alloc_size) { - int avg_sz = offset / (i - 1); // Compute approximate average bytes/frame - alloc_size += avg_sz * (num_frames - i); // Add our average + number of frames left to size - if ((laced = realloc(laced, alloc_size)) == NULL) - return NULL; - } - } - laced[offset++] = j; - } - - if (output_size != NULL) - *output_size = offset; - - return laced; -} - -mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat) { - mk_Writer *w = calloc(1, sizeof(*w)); - if (w == NULL) - return NULL; - - w->root = mk_createContext(w, NULL, 0); - if (w->root == NULL) { - free(w); - return NULL; - } - - if ((w->cues = mk_createContext(w, w->root, MATROSKA_ID_CUES)) == NULL) // Cues - { - mk_destroyContexts(w); - free(w); - return NULL; - } - - if (vlc_compat) { - if ((w->cluster.seekhead = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) // SeekHead - { - mk_destroyContexts(w); - free(w); - return NULL; - } - } - - w->fp = fopen(filename, "wb"); - if (w->fp == NULL) { - mk_destroyContexts(w); - free(w); - return NULL; - } - - w->timescale = timescale; - w->vlc_compat = vlc_compat; - - return w; -} - -int mk_writeHeader(mk_Writer *w, const char *writingApp) { - mk_Context *c; - int64_t offset = 0; - - if (w->wrote_header) - return -1; - - md5_starts(&w->segment_md5); /* Initalize MD5 */ - - CHECK(mk_writeEbmlHeader(w, "matroska", MATROSKA_VERSION, MATROSKA_VERSION)); - - if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEGMENT)) == NULL) // Segment - return -1; - CHECK(mk_flushContextID(c)); - w->segment_ptr = c->d_cur; - CHECK(mk_closeContext(c, &w->segment_ptr)); - - if (w->vlc_compat) { - CHECK(mk_writeVoid(w->root, RESERVED_SEEKHEAD)); /* Reserved space for SeekHead */ - CHECK(mk_writeVoid(w->root, RESERVED_CHAPTERS)); /* Reserved space for Chapters */ - } - else { - w->seek_data.seekhead = 0x80000000; - CHECK(mk_writeSeekHead(w, &w->seekhead_ptr)); - w->seek_data.seekhead = 0; - } - - if ((c = mk_createContext(w, w->root, MATROSKA_ID_INFO)) == NULL) // SegmentInfo - return -1; - w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr; - CHECK(mk_writeVoid(c, 16)); /* Reserve space for a SegmentUID, write it later. */ - CHECK(mk_writeStr(c, MATROSKA_ID_MUXINGAPP, PACKAGE_STRING)); // MuxingApp - CHECK(mk_writeStr(c, MATROSKA_ID_WRITINGAPP, writingApp)); // WritingApp - CHECK(mk_writeUInt(c, MATROSKA_ID_TIMECODESCALE, w->timescale)); // TimecodeScale - CHECK(mk_writeFloat(c, MATROSKA_ID_DURATION, 0)); // Duration - w->duration_ptr = c->d_cur - 4; - CHECK(mk_closeContext(c, &offset)); - w->duration_ptr += offset; - w->segmentuid_ptr = offset; - - w->seek_data.tracks = w->root->d_cur - w->segment_ptr; - - if (w->tracks) - CHECK(mk_closeContext(w->tracks, 0)); - - CHECK(mk_flushContextData(w->root)); - - w->wrote_header = 1; - w->def_duration = w->tracks_arr[0]->default_duration; - return 0; -} - -static int mk_closeCluster(mk_Writer *w) { - if (w->cluster.context == NULL) - return 0; - w->cluster.count++; - CHECK(mk_closeContext(w->cluster.context, 0)); - w->cluster.context = NULL; - CHECK(mk_flushContextData(w->root)); - return 0; -} - -int mk_flushFrame(mk_Writer *w, mk_Track *track) { - mk_Context *c, *tp; - int64_t delta, ref = 0; - unsigned fsize, bgsize; - uint8_t flags, c_delta[2]; - int i; - char *laced = NULL; - uint64_t length = 0; - - if (!track->in_frame) - return 0; - - delta = track->frame.timecode/w->timescale - w->cluster.tc_scaled; - if (delta > 2000ll || delta < -2000ll) - CHECK(mk_closeCluster(w)); - - if (w->cluster.context == NULL) { - w->cluster.tc_scaled = track->frame.timecode / w->timescale; - w->cluster.context = mk_createContext(w, w->root, MATROSKA_ID_CLUSTER); // Cluster - if (w->cluster.context == NULL) - return -1; - - w->cluster.pointer = w->f_pos - w->segment_ptr; - - if (w->vlc_compat) - CHECK(mk_writeSeek(w, w->cluster.seekhead, MATROSKA_ID_CLUSTER, w->cluster.pointer)); - - CHECK(mk_writeUInt(w->cluster.context, MATROSKA_ID_CLUSTERTIMECODE, w->cluster.tc_scaled)); // Cluster Timecode - - delta = 0; - w->cluster.block_count = 0; - } - - /* Calculate the encoded lacing sizes. */ - switch (track->frame.lacing) { - case MK_LACING_XIPH: - laced = mk_laceXiph(track->frame.lacing_sizes, track->frame.lacing_num_frames, &length); - break; - case MK_LACING_EBML: - { - uint64_t u_size = 0; - length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1; // Add one for the frame count. - for (i = 1; i < track->frame.lacing_num_frames; i++) - { - u_size = llabs(track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1]); - length += mk_ebmlSizeSize((u_size) << 1); // Shift by one so we get the right size for a signed number. - } - break; - } - case MK_LACING_FIXED: - { - laced = calloc(1, sizeof(char)); - laced[0] = track->frame.lacing_num_frames; - ++length; - break; - } - default: - break; - } - - fsize = track->frame.data ? track->frame.data->d_cur : 0; - bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4 + length) + 1 + length; - if (!track->frame.keyframe) { - ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta; - bgsize += 1 + 1 + mk_ebmlSIntSize(ref); - } - - CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCKGROUP)); // BlockGroup - CHECK(mk_writeSize(w->cluster.context, bgsize)); - CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCK)); // Block - CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length)); // Block size - CHECK(mk_writeSize(w->cluster.context, track->track_id)); // track number - - w->cluster.block_count++; - - c_delta[0] = delta >> 8; - c_delta[1] = delta; - CHECK(mk_appendContextData(w->cluster.context, c_delta, 2)); // Timecode relative to Cluster. - -// flags = ( track->frame.keyframe << 8 ) | track->frame.lacing; - flags = track->frame.lacing << 1; // Flags: Bit 5-6 describe what type of lacing to use. - CHECK(mk_appendContextData(w->cluster.context, &flags, 1)); - if (track->frame.lacing) { - if (track->frame.lacing == MK_LACING_EBML) { - CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1)); // Number of frames in lace-1 - CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0])); // Size of 1st frame. - for (i = 1; i < track->frame.lacing_num_frames; i++) - { - CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1])); // Size difference between previous size and this size. - } - } else if (length > 0 && laced != NULL) { - CHECK(mk_appendContextData(w->cluster.context, laced, length)); - free(laced); - laced = NULL; - } - } - - if (track->frame.data) { - CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, track->frame.data->d_cur)); - track->frame.data->d_cur = 0; - } - if (!track->frame.keyframe) - CHECK(mk_writeSInt(w->cluster.context, MATROSKA_ID_REFERENCEBLOCK, ref)); // ReferenceBlock - - /* This may get a little out of hand, but it seems sane enough for now. */ - if (track->frame.keyframe && (track->track_type == MK_TRACK_VIDEO)) { -// if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && ((track->prev_cue_pos + 3*CLSIZE) <= w->f_pos || track->frame.timecode == 0)) { - if ((c = mk_createContext(w, w->cues, MATROSKA_ID_CUEPOINT)) == NULL) // CuePoint - return -1; - CHECK(mk_writeUInt(c, MATROSKA_ID_CUETIME, (track->frame.timecode / w->timescale))); // CueTime - - if ((tp = mk_createContext(w, c, MATROSKA_ID_CUETRACKPOSITIONS)) == NULL) // CueTrackPositions - return -1; - CHECK(mk_writeUInt(tp, MATROSKA_ID_CUETRACK, track->track_id)); // CueTrack - CHECK(mk_writeUInt(tp, MATROSKA_ID_CUECLUSTERPOSITION, w->cluster.pointer)); // CueClusterPosition -// CHECK(mk_writeUInt(c, MATROSKA_ID_CUEBLOCKNUMBER, w->cluster.block_count)); // CueBlockNumber - CHECK(mk_closeContext(tp, 0)); - CHECK(mk_closeContext(c, 0)); - track->prev_cue_pos = w->f_pos; - } - - track->in_frame = 0; - track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta; - - if (w->cluster.context->d_cur > CLSIZE) - CHECK(mk_closeCluster(w)); - - return 0; -} - -int mk_startFrame(mk_Writer *w, mk_Track *track) { - if (mk_flushFrame(w, track) < 0) - return -1; - - track->in_frame = 1; - track->frame.keyframe = 0; - track->frame.lacing = MK_LACING_NONE; - track->frame.lacing_num_frames = 0; - track->frame.lacing_sizes = NULL; - - return 0; -} - -int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe) { - if (!track->in_frame) - return -1; - - track->frame.timecode = timestamp; - track->frame.keyframe = keyframe != 0; - - if (track->max_frame_tc < timestamp) - track->max_frame_tc = timestamp; - - return 0; -} - -int mk_setFrameLacing(mk_Writer *w, mk_Track *track, mk_LacingType lacing, uint8_t num_frames, uint64_t sizes[]) { - if (!track->in_frame) - return -1; - track->frame.lacing_sizes = calloc(num_frames, sizeof(uint64_t)); - - track->frame.lacing = lacing; - track->frame.lacing_num_frames = num_frames; - memcpy(track->frame.lacing_sizes, sizes, num_frames * sizeof(uint64_t)); - - return 0; -} - -int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size) { - if (!track->in_frame) - return -1; - - if (track->frame.data == NULL) - if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL) - return -1; - - md5_update(&w->segment_md5, (unsigned char *)data, size); - - return mk_appendContextData(track->frame.data, data, size); -} - -int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, uint64_t seek_pos) { - mk_Context *s; - - if ((s = mk_createContext(w, c, MATROSKA_ID_SEEKENTRY)) == NULL) // Seek - return -1; - CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKID, seek_id)); // SeekID - CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKPOSITION, seek_pos)); // SeekPosition - CHECK(mk_closeContext(s, 0)); - - return 0; -} - -/* The offset of the SeekHead is returned in *pointer. */ -int mk_writeSeekHead(mk_Writer *w, int64_t *pointer) { - mk_Context *c; - int64_t seekhead_ptr; - - if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) // SeekHead - return -1; - if (pointer != NULL) - seekhead_ptr = w->f_pos; - if (w->seek_data.seekhead) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_SEEKHEAD, w->seek_data.seekhead)); - if (w->seek_data.segmentinfo) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_INFO, w->seek_data.segmentinfo)); - if (w->seek_data.tracks) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_TRACKS, w->seek_data.tracks)); - if (w->seek_data.cues) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_CUES, w->seek_data.cues)); - if (w->seek_data.attachments) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_ATTACHMENTS, w->seek_data.attachments)); - if (w->seek_data.chapters) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_CHAPTERS, w->seek_data.chapters)); - if (w->seek_data.tags) - CHECK(mk_writeSeek(w, c, MATROSKA_ID_TAGS, w->seek_data.tags)); - CHECK(mk_closeContext(c, 0)); - - if (pointer != NULL) - *pointer = seekhead_ptr; - - return 0; -} - -int mk_close(mk_Writer *w) { - int i, ret = 0; - mk_Track *tk; - int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc; - uint64_t segment_size = 0; - unsigned char c_size[8]; - unsigned char segment_uid[16]; - - md5_finish(&w->segment_md5, segment_uid); - - for (i = w->num_tracks - 1; i >= 0; i--) - { - tk = w->tracks_arr[i]; - w->tracks_arr[i] = NULL; - --w->num_tracks; - if (mk_flushFrame(w, tk) < 0) - ret = -1; - free(tk); - tk = NULL; - } - - if (mk_closeCluster(w) < 0) - ret = -1; - - w->seek_data.cues = w->f_pos - w->segment_ptr; - if (mk_closeContext(w->cues, 0) < 0) - ret = -1; - if (mk_flushContextData(w->root) < 0) - ret = -1; - - if (w->vlc_compat && w->cluster.seekhead) { - w->seek_data.seekhead = w->f_pos - w->segment_ptr; - if (mk_closeContext(w->cluster.seekhead, 0) < 0) - ret = -1; - if (mk_flushContextData(w->root) < 0) - ret = -1; - } - - if (w->chapters != NULL) - { - if (w->vlc_compat) { - if (mk_flushContextData(w->root) < 0) - ret = -1; - if (mk_seekFile(w, w->segment_ptr + 0x100 + 3) < 0) - ret = -1; - } - w->seek_data.chapters = w->f_pos - w->segment_ptr; - mk_writeChapters(w); - if (mk_flushContextData(w->root) < 0) - ret = -1; - if (w->vlc_compat) { - if (mk_writeVoid(w->root, (0x800 - (w->f_pos - w->segment_ptr - 0x100 - 3))) < 0) - ret = -1; - if (mk_flushContextData(w->root) < 0) - ret = -1; - } - } - - if (w->wrote_header) { - if (w->vlc_compat) { - if (mk_seekFile(w, w->segment_ptr) < 0) - ret = -1; - } - - if (mk_writeSeekHead(w, &w->seek_data.seekhead) < 0) - ret = -1; - w->seek_data.seekhead -= w->segment_ptr; - - if (w->vlc_compat) - { - if (mk_flushContextData(w->root) < 0) - ret = -1; - if (mk_writeVoid(w->root, (0x100 - (w->f_pos - w->segment_ptr))) < 0) - ret = -1; - } - - if (mk_flushContextData(w->root) < 0) - ret = -1; - - if (!w->vlc_compat) - { - int i = w->seek_data.segmentinfo; - w->seek_data.segmentinfo = 0; - w->seek_data.tracks = 0; - w->seek_data.cues = 0; - w->seek_data.chapters = 0; - w->seek_data.attachments = 0; - w->seek_data.tags = 0; - if (mk_seekFile(w, w->segment_ptr) < 0) - ret = -1; - if (mk_writeSeekHead(w, NULL) < 0 || - mk_flushContextData(w->root) < 0) - ret = -1; - if (((i + w->segment_ptr) - w->f_pos - 2) > 1) - if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0 || - mk_flushContextData(w->root) < 0) - ret = -1; - } - - if (mk_seekFile(w, w->duration_ptr) < 0) - ret = -1; - if (mk_writeFloatRaw(w->root, (float)((double)(max_frame_tc+w->def_duration) / w->timescale)) < 0 || - mk_flushContextData(w->root) < 0) - ret = -1; - if (mk_seekFile(w, w->segment_ptr - 8) < 0) - ret = -1; - segment_size = w->f_eof - w->segment_ptr; - for (i = 7; i > 0; --i) - c_size[i] = segment_size >> (8 * (7-i)); - c_size[i] = 0x01; - if (mk_appendContextData(w->root, &c_size, 8) < 0 || - mk_flushContextData(w->root) < 0) - ret = -1; - if (mk_seekFile(w, w->segmentuid_ptr) < 0) - ret = -1; - if (mk_writeBin(w->root, MATROSKA_ID_SEGMENTUID, segment_uid, sizeof(segment_uid)) < 0 || - mk_flushContextData(w->root) < 0) - ret = -1; - } - - if (mk_closeContext(w->root, 0) < 0) - ret = -1; - mk_destroyContexts(w); - fclose(w->fp); - free(w->tracks_arr); - free(w); - - return ret; -} +/***************************************************************************** + * matroska.c: + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Authors: Mike Matsnev + * Nathan Caldwell + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ +#include "config.h" +#include "libmkv.h" +#include "matroska.h" +#include "md5.h" + +#define RESERVED_SEEKHEAD 0x100 +/* 256 bytes should be enough room for our Seek entries. */ +#define RESERVED_CHAPTERS 0x800 +/* 2048 bytes, hopefully enough for Chapters. */ + +int mk_seekFile(mk_Writer *w, uint64_t pos) +{ + if (fseek(w->fp, pos, SEEK_SET)) + return -1; + + w->f_pos = pos; + + if (pos > w->f_eof) + w->f_eof = pos; + + return 0; +} + +char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, + uint64_t *output_size) +{ + unsigned i, j; + uint64_t offset = 0; + uint64_t alloc_size = num_frames * 6; /* Complete guess. It gets realloc'd + * below if we need more space, though. + */ + char *laced = calloc(alloc_size, sizeof(*laced)); + if (laced == NULL) + return NULL; + + laced[offset++] = num_frames; + for (i = 0; i < num_frames; i++) { + for (j = sizes[i]; j >= 255; j -= 255) { + laced[offset++] = 255; + if (offset + 1 >= alloc_size) { + int avg_sz = offset / (i - 1); /* Compute approximate average bytes/frame */ + alloc_size += avg_sz * (num_frames - i); + /* Sum average so far and number of frames left + * with alloc'd size + */ + if ((laced = realloc(laced, alloc_size)) == NULL) + return NULL; + } + } + laced[offset++] = j; + } + + if (output_size != NULL) + *output_size = offset; + + return laced; +} + +mk_Writer *mk_createWriter(const char *filename, int64_t timescale, + uint8_t vlc_compat) +{ + mk_Writer *w = calloc(1, sizeof(*w)); + if (w == NULL) + return NULL; + + w->root = mk_createContext(w, NULL, 0); + if (w->root == NULL) { + free(w); + return NULL; + } + + /* Cues */ + if ((w->cues = mk_createContext(w, w->root, MATROSKA_ID_CUES)) == NULL) + { + mk_destroyContexts(w); + free(w); + return NULL; + } + + if (vlc_compat) { + /* SeekHead */ + if ((w->cluster.seekhead = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) + { + mk_destroyContexts(w); + free(w); + return NULL; + } + } + + w->fp = fopen(filename, "wb"); + if (w->fp == NULL) { + mk_destroyContexts(w); + free(w); + return NULL; + } + + w->timescale = timescale; + w->vlc_compat = vlc_compat; + + return w; +} + +int mk_writeHeader(mk_Writer *w, const char *writingApp) +{ + mk_Context *c; + int64_t offset = 0; + + if (w->wrote_header) + return -1; + + md5_starts(&w->segment_md5); /* Initalize MD5 */ + + CHECK(mk_writeEbmlHeader(w, "matroska", MATROSKA_VERSION, MATROSKA_VERSION)); + + /* Segment */ + if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEGMENT)) == NULL) + return -1; + CHECK(mk_flushContextID(c)); + w->segment_ptr = c->d_cur; + CHECK(mk_closeContext(c, &w->segment_ptr)); + + if (w->vlc_compat) { + CHECK(mk_writeVoid(w->root, RESERVED_SEEKHEAD)); /* Reserved space for SeekHead */ + CHECK(mk_writeVoid(w->root, RESERVED_CHAPTERS)); /* Reserved space for Chapters */ + } else { + w->seek_data.seekhead = 0x80000000; + CHECK(mk_writeSeekHead(w, &w->seekhead_ptr)); + w->seek_data.seekhead = 0; + } + + if ((c = mk_createContext(w, w->root, MATROSKA_ID_INFO)) == NULL) /* SegmentInfo */ + return -1; + w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr; + CHECK(mk_writeVoid(c, 16)); /* Reserve space for a SegmentUID, to be written it later. */ + CHECK(mk_writeStr(c, MATROSKA_ID_MUXINGAPP, PACKAGE_STRING)); /* MuxingApp */ + CHECK(mk_writeStr(c, MATROSKA_ID_WRITINGAPP, writingApp)); /* WritingApp */ + CHECK(mk_writeUInt(c, MATROSKA_ID_TIMECODESCALE, w->timescale)); /* TimecodeScale */ + CHECK(mk_writeFloat(c, MATROSKA_ID_DURATION, 0)); /* Duration */ + w->duration_ptr = c->d_cur - 4; + CHECK(mk_closeContext(c, &offset)); + w->duration_ptr += offset; + w->segmentuid_ptr = offset; + + w->seek_data.tracks = w->root->d_cur - w->segment_ptr; + + if (w->tracks) + CHECK(mk_closeContext(w->tracks, 0)); + + CHECK(mk_flushContextData(w->root)); + + w->wrote_header = 1; + w->def_duration = w->tracks_arr[0]->default_duration; + return 0; +} + +static int mk_closeCluster(mk_Writer *w) +{ + if (w->cluster.context == NULL) + return 0; + w->cluster.count++; + CHECK(mk_closeContext(w->cluster.context, 0)); + w->cluster.context = NULL; + CHECK(mk_flushContextData(w->root)); + + return 0; +} + +int mk_flushFrame(mk_Writer *w, mk_Track *track) +{ + mk_Context *c, *tp; + int64_t delta, ref = 0; + unsigned fsize, bgsize; + uint8_t flags, c_delta[2]; + int i; + char *laced = NULL; + uint64_t length = 0; + + if (!track->in_frame) + return 0; + + delta = track->frame.timecode / w->timescale - w->cluster.tc_scaled; + if (delta > 2000ll || delta < -2000ll) + CHECK(mk_closeCluster(w)); + + if (w->cluster.context == NULL) { + w->cluster.tc_scaled = track->frame.timecode / w->timescale; + /* Cluster */ + w->cluster.context = mk_createContext(w, w->root, MATROSKA_ID_CLUSTER); + if (w->cluster.context == NULL) + return -1; + + w->cluster.pointer = w->f_pos - w->segment_ptr; + + if (w->vlc_compat) { + /* Cluster SeekEntry */ + CHECK(mk_writeSeek(w, w->cluster.seekhead, MATROSKA_ID_CLUSTER, + w->cluster.pointer)); + } + + /* Cluster Timecode */ + CHECK(mk_writeUInt(w->cluster.context, MATROSKA_ID_CLUSTERTIMECODE, w->cluster.tc_scaled)); + + delta = 0; + w->cluster.block_count = 0; + } + + /* Calculate the encoded lacing sizes. */ + switch (track->frame.lacing) { + case MK_LACING_XIPH: + laced = mk_laceXiph(track->frame.lacing_sizes, + track->frame.lacing_num_frames, &length); + break; + case MK_LACING_EBML: + { + uint64_t u_size = 0; + /* Add one below for the frame count. */ + length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1; + for (i = 1; i < track->frame.lacing_num_frames; i++) { + u_size = llabs(track->frame.lacing_sizes[i] - + track->frame.lacing_sizes[i - 1]); + /* Shift by one so we get the right size for a signed number. */ + length += mk_ebmlSizeSize((u_size) << 1); + } + break; + } + case MK_LACING_FIXED: + { + laced = calloc(1, sizeof(*laced)); + laced[0] = track->frame.lacing_num_frames; + ++length; + break; + } + default: + break; + } + + fsize = track->frame.data ? track->frame.data->d_cur : 0; + bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4 + length) + 1 + length; + if (!track->frame.keyframe) { + ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta; + bgsize += 1 + 1 + mk_ebmlSIntSize(ref); + } + + CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCKGROUP)); /* BlockGroup */ + CHECK(mk_writeSize(w->cluster.context, bgsize)); + CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCK)); /* Block */ + CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length)); /* BlockSize */ + CHECK(mk_writeSize(w->cluster.context, track->track_id)); /* track number */ + + w->cluster.block_count++; + + c_delta[0] = delta >> 8; + c_delta[1] = delta; + /* Timecode relative to Cluster. */ + CHECK(mk_appendContextData(w->cluster.context, c_delta, 2)); + +/* flags = ( track->frame.keyframe << 8 ) | track->frame.lacing; */ + flags = track->frame.lacing << 1; /* Flags: Bit 5-6 describe what type of lacing to use. */ + CHECK(mk_appendContextData(w->cluster.context, &flags, 1)); + if (track->frame.lacing) { + if (track->frame.lacing == MK_LACING_EBML) { + /* Number of frames in lace - 1 */ + CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1)); + /* Size of 1st frame. */ + CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0])); + for (i = 1; i < track->frame.lacing_num_frames; i++) { + /* Size difference between previous size and this size. */ + CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i - 1])); + } + } else if (length > 0 && laced != NULL) { + CHECK(mk_appendContextData(w->cluster.context, laced, length)); + free(laced); + laced = NULL; + } + } + + if (track->frame.data) { + CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, + track->frame.data->d_cur)); + track->frame.data->d_cur = 0; + } + if (!track->frame.keyframe) + /* ReferenceBlock */ + CHECK(mk_writeSInt(w->cluster.context, MATROSKA_ID_REFERENCEBLOCK, ref)); + + /* This may get a little out of hand, but it seems sane enough for now. */ + if (track->frame.keyframe && (track->track_type == MK_TRACK_VIDEO)) { +/* if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && ((track->prev_cue_pos + 3*CLSIZE) <= w->f_pos || track->frame.timecode == 0)) { */ + /* CuePoint */ + if ((c = mk_createContext(w, w->cues, MATROSKA_ID_CUEPOINT)) == NULL) + return -1; + /* CueTime */ + CHECK(mk_writeUInt(c, MATROSKA_ID_CUETIME, (track->frame.timecode / w->timescale))); + + /* CueTrackPositions */ + if ((tp = mk_createContext(w, c, MATROSKA_ID_CUETRACKPOSITIONS)) == NULL) + return -1; + /* CueTrack */ + CHECK(mk_writeUInt(tp, MATROSKA_ID_CUETRACK, track->track_id)); + /* CueClusterPosition */ + CHECK(mk_writeUInt(tp, MATROSKA_ID_CUECLUSTERPOSITION, w->cluster.pointer)); + /* CueBlockNumber */ +/* CHECK(mk_writeUInt(c, MATROSKA_ID_CUEBLOCKNUMBER, w->cluster.block_count)); */ + CHECK(mk_closeContext(tp, 0)); + CHECK(mk_closeContext(c, 0)); + track->prev_cue_pos = w->f_pos; + } + + track->in_frame = 0; + track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta; + + if (w->cluster.context->d_cur > CLSIZE) + CHECK(mk_closeCluster(w)); + + return 0; +} + +int mk_startFrame(mk_Writer *w, mk_Track *track) +{ + if (mk_flushFrame(w, track) < 0) + return -1; + + track->in_frame = 1; + track->frame.keyframe = 0; + track->frame.lacing = MK_LACING_NONE; + track->frame.lacing_num_frames = 0; + track->frame.lacing_sizes = NULL; + + return 0; +} + +int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, + unsigned keyframe) +{ + if (!track->in_frame) + return -1; + + track->frame.timecode = timestamp; + track->frame.keyframe = keyframe != 0; + + if (track->max_frame_tc < timestamp) + track->max_frame_tc = timestamp; + + return 0; +} + +int mk_setFrameLacing(mk_Writer *w, mk_Track *track, + mk_LacingType lacing, uint8_t num_frames, + uint64_t sizes[]) +{ + if (!track->in_frame) + return -1; + track->frame.lacing_sizes = calloc(num_frames, sizeof(*sizes)); + + track->frame.lacing = lacing; + track->frame.lacing_num_frames = num_frames; + memcpy(track->frame.lacing_sizes, sizes, + num_frames * sizeof(*sizes)); + + return 0; +} + +int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, + unsigned size) +{ + if (!track->in_frame) + return -1; + + if (track->frame.data == NULL) { + if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL) + return -1; + } + + md5_update(&w->segment_md5, (unsigned char *) data, size); + + return mk_appendContextData(track->frame.data, data, size); +} + +int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, + uint64_t seek_pos) +{ + mk_Context *s; + + if ((s = mk_createContext(w, c, MATROSKA_ID_SEEKENTRY)) == NULL) /* Seek */ + return -1; + CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKID, seek_id)); /* SeekID */ + CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKPOSITION, seek_pos)); /* SeekPosition */ + CHECK(mk_closeContext(s, 0)); + + return 0; +} + +/* The offset of the SeekHead is returned in pointer. */ +int mk_writeSeekHead(mk_Writer *w, int64_t *pointer) +{ + mk_Context *c; + int64_t seekhead_ptr; + + /* SeekHead */ + if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) + return -1; + if (pointer != NULL) + seekhead_ptr = w->f_pos; + if (w->seek_data.seekhead) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_SEEKHEAD, w->seek_data.seekhead)); + if (w->seek_data.segmentinfo) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_INFO, w->seek_data.segmentinfo)); + if (w->seek_data.tracks) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_TRACKS, w->seek_data.tracks)); + if (w->seek_data.cues) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_CUES, w->seek_data.cues)); + if (w->seek_data.attachments) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_ATTACHMENTS, w->seek_data.attachments)); + if (w->seek_data.chapters) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_CHAPTERS, w->seek_data.chapters)); + if (w->seek_data.tags) + CHECK(mk_writeSeek(w, c, MATROSKA_ID_TAGS, w->seek_data.tags)); + CHECK(mk_closeContext(c, 0)); + + if (pointer != NULL) + *pointer = seekhead_ptr; + + return 0; +} + +int mk_close(mk_Writer *w) +{ + int i, ret = 0; + mk_Track *tk; + int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc; + uint64_t segment_size = 0; + unsigned char c_size[8]; + unsigned char segment_uid[16]; + + md5_finish(&w->segment_md5, segment_uid); + + for (i = w->num_tracks - 1; i >= 0; i--) { + tk = w->tracks_arr[i]; + w->tracks_arr[i] = NULL; + --w->num_tracks; + if (mk_flushFrame(w, tk) < 0) + ret = -1; + free(tk); + tk = NULL; + } + + if (mk_closeCluster(w) < 0) + ret = -1; + + w->seek_data.cues = w->f_pos - w->segment_ptr; + if (mk_closeContext(w->cues, 0) < 0) + ret = -1; + if (mk_flushContextData(w->root) < 0) + ret = -1; + + if (w->vlc_compat && w->cluster.seekhead) { + w->seek_data.seekhead = w->f_pos - w->segment_ptr; + if (mk_closeContext(w->cluster.seekhead, 0) < 0) + ret = -1; + if (mk_flushContextData(w->root) < 0) + ret = -1; + } + + if (w->chapters != NULL) { + if (w->vlc_compat) { + if (mk_flushContextData(w->root) < 0) + ret = -1; + if (mk_seekFile(w, w->segment_ptr + 0x100 + 3) < 0) + ret = -1; + } + w->seek_data.chapters = w->f_pos - w->segment_ptr; + mk_writeChapters(w); + if (mk_flushContextData(w->root) < 0) + ret = -1; + if (w->vlc_compat) { + if (mk_writeVoid(w->root, (0x800 - (w->f_pos - w->segment_ptr - 0x100 - 3))) < 0) + ret = -1; + if (mk_flushContextData(w->root) < 0) + ret = -1; + } + } + + if (w->wrote_header) { + if (w->vlc_compat) { + if (mk_seekFile(w, w->segment_ptr) < 0) + ret = -1; + } + + if (mk_writeSeekHead(w, &w->seek_data.seekhead) < 0) + ret = -1; + w->seek_data.seekhead -= w->segment_ptr; + + if (w->vlc_compat) { + if (mk_flushContextData(w->root) < 0) + ret = -1; + if (mk_writeVoid(w->root, (0x100 - (w->f_pos - w->segment_ptr))) < 0) + ret = -1; + } + + if (mk_flushContextData(w->root) < 0) + ret = -1; + + if (!w->vlc_compat) { + int i = w->seek_data.segmentinfo; + w->seek_data.segmentinfo = 0; + w->seek_data.tracks = 0; + w->seek_data.cues = 0; + w->seek_data.chapters = 0; + w->seek_data.attachments = 0; + w->seek_data.tags = 0; + if (mk_seekFile(w, w->segment_ptr) < 0) + ret = -1; + if (mk_writeSeekHead(w, NULL) < 0 || mk_flushContextData(w->root) < 0) + ret = -1; + if (((i + w->segment_ptr) - w->f_pos - 2) > 1) + if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0 + || mk_flushContextData(w->root) < 0) + ret = -1; + } + + if (mk_seekFile(w, w->duration_ptr) < 0) + ret = -1; + if (mk_writeFloatRaw(w->root, + (float) ((double) (max_frame_tc + w->def_duration) / + w->timescale)) < 0 + || mk_flushContextData(w->root) < 0) + ret = -1; + if (mk_seekFile(w, w->segment_ptr - 8) < 0) + ret = -1; + segment_size = w->f_eof - w->segment_ptr; + for (i = 7; i > 0; --i) + c_size[i] = segment_size >> (8 * (7 - i)); + c_size[i] = 0x01; + if (mk_appendContextData(w->root, &c_size, 8) < 0 || + mk_flushContextData(w->root) < 0) + ret = -1; + if (mk_seekFile(w, w->segmentuid_ptr) < 0) + ret = -1; + if (mk_writeBin(w->root, MATROSKA_ID_SEGMENTUID, segment_uid, + sizeof(segment_uid)) < 0 || + mk_flushContextData(w->root) < 0) + ret = -1; + } + + if (mk_closeContext(w->root, 0) < 0) + ret = -1; + mk_destroyContexts(w); + fclose(w->fp); + free(w->tracks_arr); + free(w); + + return ret; +} diff --git a/src/matroska.h b/src/matroska.h dissimilarity index 78% index 164eaca..9619496 100644 --- a/src/matroska.h +++ b/src/matroska.h @@ -1,267 +1,267 @@ -/***************************************************************************** - * matroska.h: - ***************************************************************************** - * Copyright (C) 2005 x264 project - * $Id: $ - * - * Authors: Mike Matsnev - * Nathan Caldwell - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ -#ifndef _MATROSKA_H -#define _MATROSKA_H 1 - -#include "md5.h" -#include "ebml.h" - -#define CLSIZE 1048576 -#define CHECK(x) do { if ((x) < 0) return -1; } while (0) - -/* Matroska version supported */ -#define MATROSKA_VERSION 2 - -/* Copied from ffmpeg */ -/* - * Matroska element IDs. max. 32-bit. - */ - -/* toplevel segment */ -#define MATROSKA_ID_SEGMENT 0x18538067 /* sub-elements */ - -/* matroska top-level master IDs */ -#define MATROSKA_ID_SEEKHEAD 0x114D9B74 /* sub-elements */ -#define MATROSKA_ID_INFO 0x1549A966 /* sub-elements */ -#define MATROSKA_ID_CLUSTER 0x1F43B675 /* sub-elements */ -#define MATROSKA_ID_TRACKS 0x1654AE6B /* sub-elements */ -#define MATROSKA_ID_CUES 0x1C53BB6B /* sub-elements */ -#define MATROSKA_ID_ATTACHMENTS 0x1941A469 /* sub-elements */ -#define MATROSKA_ID_CHAPTERS 0x1043A770 /* sub-elements */ -#define MATROSKA_ID_TAGS 0x1254C367 /* sub-elements */ - -/* IDs in the info master */ -#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1 /* u-integer */ -#define MATROSKA_ID_DURATION 0x4489 /* float */ -#define MATROSKA_ID_TITLE 0x7BA9 /* UTF-8 */ -#define MATROSKA_ID_WRITINGAPP 0x5741 /* UTF-8 */ -#define MATROSKA_ID_MUXINGAPP 0x4D80 /* UTF-8 */ -#define MATROSKA_ID_DATEUTC 0x4461 /* date */ -#define MATROSKA_ID_SEGMENTUID 0x73A4 /* binary */ - -/* ID in the tracks master */ -#define MATROSKA_ID_TRACKENTRY 0xAE /* sub-elements */ - -/* IDs in the trackentry master */ -#define MATROSKA_ID_TRACKNUMBER 0xD7 /* u-integer */ -#define MATROSKA_ID_TRACKUID 0x73C5 /* u-integer */ -#define MATROSKA_ID_TRACKTYPE 0x83 /* u-integer */ -#define MATROSKA_ID_TRACKAUDIO 0xE1 /* sub-elements */ -#define MATROSKA_ID_TRACKVIDEO 0xE0 /* sub-elements */ -#define MATROSKA_ID_CODECID 0x86 /* string */ -#define MATROSKA_ID_CODECPRIVATE 0x63A2 /* binary */ -#define MATROSKA_ID_CODECNAME 0x258688 /* UTF-8 */ -#define MATROSKA_ID_CODECINFOURL 0x3B4040 /* string */ -#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 /* string */ -#define MATROSKA_ID_TRACKNAME 0x536E /* UTF-8 */ -#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C /* string */ -#define MATROSKA_ID_TRACKFLAGENABLED 0xB9 /* u-integer (1 bit) */ -#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88 /* u-integer (1 bit) */ -#define MATROSKA_ID_TRACKFLAGLACING 0x9C /* u-integer (1 bit) */ -#define MATROSKA_ID_TRACKFLAGFORCED 0x55AA /* u-integer (1 bit) */ -#define MATROSKA_ID_TRACKMINCACHE 0x6DE7 /* u-integer */ -#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8 /* u-integer */ -#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383 /* u-integer */ - -/* IDs in the trackvideo master */ -#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0 /* u-integer */ -#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA /* u-integer */ -#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2 /* u-integer */ -#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0 /* u-integer */ -#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA /* u-integer */ -#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A /* u-integer (1 bit) */ -#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B9 /* u-integer */ -#define MATROSKA_ID_VIDEOASPECTRATIOTYPE 0x54B3 /* u-integer */ -#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524 /* binary */ -#define MATROSKA_ID_VIDEOPIXELCROPBOTTOM 0x54AA /* u-integer */ -#define MATROSKA_ID_VIDEOPIXELCROPTOP 0x54BB /* u-integer */ -#define MATROSKA_ID_VIDEOPIXELCROPLEFT 0x54CC /* u-integer */ -#define MATROSKA_ID_VIDEOPIXELCROPRIGHT 0x54DD /* u-integer */ - -/* IDs in the trackaudio master */ -#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5 /* float */ -#define MATROSKA_ID_AUDIOOUTSAMPLINGFREQ 0x78B5 /* float */ -#define MATROSKA_ID_AUDIOBITDEPTH 0x6264 /* u-integer */ -#define MATROSKA_ID_AUDIOCHANNELS 0x9F /* u-integer */ - -/* ID in the cues master */ -#define MATROSKA_ID_CUEPOINT 0xBB /* sub-elements */ - -/* IDs in the pointentry master */ -#define MATROSKA_ID_CUETIME 0xB3 /* u-integer */ -#define MATROSKA_ID_CUETRACKPOSITIONS 0xB7 /* sub-elements */ - -/* IDs in the cuetrackposition master */ -#define MATROSKA_ID_CUETRACK 0xF7 /* u-integer */ -#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 /* u-integer */ -#define MATROSKA_ID_CUEBLOCKNUMBER 0x5378 /* u-integer */ - -/* IDs in the tags master */ -/* TODO */ - -/* IDs in the seekhead master */ -#define MATROSKA_ID_SEEKENTRY 0x4DBB /* sub-elements */ - -/* IDs in the seekpoint master */ -#define MATROSKA_ID_SEEKID 0x53AB /* binary */ -#define MATROSKA_ID_SEEKPOSITION 0x53AC /* u-integer */ - -/* IDs in the cluster master */ -#define MATROSKA_ID_CLUSTERTIMECODE 0xE7 /* u-integer */ -#define MATROSKA_ID_BLOCKGROUP 0xA0 /* sub-elements */ -#define MATROSKA_ID_SIMPLEBLOCK 0xA3 /* binary */ - -/* IDs in the blockgroup master */ -#define MATROSKA_ID_BLOCK 0xA1 /* binary */ -#define MATROSKA_ID_BLOCKDURATION 0x9B /* u-integer */ -#define MATROSKA_ID_REFERENCEBLOCK 0xFB /* s-integer */ - -/* IDs in the attachments master */ -#define MATROSKA_ID_ATTACHEDFILE 0x61A7 /* sub-elements */ -#define MATROSKA_ID_FILENAME 0x466E /* UTF-8 */ -#define MATROSKA_ID_FILEMIMETYPE 0x4660 /* string */ -#define MATROSKA_ID_FILEDATA 0x465C /* binary */ -#define MATROSKA_ID_FILEUID 0x46AE /* u-integer */ -/* Above copied from ffmpeg */ - -/* IDs in the chapters master */ -#define MATROSKA_ID_EDITIONENTRY 0x45B9 /* sub-elements */ - -/* IDs in the edition entry master */ -#define MATROSKA_ID_EDITIONUID 0x45BC /* u-integer */ -#define MATROSKA_ID_EDITIONFLAGHIDDEN 0x45BD /* u-integer (1 bit) */ -#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB /* u-integer (1 bit) */ -#define MATROSKA_ID_EDITIONFLAGORDERED 0x45DD /* u-integer (1 bit) */ -#define MATROSKA_ID_CHAPTERATOM 0xB6 /* sub-elements */ - -/* IDs in the chapter atom master */ -#define MATROSKA_ID_CHAPTERUID 0x73C4 /* u-integer */ -#define MATROSKA_ID_CHAPTERTIMESTART 0x91 /* u-integer */ -#define MATROSKA_ID_CHAPTERTIMEEND 0x92 /* u-integer */ -#define MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98 /* u-integer (1 bit) */ -#define MATROSKA_ID_CHAPTERFLAGENABLED 0x4598 /* u-integer (1 bit) */ -#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67 /* binary */ -#define MATROSKA_ID_CHAPTERSEGMENTEDITIONUID 0x6EBC /* binary */ -#define MATROSKA_ID_CHAPTERPHYSICALEQUIV 0x63C3 /* u-integer */ -#define MATROSKA_ID_CHAPTERTRACK 0x8F /* sub-elements */ -#define MATROSKA_ID_CHAPTERDISPLAY 0x80 /* sub-elements */ -#define MATROSKA_ID_CHAPPROCESS 0x6944 /* sub-elements */ - -/* IDs in the chapter track master */ -#define MATROSKA_ID_CHAPTERTRACKNUMBER 0x89 /* u-integer */ - -/* IDs in the chapter display master */ -#define MATROSKA_ID_CHAPTERSTRING 0x85 /* UTF-8 */ -#define MATROSKA_ID_CHAPTERLANGUAGE 0x437C /* string */ -#define MATROSKA_ID_CHAPTERCOUNTRY 0x437E /* string */ - -/* IDs in the chap process master */ -#define MATROSKA_ID_CHAPPROCESSCODECID 0x6955 /* u-integer */ -#define MATROSKA_ID_CHAPPROCESSPRIVATE 0x450D /* binary */ -#define MATROSKA_ID_CHAPPROCESSCOMMAND 0x6911 /* sub-elements */ - -/* IDs in the chap proccess command master */ -#define MATROSKA_ID_CHAPPROCESSTIME 0x6922 /* u-integer */ -#define MATROSKA_ID_CHAPPROCESSDATA 0x6933 /* binary */ - -typedef struct mk_Seek_s mk_Seek; -typedef struct mk_Chapter_s mk_Chapter; - -struct mk_Writer_s { - FILE *fp; - uint64_t f_pos; - uint64_t f_eof; - - int64_t duration_ptr; - int64_t seekhead_ptr; - int64_t segment_ptr; - int64_t segmentuid_ptr; - - mk_Context *root; - mk_Context *freelist; - mk_Context *actlist; - mk_Context *chapters; - mk_Context *edition_entry; - mk_Context *tracks; - mk_Context *cues; - - uint64_t def_duration; - uint64_t timescale; - - uint8_t wrote_header; - - uint8_t num_tracks; - uint8_t alloc_tracks; - mk_Track **tracks_arr; - - struct { - int64_t segmentinfo; - int64_t seekhead; - int64_t tracks; - int64_t cues; - int64_t chapters; - int64_t attachments; - int64_t tags; - } seek_data; - - struct { - mk_Context *context; - mk_Context *seekhead; - uint64_t block_count; - uint64_t count; - uint64_t pointer; - int64_t tc_scaled; - } cluster; - - md5_context segment_md5; - uint8_t vlc_compat; -}; - -struct mk_Track_s { - uint8_t track_id; - - int64_t prev_frame_tc_scaled; - int64_t max_frame_tc; - uint8_t in_frame; - uint64_t default_duration; - mk_TrackType track_type; - int64_t prev_cue_pos; - - struct { - mk_Context *data; - int64_t timecode; - uint8_t keyframe; - mk_LacingType lacing; - uint8_t lacing_num_frames; - uint64_t *lacing_sizes; - } frame; -}; - -int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, uint64_t seek_pos); -int mk_writeSeekHead(mk_Writer *w, int64_t *pointer); -int mk_writeTracks(mk_Writer *w, mk_Context *tracks); -int mk_writeChapters(mk_Writer *w); -int mk_seekFile(mk_Writer *w, uint64_t pos); - -#endif +/***************************************************************************** + * matroska.h: + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Authors: Mike Matsnev + * Nathan Caldwell + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ +#ifndef _MATROSKA_H +#define _MATROSKA_H 1 + +#include "md5.h" +#include "ebml.h" + +#define CLSIZE 1048576 +#define CHECK(x) do { if ((x) < 0) return -1; } while (0) + +/* Matroska version supported */ +#define MATROSKA_VERSION 2 + +/* Copied from ffmpeg */ +/* + * Matroska element IDs. max. 32-bit. + */ + +/* toplevel segment */ +#define MATROSKA_ID_SEGMENT 0x18538067 /* sub-elements */ + +/* matroska top-level master IDs */ +#define MATROSKA_ID_SEEKHEAD 0x114D9B74 /* sub-elements */ +#define MATROSKA_ID_INFO 0x1549A966 /* sub-elements */ +#define MATROSKA_ID_CLUSTER 0x1F43B675 /* sub-elements */ +#define MATROSKA_ID_TRACKS 0x1654AE6B /* sub-elements */ +#define MATROSKA_ID_CUES 0x1C53BB6B /* sub-elements */ +#define MATROSKA_ID_ATTACHMENTS 0x1941A469 /* sub-elements */ +#define MATROSKA_ID_CHAPTERS 0x1043A770 /* sub-elements */ +#define MATROSKA_ID_TAGS 0x1254C367 /* sub-elements */ + +/* IDs in the info master */ +#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1 /* u-integer */ +#define MATROSKA_ID_DURATION 0x4489 /* float */ +#define MATROSKA_ID_TITLE 0x7BA9 /* UTF-8 */ +#define MATROSKA_ID_WRITINGAPP 0x5741 /* UTF-8 */ +#define MATROSKA_ID_MUXINGAPP 0x4D80 /* UTF-8 */ +#define MATROSKA_ID_DATEUTC 0x4461 /* date */ +#define MATROSKA_ID_SEGMENTUID 0x73A4 /* binary */ + +/* ID in the tracks master */ +#define MATROSKA_ID_TRACKENTRY 0xAE /* sub-elements */ + +/* IDs in the trackentry master */ +#define MATROSKA_ID_TRACKNUMBER 0xD7 /* u-integer */ +#define MATROSKA_ID_TRACKUID 0x73C5 /* u-integer */ +#define MATROSKA_ID_TRACKTYPE 0x83 /* u-integer */ +#define MATROSKA_ID_TRACKAUDIO 0xE1 /* sub-elements */ +#define MATROSKA_ID_TRACKVIDEO 0xE0 /* sub-elements */ +#define MATROSKA_ID_CODECID 0x86 /* string */ +#define MATROSKA_ID_CODECPRIVATE 0x63A2 /* binary */ +#define MATROSKA_ID_CODECNAME 0x258688 /* UTF-8 */ +#define MATROSKA_ID_CODECINFOURL 0x3B4040 /* string */ +#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 /* string */ +#define MATROSKA_ID_TRACKNAME 0x536E /* UTF-8 */ +#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C /* string */ +#define MATROSKA_ID_TRACKFLAGENABLED 0xB9 /* u-integer (1 bit) */ +#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88 /* u-integer (1 bit) */ +#define MATROSKA_ID_TRACKFLAGLACING 0x9C /* u-integer (1 bit) */ +#define MATROSKA_ID_TRACKFLAGFORCED 0x55AA /* u-integer (1 bit) */ +#define MATROSKA_ID_TRACKMINCACHE 0x6DE7 /* u-integer */ +#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8 /* u-integer */ +#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383 /* u-integer */ + +/* IDs in the trackvideo master */ +#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0 /* u-integer */ +#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA /* u-integer */ +#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2 /* u-integer */ +#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0 /* u-integer */ +#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA /* u-integer */ +#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A /* u-integer (1 bit) */ +#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B9 /* u-integer */ +#define MATROSKA_ID_VIDEOASPECTRATIOTYPE 0x54B3 /* u-integer */ +#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524 /* binary */ +#define MATROSKA_ID_VIDEOPIXELCROPBOTTOM 0x54AA /* u-integer */ +#define MATROSKA_ID_VIDEOPIXELCROPTOP 0x54BB /* u-integer */ +#define MATROSKA_ID_VIDEOPIXELCROPLEFT 0x54CC /* u-integer */ +#define MATROSKA_ID_VIDEOPIXELCROPRIGHT 0x54DD /* u-integer */ + +/* IDs in the trackaudio master */ +#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5 /* float */ +#define MATROSKA_ID_AUDIOOUTSAMPLINGFREQ 0x78B5 /* float */ +#define MATROSKA_ID_AUDIOBITDEPTH 0x6264 /* u-integer */ +#define MATROSKA_ID_AUDIOCHANNELS 0x9F /* u-integer */ + +/* ID in the cues master */ +#define MATROSKA_ID_CUEPOINT 0xBB /* sub-elements */ + +/* IDs in the pointentry master */ +#define MATROSKA_ID_CUETIME 0xB3 /* u-integer */ +#define MATROSKA_ID_CUETRACKPOSITIONS 0xB7 /* sub-elements */ + +/* IDs in the cuetrackposition master */ +#define MATROSKA_ID_CUETRACK 0xF7 /* u-integer */ +#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 /* u-integer */ +#define MATROSKA_ID_CUEBLOCKNUMBER 0x5378 /* u-integer */ + +/* IDs in the tags master */ +/* TODO */ + +/* IDs in the seekhead master */ +#define MATROSKA_ID_SEEKENTRY 0x4DBB /* sub-elements */ + +/* IDs in the seekpoint master */ +#define MATROSKA_ID_SEEKID 0x53AB /* binary */ +#define MATROSKA_ID_SEEKPOSITION 0x53AC /* u-integer */ + +/* IDs in the cluster master */ +#define MATROSKA_ID_CLUSTERTIMECODE 0xE7 /* u-integer */ +#define MATROSKA_ID_BLOCKGROUP 0xA0 /* sub-elements */ +#define MATROSKA_ID_SIMPLEBLOCK 0xA3 /* binary */ + +/* IDs in the blockgroup master */ +#define MATROSKA_ID_BLOCK 0xA1 /* binary */ +#define MATROSKA_ID_BLOCKDURATION 0x9B /* u-integer */ +#define MATROSKA_ID_REFERENCEBLOCK 0xFB /* s-integer */ + +/* IDs in the attachments master */ +#define MATROSKA_ID_ATTACHEDFILE 0x61A7 /* sub-elements */ +#define MATROSKA_ID_FILENAME 0x466E /* UTF-8 */ +#define MATROSKA_ID_FILEMIMETYPE 0x4660 /* string */ +#define MATROSKA_ID_FILEDATA 0x465C /* binary */ +#define MATROSKA_ID_FILEUID 0x46AE /* u-integer */ +/* Above copied from ffmpeg */ + +/* IDs in the chapters master */ +#define MATROSKA_ID_EDITIONENTRY 0x45B9 /* sub-elements */ + +/* IDs in the edition entry master */ +#define MATROSKA_ID_EDITIONUID 0x45BC /* u-integer */ +#define MATROSKA_ID_EDITIONFLAGHIDDEN 0x45BD /* u-integer (1 bit) */ +#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB /* u-integer (1 bit) */ +#define MATROSKA_ID_EDITIONFLAGORDERED 0x45DD /* u-integer (1 bit) */ +#define MATROSKA_ID_CHAPTERATOM 0xB6 /* sub-elements */ + +/* IDs in the chapter atom master */ +#define MATROSKA_ID_CHAPTERUID 0x73C4 /* u-integer */ +#define MATROSKA_ID_CHAPTERTIMESTART 0x91 /* u-integer */ +#define MATROSKA_ID_CHAPTERTIMEEND 0x92 /* u-integer */ +#define MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98 /* u-integer (1 bit) */ +#define MATROSKA_ID_CHAPTERFLAGENABLED 0x4598 /* u-integer (1 bit) */ +#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67 /* binary */ +#define MATROSKA_ID_CHAPTERSEGMENTEDITIONUID 0x6EBC /* binary */ +#define MATROSKA_ID_CHAPTERPHYSICALEQUIV 0x63C3 /* u-integer */ +#define MATROSKA_ID_CHAPTERTRACK 0x8F /* sub-elements */ +#define MATROSKA_ID_CHAPTERDISPLAY 0x80 /* sub-elements */ +#define MATROSKA_ID_CHAPPROCESS 0x6944 /* sub-elements */ + +/* IDs in the chapter track master */ +#define MATROSKA_ID_CHAPTERTRACKNUMBER 0x89 /* u-integer */ + +/* IDs in the chapter display master */ +#define MATROSKA_ID_CHAPTERSTRING 0x85 /* UTF-8 */ +#define MATROSKA_ID_CHAPTERLANGUAGE 0x437C /* string */ +#define MATROSKA_ID_CHAPTERCOUNTRY 0x437E /* string */ + +/* IDs in the chap process master */ +#define MATROSKA_ID_CHAPPROCESSCODECID 0x6955 /* u-integer */ +#define MATROSKA_ID_CHAPPROCESSPRIVATE 0x450D /* binary */ +#define MATROSKA_ID_CHAPPROCESSCOMMAND 0x6911 /* sub-elements */ + +/* IDs in the chap proccess command master */ +#define MATROSKA_ID_CHAPPROCESSTIME 0x6922 /* u-integer */ +#define MATROSKA_ID_CHAPPROCESSDATA 0x6933 /* binary */ + +typedef struct mk_Seek_s mk_Seek; +typedef struct mk_Chapter_s mk_Chapter; + +struct mk_Writer_s { + FILE *fp; + uint64_t f_pos; + uint64_t f_eof; + + int64_t duration_ptr; + int64_t seekhead_ptr; + int64_t segment_ptr; + int64_t segmentuid_ptr; + + mk_Context *root; + mk_Context *freelist; + mk_Context *actlist; + mk_Context *chapters; + mk_Context *edition_entry; + mk_Context *tracks; + mk_Context *cues; + + uint64_t def_duration; + uint64_t timescale; + + uint8_t wrote_header; + + uint8_t num_tracks; + uint8_t alloc_tracks; + mk_Track **tracks_arr; + + struct { + int64_t segmentinfo; + int64_t seekhead; + int64_t tracks; + int64_t cues; + int64_t chapters; + int64_t attachments; + int64_t tags; + } seek_data; + + struct { + mk_Context *context; + mk_Context *seekhead; + uint64_t block_count; + uint64_t count; + uint64_t pointer; + int64_t tc_scaled; + } cluster; + + md5_context segment_md5; + uint8_t vlc_compat; +}; + +struct mk_Track_s { + uint8_t track_id; + + int64_t prev_frame_tc_scaled; + int64_t max_frame_tc; + uint8_t in_frame; + uint64_t default_duration; + mk_TrackType track_type; + int64_t prev_cue_pos; + + struct { + mk_Context *data; + int64_t timecode; + uint8_t keyframe; + mk_LacingType lacing; + uint8_t lacing_num_frames; + uint64_t *lacing_sizes; + } frame; +}; + +int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, + uint64_t seek_pos); +int mk_writeSeekHead(mk_Writer *w, int64_t *pointer); +int mk_writeTracks(mk_Writer *w, mk_Context *tracks); +int mk_writeChapters(mk_Writer *w); +int mk_seekFile(mk_Writer *w, uint64_t pos); + +#endif diff --git a/src/md5.c b/src/md5.c dissimilarity index 77% index f8c30b4..320371c 100644 --- a/src/md5.c +++ b/src/md5.c @@ -1,455 +1,442 @@ -/* - * RFC 1321 compliant MD5 implementation - * - * Copyright (C) 2006-2007 Christophe Devine - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License, version 2.1 as published by the Free Software Foundation. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -/* - * The MD5 algorithm was designed by Ron Rivest in 1991. - * From the XYSSL project. - * - * http://www.ietf.org/rfc/rfc1321.txt - */ -#include "md5.h" - -#include -#include - -/* - * 32-bit integer manipulation macros (little endian) - */ -#ifndef GET_ULONG_LE -#define GET_ULONG_LE(n,b,i) \ -{ \ - (n) = ( (unsigned long) (b)[(i) ] ) \ - | ( (unsigned long) (b)[(i) + 1] << 8 ) \ - | ( (unsigned long) (b)[(i) + 2] << 16 ) \ - | ( (unsigned long) (b)[(i) + 3] << 24 ); \ -} -#endif - -#ifndef PUT_ULONG_LE -#define PUT_ULONG_LE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ -} -#endif - -/* - * MD5 context setup - */ -void md5_starts( md5_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; -} - -static void md5_process( md5_context *ctx, unsigned char data[64] ) -{ - unsigned long X[16], A, B, C, D; - - GET_ULONG_LE( X[ 0], data, 0 ); - GET_ULONG_LE( X[ 1], data, 4 ); - GET_ULONG_LE( X[ 2], data, 8 ); - GET_ULONG_LE( X[ 3], data, 12 ); - GET_ULONG_LE( X[ 4], data, 16 ); - GET_ULONG_LE( X[ 5], data, 20 ); - GET_ULONG_LE( X[ 6], data, 24 ); - GET_ULONG_LE( X[ 7], data, 28 ); - GET_ULONG_LE( X[ 8], data, 32 ); - GET_ULONG_LE( X[ 9], data, 36 ); - GET_ULONG_LE( X[10], data, 40 ); - GET_ULONG_LE( X[11], data, 44 ); - GET_ULONG_LE( X[12], data, 48 ); - GET_ULONG_LE( X[13], data, 52 ); - GET_ULONG_LE( X[14], data, 56 ); - GET_ULONG_LE( X[15], data, 60 ); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define P(a,b,c,d,k,s,t) \ -{ \ - a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - -#define F(x,y,z) (z ^ (x & (y ^ z))) - - P( A, B, C, D, 0, 7, 0xD76AA478 ); - P( D, A, B, C, 1, 12, 0xE8C7B756 ); - P( C, D, A, B, 2, 17, 0x242070DB ); - P( B, C, D, A, 3, 22, 0xC1BDCEEE ); - P( A, B, C, D, 4, 7, 0xF57C0FAF ); - P( D, A, B, C, 5, 12, 0x4787C62A ); - P( C, D, A, B, 6, 17, 0xA8304613 ); - P( B, C, D, A, 7, 22, 0xFD469501 ); - P( A, B, C, D, 8, 7, 0x698098D8 ); - P( D, A, B, C, 9, 12, 0x8B44F7AF ); - P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); - P( B, C, D, A, 11, 22, 0x895CD7BE ); - P( A, B, C, D, 12, 7, 0x6B901122 ); - P( D, A, B, C, 13, 12, 0xFD987193 ); - P( C, D, A, B, 14, 17, 0xA679438E ); - P( B, C, D, A, 15, 22, 0x49B40821 ); - -#undef F - -#define F(x,y,z) (y ^ (z & (x ^ y))) - - P( A, B, C, D, 1, 5, 0xF61E2562 ); - P( D, A, B, C, 6, 9, 0xC040B340 ); - P( C, D, A, B, 11, 14, 0x265E5A51 ); - P( B, C, D, A, 0, 20, 0xE9B6C7AA ); - P( A, B, C, D, 5, 5, 0xD62F105D ); - P( D, A, B, C, 10, 9, 0x02441453 ); - P( C, D, A, B, 15, 14, 0xD8A1E681 ); - P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); - P( A, B, C, D, 9, 5, 0x21E1CDE6 ); - P( D, A, B, C, 14, 9, 0xC33707D6 ); - P( C, D, A, B, 3, 14, 0xF4D50D87 ); - P( B, C, D, A, 8, 20, 0x455A14ED ); - P( A, B, C, D, 13, 5, 0xA9E3E905 ); - P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); - P( C, D, A, B, 7, 14, 0x676F02D9 ); - P( B, C, D, A, 12, 20, 0x8D2A4C8A ); - -#undef F - -#define F(x,y,z) (x ^ y ^ z) - - P( A, B, C, D, 5, 4, 0xFFFA3942 ); - P( D, A, B, C, 8, 11, 0x8771F681 ); - P( C, D, A, B, 11, 16, 0x6D9D6122 ); - P( B, C, D, A, 14, 23, 0xFDE5380C ); - P( A, B, C, D, 1, 4, 0xA4BEEA44 ); - P( D, A, B, C, 4, 11, 0x4BDECFA9 ); - P( C, D, A, B, 7, 16, 0xF6BB4B60 ); - P( B, C, D, A, 10, 23, 0xBEBFBC70 ); - P( A, B, C, D, 13, 4, 0x289B7EC6 ); - P( D, A, B, C, 0, 11, 0xEAA127FA ); - P( C, D, A, B, 3, 16, 0xD4EF3085 ); - P( B, C, D, A, 6, 23, 0x04881D05 ); - P( A, B, C, D, 9, 4, 0xD9D4D039 ); - P( D, A, B, C, 12, 11, 0xE6DB99E5 ); - P( C, D, A, B, 15, 16, 0x1FA27CF8 ); - P( B, C, D, A, 2, 23, 0xC4AC5665 ); - -#undef F - -#define F(x,y,z) (y ^ (x | ~z)) - - P( A, B, C, D, 0, 6, 0xF4292244 ); - P( D, A, B, C, 7, 10, 0x432AFF97 ); - P( C, D, A, B, 14, 15, 0xAB9423A7 ); - P( B, C, D, A, 5, 21, 0xFC93A039 ); - P( A, B, C, D, 12, 6, 0x655B59C3 ); - P( D, A, B, C, 3, 10, 0x8F0CCC92 ); - P( C, D, A, B, 10, 15, 0xFFEFF47D ); - P( B, C, D, A, 1, 21, 0x85845DD1 ); - P( A, B, C, D, 8, 6, 0x6FA87E4F ); - P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); - P( C, D, A, B, 6, 15, 0xA3014314 ); - P( B, C, D, A, 13, 21, 0x4E0811A1 ); - P( A, B, C, D, 4, 6, 0xF7537E82 ); - P( D, A, B, C, 11, 10, 0xBD3AF235 ); - P( C, D, A, B, 2, 15, 0x2AD7D2BB ); - P( B, C, D, A, 9, 21, 0xEB86D391 ); - -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; -} - -/* - * MD5 process buffer - */ -void md5_update( md5_context *ctx, unsigned char *input, int ilen ) -{ - int fill; - unsigned long left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (unsigned long) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, fill ); - md5_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - md5_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, ilen ); - } -} - -static const unsigned char md5_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * MD5 final digest - */ -void md5_finish( md5_context *ctx, unsigned char output[16] ) -{ - unsigned long last, padn; - unsigned long high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_ULONG_LE( low, msglen, 0 ); - PUT_ULONG_LE( high, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - md5_update( ctx, (unsigned char *) md5_padding, padn ); - md5_update( ctx, msglen, 8 ); - - PUT_ULONG_LE( ctx->state[0], output, 0 ); - PUT_ULONG_LE( ctx->state[1], output, 4 ); - PUT_ULONG_LE( ctx->state[2], output, 8 ); - PUT_ULONG_LE( ctx->state[3], output, 12 ); -} - -/* - * output = MD5( input buffer ) - */ -void md5( unsigned char *input, int ilen, unsigned char output[16] ) -{ - md5_context ctx; - - md5_starts( &ctx ); - md5_update( &ctx, input, ilen ); - md5_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); -} - -/* - * output = MD5( file contents ) - */ -int md5_file( char *path, unsigned char output[16] ) -{ - FILE *f; - size_t n; - md5_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( 1 ); - - md5_starts( &ctx ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - md5_update( &ctx, buf, (int) n ); - - md5_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( 2 ); - } - - fclose( f ); - return( 0 ); -} - -/* - * MD5 HMAC context setup - */ -void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen ) -{ - int i; - unsigned char sum[16]; - - if( keylen > 64 ) - { - md5( key, keylen, sum ); - keylen = 16; - key = sum; - } - - memset( ctx->ipad, 0x36, 64 ); - memset( ctx->opad, 0x5C, 64 ); - - for( i = 0; i < keylen; i++ ) - { - ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); - ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); - } - - md5_starts( ctx ); - md5_update( ctx, ctx->ipad, 64 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * MD5 HMAC process buffer - */ -void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen ) -{ - md5_update( ctx, input, ilen ); -} - -/* - * MD5 HMAC final digest - */ -void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ) -{ - unsigned char tmpbuf[16]; - - md5_finish( ctx, tmpbuf ); - md5_starts( ctx ); - md5_update( ctx, ctx->opad, 64 ); - md5_update( ctx, tmpbuf, 16 ); - md5_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * output = HMAC-MD5( hmac key, input buffer ) - */ -void md5_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen, - unsigned char output[16] ) -{ - md5_context ctx; - - md5_hmac_starts( &ctx, key, keylen ); - md5_hmac_update( &ctx, input, ilen ); - md5_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); -} - -#if defined(XYSSL_SELF_TEST) - -/* - * RFC 1321 test vectors - */ -static const char md5_test_str[7][81] = -{ - { "" }, - { "a" }, - { "abc" }, - { "message digest" }, - { "abcdefghijklmnopqrstuvwxyz" }, - { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, - { "12345678901234567890123456789012345678901234567890123456789012" \ - "345678901234567890" } -}; - -static const unsigned char md5_test_sum[7][16] = -{ - { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, - 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E }, - { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, - 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 }, - { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, - 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }, - { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, - 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 }, - { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, - 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B }, - { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, - 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F }, - { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, - 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A } -}; - -/* - * Checkup routine - */ -int md5_self_test( int verbose ) -{ - int i; - unsigned char md5sum[16]; - - for( i = 0; i < 7; i++ ) - { - if( verbose != 0 ) - printf( " MD5 test #%d: ", i + 1 ); - - md5( (unsigned char *) md5_test_str[i], - strlen( md5_test_str[i] ), md5sum ); - - if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); - - return( 0 ); -} - -#endif +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2006-2007 Christophe Devine + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * From the XYSSL project. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ +#include "md5.h" + +#include +#include + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD5 context setup + */ +void md5_starts(md5_context * ctx) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md5_process(md5_context * ctx, unsigned char data[64]) +{ + unsigned long X[16], A, B, C, D; + + GET_ULONG_LE(X[0], data, 0); + GET_ULONG_LE(X[1], data, 4); + GET_ULONG_LE(X[2], data, 8); + GET_ULONG_LE(X[3], data, 12); + GET_ULONG_LE(X[4], data, 16); + GET_ULONG_LE(X[5], data, 20); + GET_ULONG_LE(X[6], data, 24); + GET_ULONG_LE(X[7], data, 28); + GET_ULONG_LE(X[8], data, 32); + GET_ULONG_LE(X[9], data, 36); + GET_ULONG_LE(X[10], data, 40); + GET_ULONG_LE(X[11], data, 44); + GET_ULONG_LE(X[12], data, 48); + GET_ULONG_LE(X[13], data, 52); + GET_ULONG_LE(X[14], data, 56); + GET_ULONG_LE(X[15], data, 60); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P(A, B, C, D, 0, 7, 0xD76AA478); + P(D, A, B, C, 1, 12, 0xE8C7B756); + P(C, D, A, B, 2, 17, 0x242070DB); + P(B, C, D, A, 3, 22, 0xC1BDCEEE); + P(A, B, C, D, 4, 7, 0xF57C0FAF); + P(D, A, B, C, 5, 12, 0x4787C62A); + P(C, D, A, B, 6, 17, 0xA8304613); + P(B, C, D, A, 7, 22, 0xFD469501); + P(A, B, C, D, 8, 7, 0x698098D8); + P(D, A, B, C, 9, 12, 0x8B44F7AF); + P(C, D, A, B, 10, 17, 0xFFFF5BB1); + P(B, C, D, A, 11, 22, 0x895CD7BE); + P(A, B, C, D, 12, 7, 0x6B901122); + P(D, A, B, C, 13, 12, 0xFD987193); + P(C, D, A, B, 14, 17, 0xA679438E); + P(B, C, D, A, 15, 22, 0x49B40821); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P(A, B, C, D, 1, 5, 0xF61E2562); + P(D, A, B, C, 6, 9, 0xC040B340); + P(C, D, A, B, 11, 14, 0x265E5A51); + P(B, C, D, A, 0, 20, 0xE9B6C7AA); + P(A, B, C, D, 5, 5, 0xD62F105D); + P(D, A, B, C, 10, 9, 0x02441453); + P(C, D, A, B, 15, 14, 0xD8A1E681); + P(B, C, D, A, 4, 20, 0xE7D3FBC8); + P(A, B, C, D, 9, 5, 0x21E1CDE6); + P(D, A, B, C, 14, 9, 0xC33707D6); + P(C, D, A, B, 3, 14, 0xF4D50D87); + P(B, C, D, A, 8, 20, 0x455A14ED); + P(A, B, C, D, 13, 5, 0xA9E3E905); + P(D, A, B, C, 2, 9, 0xFCEFA3F8); + P(C, D, A, B, 7, 14, 0x676F02D9); + P(B, C, D, A, 12, 20, 0x8D2A4C8A); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P(A, B, C, D, 5, 4, 0xFFFA3942); + P(D, A, B, C, 8, 11, 0x8771F681); + P(C, D, A, B, 11, 16, 0x6D9D6122); + P(B, C, D, A, 14, 23, 0xFDE5380C); + P(A, B, C, D, 1, 4, 0xA4BEEA44); + P(D, A, B, C, 4, 11, 0x4BDECFA9); + P(C, D, A, B, 7, 16, 0xF6BB4B60); + P(B, C, D, A, 10, 23, 0xBEBFBC70); + P(A, B, C, D, 13, 4, 0x289B7EC6); + P(D, A, B, C, 0, 11, 0xEAA127FA); + P(C, D, A, B, 3, 16, 0xD4EF3085); + P(B, C, D, A, 6, 23, 0x04881D05); + P(A, B, C, D, 9, 4, 0xD9D4D039); + P(D, A, B, C, 12, 11, 0xE6DB99E5); + P(C, D, A, B, 15, 16, 0x1FA27CF8); + P(B, C, D, A, 2, 23, 0xC4AC5665); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P(A, B, C, D, 0, 6, 0xF4292244); + P(D, A, B, C, 7, 10, 0x432AFF97); + P(C, D, A, B, 14, 15, 0xAB9423A7); + P(B, C, D, A, 5, 21, 0xFC93A039); + P(A, B, C, D, 12, 6, 0x655B59C3); + P(D, A, B, C, 3, 10, 0x8F0CCC92); + P(C, D, A, B, 10, 15, 0xFFEFF47D); + P(B, C, D, A, 1, 21, 0x85845DD1); + P(A, B, C, D, 8, 6, 0x6FA87E4F); + P(D, A, B, C, 15, 10, 0xFE2CE6E0); + P(C, D, A, B, 6, 15, 0xA3014314); + P(B, C, D, A, 13, 21, 0x4E0811A1); + P(A, B, C, D, 4, 6, 0xF7537E82); + P(D, A, B, C, 11, 10, 0xBD3AF235); + P(C, D, A, B, 2, 15, 0x2AD7D2BB); + P(B, C, D, A, 9, 21, 0xEB86D391); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD5 process buffer + */ +void md5_update(md5_context * ctx, unsigned char *input, int ilen) +{ + int fill; + unsigned long left; + + if (ilen <= 0) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (unsigned long) ilen) + ctx->total[1]++; + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), (void *) input, fill); + md5_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + md5_process(ctx, input); + input += 64; + ilen -= 64; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), (void *) input, ilen); + } +} + +static const unsigned char md5_padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * MD5 final digest + */ +void md5_finish(md5_context * ctx, unsigned char output[16]) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + PUT_ULONG_LE(low, msglen, 0); + PUT_ULONG_LE(high, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + md5_update(ctx, (unsigned char *) md5_padding, padn); + md5_update(ctx, msglen, 8); + + PUT_ULONG_LE(ctx->state[0], output, 0); + PUT_ULONG_LE(ctx->state[1], output, 4); + PUT_ULONG_LE(ctx->state[2], output, 8); + PUT_ULONG_LE(ctx->state[3], output, 12); +} + +/* + * output = MD5( input buffer ) + */ +void md5(unsigned char *input, int ilen, unsigned char output[16]) +{ + md5_context ctx; + + md5_starts(&ctx); + md5_update(&ctx, input, ilen); + md5_finish(&ctx, output); + + memset(&ctx, 0, sizeof(md5_context)); +} + +/* + * output = MD5( file contents ) + */ +int md5_file(char *path, unsigned char output[16]) +{ + FILE *f; + size_t n; + md5_context ctx; + unsigned char buf[1024]; + + if ((f = fopen(path, "rb")) == NULL) + return (1); + + md5_starts(&ctx); + + while ((n = fread(buf, 1, sizeof(buf), f)) > 0) + md5_update(&ctx, buf, (int) n); + + md5_finish(&ctx, output); + + memset(&ctx, 0, sizeof(md5_context)); + + if (ferror(f) != 0) { + fclose(f); + return (2); + } + + fclose(f); + return (0); +} + +/* + * MD5 HMAC context setup + */ +void md5_hmac_starts(md5_context * ctx, unsigned char *key, int keylen) +{ + int i; + unsigned char sum[16]; + + if (keylen > 64) { + md5(key, keylen, sum); + keylen = 16; + key = sum; + } + + memset(ctx->ipad, 0x36, 64); + memset(ctx->opad, 0x5C, 64); + + for (i = 0; i < keylen; i++) { + ctx->ipad[i] = (unsigned char) (ctx->ipad[i] ^ key[i]); + ctx->opad[i] = (unsigned char) (ctx->opad[i] ^ key[i]); + } + + md5_starts(ctx); + md5_update(ctx, ctx->ipad, 64); + + memset(sum, 0, sizeof(sum)); +} + +/* + * MD5 HMAC process buffer + */ +void md5_hmac_update(md5_context * ctx, unsigned char *input, int ilen) +{ + md5_update(ctx, input, ilen); +} + +/* + * MD5 HMAC final digest + */ +void md5_hmac_finish(md5_context * ctx, unsigned char output[16]) +{ + unsigned char tmpbuf[16]; + + md5_finish(ctx, tmpbuf); + md5_starts(ctx); + md5_update(ctx, ctx->opad, 64); + md5_update(ctx, tmpbuf, 16); + md5_finish(ctx, output); + + memset(tmpbuf, 0, sizeof(tmpbuf)); +} + +/* + * output = HMAC-MD5( hmac key, input buffer ) + */ +void md5_hmac(unsigned char *key, int keylen, unsigned char *input, + int ilen, unsigned char output[16]) +{ + md5_context ctx; + + md5_hmac_starts(&ctx, key, keylen); + md5_hmac_update(&ctx, input, ilen); + md5_hmac_finish(&ctx, output); + + memset(&ctx, 0, sizeof(md5_context)); +} + +#if defined(XYSSL_SELF_TEST) + +/* + * RFC 1321 test vectors + */ +static const char md5_test_str[7][81] = { + {""}, + {"a"}, + {"abc"}, + {"message digest"}, + {"abcdefghijklmnopqrstuvwxyz"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + {"12345678901234567890123456789012345678901234567890123456789012" + "345678901234567890"} +}; + +static const unsigned char md5_test_sum[7][16] = { + {0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, + 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E}, + {0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, + 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61}, + {0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, + 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72}, + {0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, + 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0}, + {0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, + 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B}, + {0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, + 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F}, + {0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, + 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A} +}; + +/* + * Checkup routine + */ +int md5_self_test(int verbose) +{ + int i; + unsigned char md5sum[16]; + + for (i = 0; i < 7; i++) { + if (verbose != 0) + printf(" MD5 test #%d: ", i + 1); + + md5((unsigned char *) md5_test_str[i], + strlen(md5_test_str[i]), md5sum); + + if (memcmp(md5sum, md5_test_sum[i], 16) != 0) { + if (verbose != 0) + printf("failed\n"); + + return (1); + } + + if (verbose != 0) + printf("passed\n"); + } + + if (verbose != 0) + printf("\n"); + + return (0); +} + +#endif diff --git a/src/md5.h b/src/md5.h index 8d9d876..724ded2 100644 --- a/src/md5.h +++ b/src/md5.h @@ -8,16 +8,14 @@ /** * \brief MD5 context structure */ -typedef struct -{ - unsigned long total[2]; /*!< number of bytes processed */ - unsigned long state[4]; /*!< intermediate digest state */ - unsigned char buffer[64]; /*!< data block being processed */ +typedef struct { + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ - unsigned char ipad[64]; /*!< HMAC: inner padding */ - unsigned char opad[64]; /*!< HMAC: outer padding */ -} -md5_context; + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} md5_context; #ifdef __cplusplus extern "C" { @@ -28,7 +26,7 @@ extern "C" { * * \param ctx context to be initialized */ -void md5_starts( md5_context *ctx ); + void md5_starts(md5_context * ctx); /** * \brief MD5 process buffer @@ -37,7 +35,7 @@ void md5_starts( md5_context *ctx ); * \param input buffer holding the data * \param ilen length of the input data */ -void md5_update( md5_context *ctx, unsigned char *input, int ilen ); + void md5_update(md5_context * ctx, unsigned char *input, int ilen); /** * \brief MD5 final digest @@ -45,7 +43,7 @@ void md5_update( md5_context *ctx, unsigned char *input, int ilen ); * \param ctx MD5 context * \param output MD5 checksum result */ -void md5_finish( md5_context *ctx, unsigned char output[16] ); + void md5_finish(md5_context * ctx, unsigned char output[16]); /** * \brief Output = MD5( input buffer ) @@ -54,7 +52,7 @@ void md5_finish( md5_context *ctx, unsigned char output[16] ); * \param ilen length of the input data * \param output MD5 checksum result */ -void md5( unsigned char *input, int ilen, unsigned char output[16] ); + void md5(unsigned char *input, int ilen, unsigned char output[16]); /** * \brief Output = MD5( file contents ) @@ -65,7 +63,7 @@ void md5( unsigned char *input, int ilen, unsigned char output[16] ); * \return 0 if successful, 1 if fopen failed, * or 2 if fread failed */ -int md5_file( char *path, unsigned char output[16] ); + int md5_file(char *path, unsigned char output[16]); /** * \brief MD5 HMAC context setup @@ -74,7 +72,8 @@ int md5_file( char *path, unsigned char output[16] ); * \param key HMAC secret key * \param keylen length of the HMAC key */ -void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen ); + void md5_hmac_starts(md5_context * ctx, unsigned char *key, + int keylen); /** * \brief MD5 HMAC process buffer @@ -83,7 +82,8 @@ void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen ); * \param input buffer holding the data * \param ilen length of the input data */ -void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen ); + void md5_hmac_update(md5_context * ctx, unsigned char *input, + int ilen); /** * \brief MD5 HMAC final digest @@ -91,7 +91,7 @@ void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen ); * \param ctx HMAC context * \param output MD5 HMAC checksum result */ -void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); + void md5_hmac_finish(md5_context * ctx, unsigned char output[16]); /** * \brief Output = HMAC-MD5( hmac key, input buffer ) @@ -102,19 +102,18 @@ void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); * \param ilen length of the input data * \param output HMAC-MD5 result */ -void md5_hmac( unsigned char *key, int keylen, - unsigned char *input, int ilen, - unsigned char output[16] ); + void md5_hmac(unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[16]); /** * \brief Checkup routine * * \return 0 if successful, or 1 if the test failed */ -int md5_self_test( int verbose ); + int md5_self_test(int verbose); #ifdef __cplusplus } #endif - -#endif /* md5.h */ +#endif /* md5.h */ diff --git a/src/tracks.c b/src/tracks.c dissimilarity index 80% index 132316a..4bdda79 100644 --- a/src/tracks.c +++ b/src/tracks.c @@ -1,167 +1,202 @@ -/***************************************************************************** - * tracks.c: - ***************************************************************************** - * Copyright (C) 2007 libmkv - * $Id: $ - * - * Authors: Mike Matsnev - * Nathan Caldwell - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ -#include "config.h" -#include "libmkv.h" -#include "matroska.h" - -/* TODO: Figure out what can actually fail without damaging the track. */ - -#define TRACK_STEP 4 - -mk_Track *mk_createTrack(mk_Writer *w, mk_TrackConfig *tc) -{ - mk_Context *ti, *v; - int i; - mk_Track *track = calloc(1, sizeof(*track)); - if (track == NULL) - return NULL; - - if (w->num_tracks + 1 > w->alloc_tracks) - { - if ((w->tracks_arr = realloc(w->tracks_arr, (w->alloc_tracks + TRACK_STEP) * sizeof(mk_Track *))) == NULL) - return NULL; // FIXME - w->alloc_tracks += TRACK_STEP; - } - w->tracks_arr[w->num_tracks] = track; - track->track_id = ++w->num_tracks; - - if (w->tracks == NULL) - { - if ((w->tracks = mk_createContext(w, w->root, MATROSKA_ID_TRACKS)) == NULL) // tracks - return NULL; - } - - if ((ti = mk_createContext(w, w->tracks, MATROSKA_ID_TRACKENTRY)) == NULL) // TrackEntry - return NULL; - if (mk_writeUInt(ti, MATROSKA_ID_TRACKNUMBER, track->track_id) < 0) // TrackNumber - return NULL; - if (tc->trackUID) { - if (mk_writeUInt(ti, MATROSKA_ID_TRACKUID, tc->trackUID) < 0) /* TrackUID */ - return NULL; - } else { - /* - * If we aren't given a UID, randomly generate one. - * NOTE: It would probably be better to CRC32 some unique track information - * in place of something completely random. - */ - unsigned long track_uid; - track_uid = random(); - - if (mk_writeUInt(ti, MATROSKA_ID_TRACKUID, track_uid) < 0) /* TrackUID */ - return NULL; - } - if (mk_writeUInt(ti, MATROSKA_ID_TRACKTYPE, tc->trackType) < 0) // TrackType - return NULL; - track->track_type = tc->trackType; - if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGLACING, tc->flagLacing) < 0) // FlagLacing - return NULL; - if (mk_writeStr(ti, MATROSKA_ID_CODECID, tc->codecID) < 0) // CodecID - return NULL; - if (tc->codecPrivateSize && (tc->codecPrivate != NULL)) - if (mk_writeBin(ti, MATROSKA_ID_CODECPRIVATE, tc->codecPrivate, tc->codecPrivateSize) < 0) // CodecPrivate - return NULL; - if (tc->defaultDuration) { - if (mk_writeUInt(ti, MATROSKA_ID_TRACKDEFAULTDURATION, tc->defaultDuration) < 0) - return NULL; - track->default_duration = tc->defaultDuration; - } - if (tc->language) - if (mk_writeStr(ti, MATROSKA_ID_TRACKLANGUAGE, tc->language) < 0) // Language - return NULL; - if (tc->flagEnabled != 1) - if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGENABLED, tc->flagEnabled) < 0) // FlagEnabled - return NULL; - if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGDEFAULT, tc->flagDefault) < 0) // FlagDefault - return NULL; - if (tc->flagForced) - if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGFORCED, tc->flagForced) < 0) // FlagForced - return NULL; - if (tc->minCache) - if (mk_writeUInt(ti, MATROSKA_ID_TRACKMINCACHE, tc->minCache) < 0) // MinCache - return NULL; - /* FIXME: this won't handle NULL values, which signals that the cache is disabled. */ - if (tc->maxCache) - if (mk_writeUInt(ti, MATROSKA_ID_TRACKMAXCACHE, tc->maxCache) < 0) // MaxCache - return NULL; - - switch (tc->trackType) - { - case MK_TRACK_VIDEO: // Video - if ((v = mk_createContext(w, ti, MATROSKA_ID_TRACKVIDEO)) == NULL) - return NULL; - if (tc->extra.video.pixelCrop[0] != 0 || tc->extra.video.pixelCrop[1] != 0 || tc->extra.video.pixelCrop[2] != 0 || tc->extra.video.pixelCrop[3] != 0) { - for (i = 0; i < 4; i++) { - /* Each pixel crop ID is 0x11 away from the next. - * In order from 0x54AA to 0x54DD they are bottom, top, left, right. - */ - if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELCROPBOTTOM + (i * 0x11), tc->extra.video.pixelCrop[i]) < 0) // PixelCrop - return NULL; - } - } - if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELWIDTH, tc->extra.video.pixelWidth) < 0) // PixelWidth - return NULL; - if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELHEIGHT, tc->extra.video.pixelHeight) < 0 ) // PixelHeight - return NULL; - if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYWIDTH, tc->extra.video.displayWidth) < 0) // DisplayWidth - return NULL; - if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYHEIGHT, tc->extra.video.displayHeight) < 0) // DisplayHeight - return NULL; - if (tc->extra.video.displayUnit) - if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYUNIT, tc->extra.video.displayUnit) < 0) // DisplayUnit - return NULL; - if (tc->extra.video.aspectRatioType != MK_ASPECTRATIO_FREE) - if (mk_writeUInt(v, MATROSKA_ID_VIDEOASPECTRATIOTYPE, tc->extra.video.aspectRatioType) < 0) /* AspectRatioType */ - return NULL; - break; - case MK_TRACK_AUDIO: // Audio - if ((v = mk_createContext(w, ti, MATROSKA_ID_TRACKAUDIO)) == NULL) - return NULL; - if (mk_writeFloat(v, MATROSKA_ID_AUDIOSAMPLINGFREQ, tc->extra.audio.samplingFreq) < 0) // SamplingFrequency - return NULL; - if (mk_writeUInt(v, MATROSKA_ID_AUDIOCHANNELS, tc->extra.audio.channels) < 0) // Channels - return NULL; - if (tc->extra.audio.bitDepth) - if (mk_writeUInt(v, MATROSKA_ID_AUDIOBITDEPTH, tc->extra.audio.bitDepth) < 0) // BitDepth - return NULL; - break; - default: // Other TODO: Implement other track types. - return NULL; - } - - if (mk_closeContext(v, 0) < 0) - return NULL; - if (mk_closeContext(ti, 0) < 0) - return NULL; - - return track; -} - -int mk_writeTracks(mk_Writer *w, mk_Context *tracks) -{ - w->seek_data.tracks = w->root->d_cur; - - CHECK(mk_closeContext(w->tracks, 0)); - - return 0; -} +/***************************************************************************** + * tracks.c: + ***************************************************************************** + * Copyright (C) 2007 libmkv + * + * Authors: Mike Matsnev + * Nathan Caldwell + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ +#include "config.h" +#include "libmkv.h" +#include "matroska.h" + +/* TODO: Figure out what can actually fail without damaging the track. */ + +#define TRACK_STEP 4 + +mk_Track *mk_createTrack(mk_Writer *w, mk_TrackConfig *tc) +{ + mk_Context *ti, *v; + int i; + mk_Track *track = calloc(1, sizeof(*track)); + if (track == NULL) + return NULL; + + if (w->num_tracks + 1 > w->alloc_tracks) { + if ((w->tracks_arr = realloc(w->tracks_arr, (w->alloc_tracks + TRACK_STEP) * sizeof(mk_Track *))) == NULL) + return NULL; // FIXME + w->alloc_tracks += TRACK_STEP; + } + w->tracks_arr[w->num_tracks] = track; + track->track_id = ++w->num_tracks; + + if (w->tracks == NULL) { + /* Tracks */ + if ((w->tracks = mk_createContext(w, w->root, MATROSKA_ID_TRACKS)) == NULL) + return NULL; + } + + /* TrackEntry */ + if ((ti = mk_createContext(w, w->tracks, MATROSKA_ID_TRACKENTRY)) == NULL) + return NULL; + /* TrackNumber */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKNUMBER, track->track_id) < 0) + return NULL; + if (tc->trackUID) { + if (mk_writeUInt(ti, MATROSKA_ID_TRACKUID, tc->trackUID) < 0) /* TrackUID */ + return NULL; + } else { + /* + * If we aren't given a UID, randomly generate one. + * NOTE: It would probably be better to CRC32 some unique track information + * in place of something completely random. + */ + unsigned long track_uid; + track_uid = random(); + + if (mk_writeUInt(ti, MATROSKA_ID_TRACKUID, track_uid) < 0) /* TrackUID */ + return NULL; + } + if (mk_writeUInt(ti, MATROSKA_ID_TRACKTYPE, tc->trackType) < 0) /* TrackType */ + return NULL; + track->track_type = tc->trackType; + /* FlagLacing */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGLACING, tc->flagLacing) < 0) + return NULL; + if (mk_writeStr(ti, MATROSKA_ID_CODECID, tc->codecID) < 0) /* CodecID */ + return NULL; + if (tc->codecPrivateSize && (tc->codecPrivate != NULL)) { + /* CodecPrivate */ + if (mk_writeBin(ti, MATROSKA_ID_CODECPRIVATE, tc->codecPrivate, tc->codecPrivateSize) < 0) + return NULL; + } + if (tc->defaultDuration) { + /* DefaultDuration */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKDEFAULTDURATION, tc->defaultDuration) < 0) + return NULL; + track->default_duration = tc->defaultDuration; + } + if (tc->language) { + /* Language */ + if (mk_writeStr(ti, MATROSKA_ID_TRACKLANGUAGE, tc->language) < 0) + return NULL; + } + if (tc->flagEnabled != 1) { + /* FlagEnabled */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGENABLED, tc->flagEnabled) < 0) + return NULL; + } + /* FlagDefault */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGDEFAULT, tc->flagDefault) < 0) + return NULL; + if (tc->flagForced) { + /* FlagForced */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKFLAGFORCED, tc->flagForced) < 0) + return NULL; + } + if (tc->minCache){ + /* MinCache */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKMINCACHE, tc->minCache) < 0) + return NULL; + } + + /* FIXME: this won't handle NULL values, which signals that the cache is disabled. */ + if (tc->maxCache) { + /* MaxCache */ + if (mk_writeUInt(ti, MATROSKA_ID_TRACKMAXCACHE, tc->maxCache) < 0) + return NULL; + } + + switch (tc->trackType) { + case MK_TRACK_VIDEO: + /* Video */ + if ((v = mk_createContext(w, ti, MATROSKA_ID_TRACKVIDEO)) == NULL) + return NULL; + if (tc->extra.video.pixelCrop[0] != 0 + || tc->extra.video.pixelCrop[1] != 0 + || tc->extra.video.pixelCrop[2] != 0 + || tc->extra.video.pixelCrop[3] != 0) { + for (i = 0; i < 4; i++) { + /* Each pixel crop ID is 0x11 away from the next. + * In order from 0x54AA to 0x54DD they are bottom, top, left, right. + */ + /* PixelCrop{Bottom,Top,Left,Right} */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELCROPBOTTOM + (i * 0x11), tc->extra.video.pixelCrop[i]) < 0) + return NULL; + } + } + /* PixelWidth */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELWIDTH, tc->extra.video.pixelWidth) < 0) + return NULL; + /* PixelHeight */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEOPIXELHEIGHT, tc->extra.video.pixelHeight) < 0) + return NULL; + /* DisplayWidth */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYWIDTH, tc->extra.video.displayWidth) < 0) + return NULL; + /* DisplayHeight */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYHEIGHT, tc->extra.video.displayHeight) < 0) + return NULL; + if (tc->extra.video.displayUnit) { + /* DisplayUnit */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEODISPLAYUNIT, tc->extra.video.displayUnit) < 0) + return NULL; + } + if (tc->extra.video.aspectRatioType != MK_ASPECTRATIO_FREE) { + /* AspectRatioType */ + if (mk_writeUInt(v, MATROSKA_ID_VIDEOASPECTRATIOTYPE, tc->extra.video.aspectRatioType) < 0) + return NULL; + } + break; + case MK_TRACK_AUDIO: + /* Audio */ + if ((v = mk_createContext(w, ti, MATROSKA_ID_TRACKAUDIO)) == NULL) + return NULL; + /* SamplingFrequency */ + if (mk_writeFloat(v, MATROSKA_ID_AUDIOSAMPLINGFREQ, tc->extra.audio.samplingFreq) < 0) + return NULL; + /* Channels */ + if (mk_writeUInt(v, MATROSKA_ID_AUDIOCHANNELS, tc->extra.audio.channels) < 0) + return NULL; + if (tc->extra.audio.bitDepth) { + /* BitDepth */ + if (mk_writeUInt(v, MATROSKA_ID_AUDIOBITDEPTH, tc->extra.audio.bitDepth) < 0) + return NULL; + } + break; + default: /* Other track types + * TODO: Implement other valid track types. + */ + return NULL; + } + + if (mk_closeContext(v, 0) < 0) + return NULL; + if (mk_closeContext(ti, 0) < 0) + return NULL; + + return track; +} + +int mk_writeTracks(mk_Writer *w, mk_Context *tracks) +{ + w->seek_data.tracks = w->root->d_cur; + + CHECK(mk_closeContext(w->tracks, 0)); + + return 0; +} -- 2.11.4.GIT