static int mk_closeCluster(mk_Writer *w) { if (w->cluster == NULL) return 0; CHECK(mk_closeContext(w->cluster, 0)); w->cluster = NULL; CHECK(mk_flushContextData(w->root)); return 0; }
int mk_close(mk_Writer *w) { int ret = 0; if (mk_flushFrame(w) < 0 || mk_closeCluster(w) < 0) ret = -1; if (w->wrote_header) { fseek(w->fp, w->duration_ptr, SEEK_SET); if (mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)) < 0 || mk_flushContextData(w->root) < 0) ret = -1; } mk_destroyContexts(w); fclose(w->fp); free(w); return ret; }
static int mk_closeContext(mk_Context *c, unsigned *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; }
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]; if (mk_flushFrame(w, tk) < 0) ret = -1; } 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->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->attachments != NULL) { w->seek_data.attachments = w->f_pos - w->segment_ptr; mk_writeAttachments(w); if (mk_flushContextData(w->root) < 0) ret = -1; } if (w->tags != NULL) { w->seek_data.tags = w->f_pos - w->segment_ptr; mk_writeTags(w); 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 + RESERVED_SEEKHEAD + 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, (RESERVED_CHAPTERS - (w->f_pos - w->segment_ptr - RESERVED_SEEKHEAD - 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, (RESERVED_SEEKHEAD - (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; // The conditional below is easier to understand, but incorrect // because f_pos is unsigned and causes the lhs to be evaluated // as an unsigned quantity. // if (((i + w->segment_ptr) - w->f_pos - 2) > 1) if ((i + w->segment_ptr) > (w->f_pos + 3)) 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; } /* update any track private data that may have changed */ for (i = w->num_tracks - 1; i >= 0; i--) { tk = w->tracks_arr[i]; if (tk->private_data_size && tk->private_data) { if (mk_seekFile(w, tk->private_data_ptr) < 0) ret = -1; if (mk_writeBin(w->root, MATROSKA_ID_CODECPRIVATE, tk->private_data, tk->private_data_size) < 0 || mk_flushContextData(w->root) < 0) ret = -1; free(tk->private_data); } w->tracks_arr[i] = NULL; --w->num_tracks; free(tk); } if (mk_closeContext(w->root, 0) < 0) ret = -1; mk_destroyContexts(w); fclose(w->fp); free(w->tracks_arr); free(w); return ret; }
int mk_writeHeader(mk_Writer *w, const char *writingApp) { mk_Context *c; mk_Track *tk; int i; 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; /* Reserve space for a SegmentUID (16 bytes + 1 byte longer EBML ID), to be written it later. */ CHECK(mk_writeVoid(c, 16 + 1)); 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) { offset = 0; CHECK(mk_closeContext(w->tracks, &offset)); for (i = 0; i < w->num_tracks; i++) { tk = w->tracks_arr[i]; if (tk->private_data_size) tk->private_data_ptr += offset; } } CHECK(mk_flushContextData(w->root)); w->wrote_header = 1; w->def_duration = w->tracks_arr[0]->default_duration; return 0; }
int mk_writeHeader(mk_Writer *w, const char *writingApp, const char *codecID, const void *codecPrivate, unsigned codecPrivateSize, int64_t default_frame_duration, int64_t timescale, unsigned width, unsigned height, unsigned d_width, unsigned d_height) { mk_Context *c, *ti, *v; if (w->wrote_header) return -1; w->timescale = timescale; w->def_duration = default_frame_duration; if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML return -1; CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion CHECK(mk_closeContext(c, 0)); if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment return -1; CHECK(mk_flushContextID(c)); CHECK(mk_closeContext(c, 0)); if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo return -1; CHECK(mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0")); CHECK(mk_writeStr(c, 0x5741, writingApp)); CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale)); CHECK(mk_writeFloat(c, 0x4489, 0)); w->duration_ptr = c->d_cur - 4; CHECK(mk_closeContext(c, &w->duration_ptr)); if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks return -1; if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry return -1; CHECK(mk_writeUInt(ti, 0xd7, 1)); // TrackNumber CHECK(mk_writeUInt(ti, 0x73c5, 1)); // TrackUID CHECK(mk_writeUInt(ti, 0x83, 1)); // TrackType CHECK(mk_writeUInt(ti, 0x9c, 0)); // FlagLacing CHECK(mk_writeStr(ti, 0x86, codecID)); // CodecID if (codecPrivateSize) CHECK(mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize)); // CodecPrivate if (default_frame_duration) CHECK(mk_writeUInt(ti, 0x23e383, default_frame_duration)); // DefaultDuration if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video return -1; CHECK(mk_writeUInt(v, 0xb0, width)); CHECK(mk_writeUInt(v, 0xba, height)); CHECK(mk_writeUInt(v, 0x54b0, d_width)); CHECK(mk_writeUInt(v, 0x54ba, d_height)); CHECK(mk_closeContext(v, 0)); CHECK(mk_closeContext(ti, 0)); CHECK(mk_closeContext(c, 0)); CHECK(mk_flushContextData(w->root)); w->wrote_header = 1; return 0; }