From ece5a963f2ac093f13e308339f45de7c17ad7818 Mon Sep 17 00:00:00 2001 From: ireader Date: Sat, 6 Jan 2024 15:10:38 +0800 Subject: [PATCH] feat: mp4 chapter: track or udta --- libmov/include/mov-format.h | 1 + libmov/include/mov-udta.h | 7 ++++ libmov/source/mov-internal.h | 5 +++ libmov/source/mov-minf.c | 49 ++++++++++++++++++++++ libmov/source/mov-reader.c | 1 + libmov/source/mov-stsd.c | 26 +++++++++++- libmov/source/mov-stts.c | 2 +- libmov/source/mov-tag.c | 1 + libmov/source/mov-track.c | 51 +++++++++++++++++++++-- libmov/source/mov-udta.c | 74 +++++++++++++++++++++++++++++++++ libmov/source/mov-writer.c | 4 ++ libmov/test/mov-writer-test.cpp | 19 ++++++++- 12 files changed, 233 insertions(+), 7 deletions(-) diff --git a/libmov/include/mov-format.h b/libmov/include/mov-format.h index c3c9737f..4106d32e 100644 --- a/libmov/include/mov-format.h +++ b/libmov/include/mov-format.h @@ -29,6 +29,7 @@ #define MOV_OBJECT_VP9 0xB1 // VP9 Video #define MOV_OBJECT_FLAC 0xC1 // nonstandard from FFMPEG #define MOV_OBJECT_VP8 0xC2 // nonstandard +#define MOV_OBJECT_CHAPTER 0xC3 // chapter https://developer.apple.com/documentation/quicktime-file-format/base_media_information_header_atom #define MOV_OBJECT_H266 0xFC // ITU-T Recommendation H.266 #define MOV_OBJECT_G711a 0xFD // ITU G.711 alaw #define MOV_OBJECT_G711u 0xFE // ITU G.711 ulaw diff --git a/libmov/include/mov-udta.h b/libmov/include/mov-udta.h index 1e21c2da..0f4238ac 100644 --- a/libmov/include/mov-udta.h +++ b/libmov/include/mov-udta.h @@ -40,6 +40,13 @@ struct mov_udta_meta_t int mov_udta_meta_write(const struct mov_udta_meta_t* meta, void* data, int bytes); +struct mov_udta_chapter_t +{ + uint64_t timestamp; // 1s = 10 * 1000 * 1000 + const char* title; // optional +}; +int mov_udta_chapter_write(const struct mov_udta_chapter_t* chapters, int count, void* data, int bytes); + #ifdef __cplusplus } #endif diff --git a/libmov/source/mov-internal.h b/libmov/source/mov-internal.h index 31b2696f..38f9c7fb 100644 --- a/libmov/source/mov-internal.h +++ b/libmov/source/mov-internal.h @@ -177,6 +177,8 @@ struct mov_track_t struct mov_elst_t* elst; size_t elst_count; + + uint32_t chpl_track; // MOV_CHAPTER track struct mov_sample_t* samples; uint32_t sample_count; @@ -254,6 +256,7 @@ int mov_read_text(struct mov_t* mov, const struct mov_box_t* box); int mov_read_smdm(struct mov_t* mov, const struct mov_box_t* box); int mov_read_coll(struct mov_t* mov, const struct mov_box_t* box); int mov_read_udta(struct mov_t* mov, const struct mov_box_t* box); +int mov_read_chpl(struct mov_t* mov, const struct mov_box_t* box); size_t mov_write_ftyp(const struct mov_t* mov); size_t mov_write_mvhd(const struct mov_t* mov); @@ -263,6 +266,7 @@ size_t mov_write_hdlr(const struct mov_t* mov); size_t mov_write_vmhd(const struct mov_t* mov); size_t mov_write_smhd(const struct mov_t* mov); size_t mov_write_nmhd(const struct mov_t* mov); +size_t mov_write_gmhd(const struct mov_t* mov); size_t mov_write_sthd(const struct mov_t* mov); size_t mov_write_dinf(const struct mov_t* mov); size_t mov_write_dref(const struct mov_t* mov); @@ -291,6 +295,7 @@ size_t mov_write_mehd(const struct mov_t* mov); size_t mov_write_sidx(const struct mov_t* mov, uint64_t offset); size_t mov_write_mfhd(const struct mov_t* mov, uint32_t fragment); size_t mov_write_edts(const struct mov_t* mov); +size_t mov_write_tref(const struct mov_t* mov); size_t mov_write_stbl(const struct mov_t* mov); size_t mov_write_minf(const struct mov_t* mov); size_t mov_write_mdia(const struct mov_t* mov); diff --git a/libmov/source/mov-minf.c b/libmov/source/mov-minf.c index 999d6321..74425caf 100644 --- a/libmov/source/mov-minf.c +++ b/libmov/source/mov-minf.c @@ -76,6 +76,25 @@ int mov_read_text(struct mov_t* mov, const struct mov_box_t* box) return 0; } +int mov_read_chpl(struct mov_t* mov, const struct mov_box_t* box) +{ + int i, count, len; + mov_buffer_r8(&mov->io); /* version */ + mov_buffer_r24(&mov->io); /* flags */ + mov_buffer_skip(&mov->io, 4); /* unknown*/ + + count = mov_buffer_r8(&mov->io); /* count */ + for (i = 0; i < count; i++) + { + mov_buffer_r64(&mov->io); /* timestamp */ + len = mov_buffer_r8(&mov->io); /* title size */ + mov_buffer_skip(&mov->io, len); /* title */ + } + + (void)box; + return 0; +} + size_t mov_write_vmhd(const struct mov_t* mov) { mov_buffer_w32(&mov->io, 20); /* size (always 0x14) */ @@ -103,6 +122,36 @@ size_t mov_write_nmhd(const struct mov_t* mov) return 12; } +size_t mov_write_gmhd(const struct mov_t* mov) +{ + mov_buffer_w32(&mov->io, 8 + 24 + 44); /* size */ + mov_buffer_write(&mov->io, "gmhd", 4); + + mov_buffer_w32(&mov->io, 24); /* size */ + mov_buffer_write(&mov->io, "gmin", 4); + mov_buffer_w32(&mov->io, 0); /* version & flags */ + mov_buffer_w16(&mov->io, 0x40); /* graphics mode */ + mov_buffer_w16(&mov->io, 0x8000); /* opcolor red*/ + mov_buffer_w16(&mov->io, 0x8000); /* opcolor green*/ + mov_buffer_w16(&mov->io, 0x8000); /* opcolor blue*/ + mov_buffer_w16(&mov->io, 0); /* balance */ + mov_buffer_w16(&mov->io, 0); /* reserved */ + + mov_buffer_w32(&mov->io, 44); /* size */ + mov_buffer_write(&mov->io, "text", 4); + mov_buffer_w32(&mov->io, 0x00010000); /* u */ + mov_buffer_w32(&mov->io, 0); + mov_buffer_w32(&mov->io, 0); + mov_buffer_w32(&mov->io, 0); /* v */ + mov_buffer_w32(&mov->io, 0x00010000); + mov_buffer_w32(&mov->io, 0); + mov_buffer_w32(&mov->io, 0); /* w */ + mov_buffer_w32(&mov->io, 0); + mov_buffer_w32(&mov->io, 0x40000000); + + return 8 + 24 + 44; +} + /* ISO/IEC 14496-12:2015(E) 12.6.2 Subtitle media header (p185) aligned(8) class SubtitleMediaHeaderBox extends FullBox ('sthd', version = 0, flags = 0){ diff --git a/libmov/source/mov-reader.c b/libmov/source/mov-reader.c index c3980043..7ade5a65 100755 --- a/libmov/source/mov-reader.c +++ b/libmov/source/mov-reader.c @@ -208,6 +208,7 @@ static struct mov_parse_t s_mov_parse_table[] = { { MOV_TAG('a', 'v', '1', 'C'), MOV_NULL, mov_read_extra }, // av1-isobmff { MOV_TAG('a', 'v', 'c', 'C'), MOV_NULL, mov_read_extra }, // ISO/IEC 14496-15:2010(E) avcC { MOV_TAG('b', 't', 'r', 't'), MOV_NULL, mov_read_btrt }, // ISO/IEC 14496-15:2010(E) 5.3.4.1.1 Definition + { MOV_TAG('c', 'h', 'p', 'l'), MOV_STBL, mov_read_chpl }, // chapter title { MOV_TAG('c', 'o', '6', '4'), MOV_STBL, mov_read_stco }, { MOV_TAG('C', 'o', 'L', 'L'), MOV_STBL, mov_read_coll }, { MOV_TAG('c', 't', 't', 's'), MOV_STBL, mov_read_ctts }, diff --git a/libmov/source/mov-stsd.c b/libmov/source/mov-stsd.c index 0253ea68..1ed561b6 100644 --- a/libmov/source/mov-stsd.c +++ b/libmov/source/mov-stsd.c @@ -196,6 +196,8 @@ static int mov_read_hint_sample_entry(struct mov_t* mov, struct mov_sample_entry struct mov_box_t box; mov_read_sample_entry(mov, &box, &entry->data_reference_index); mov_buffer_skip(&mov->io, box.size - 16); + entry->object_type_indication = mov_tag_to_object(box.type); + entry->stream_type = MP4_STREAM_VISUAL; mov->track->tag = box.type; return mov_buffer_error(&mov->io); } @@ -205,6 +207,8 @@ static int mov_read_meta_sample_entry(struct mov_t* mov, struct mov_sample_entry struct mov_box_t box; mov_read_sample_entry(mov, &box, &entry->data_reference_index); mov_buffer_skip(&mov->io, box.size - 16); + entry->object_type_indication = mov_tag_to_object(box.type); + entry->stream_type = MP4_STREAM_VISUAL; mov->track->tag = box.type; return mov_buffer_error(&mov->io); } @@ -226,7 +230,7 @@ static int mov_read_text_sample_entry(struct mov_t* mov, struct mov_sample_entry mov_read_sample_entry(mov, &box, &entry->data_reference_index); if (MOV_TEXT == box.type) { - // https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-69835 + // https://developer.apple.com/documentation/quicktime-file-format/text_sample_description //mov_buffer_r32(&mov->io); /* display flags */ //mov_buffer_r32(&mov->io); /* text justification */ //mov_buffer_r16(&mov->io); /* background color: 48-bit RGB color */ @@ -249,6 +253,8 @@ static int mov_read_text_sample_entry(struct mov_t* mov, struct mov_sample_entry mov_buffer_skip(&mov->io, box.size - 16); } + entry->object_type_indication = mov_tag_to_object(box.type); + entry->stream_type = MP4_STREAM_VISUAL; mov->track->tag = box.type; return mov_buffer_error(&mov->io); } @@ -381,6 +387,18 @@ int mov_read_stsd(struct mov_t* mov, const struct mov_box_t* box) // return size; //} +static size_t mov_write_btrt(const struct mov_t* mov, const struct mov_sample_entry_t* entry) +{ + mov_buffer_w32(&mov->io, 20); /* size */ + mov_buffer_write(&mov->io, "btrt", 4); + mov_buffer_w32(&mov->io, entry->u.bitrate.bufferSizeDB); + mov_buffer_w32(&mov->io, 0x00000014); + mov_buffer_w32(&mov->io, 0x00000014); + //mov_buffer_w32(&mov->io, entry->u.bitrate.maxBitrate); + //mov_buffer_w32(&mov->io, entry->u.bitrate.avgBitrate); + return 20; +} + static size_t mov_write_video(const struct mov_t* mov, const struct mov_sample_entry_t* entry) { size_t size; @@ -437,6 +455,7 @@ static size_t mov_write_video(const struct mov_t* mov, const struct mov_sample_e else if (MOV_OBJECT_VP8 == entry->object_type_indication || MOV_OBJECT_VP9 == entry->object_type_indication) size += mov_write_vpcc(mov); + //size += mov_write_btrt(mov, entry); mov_write_size(mov, offset, size); /* update size */ return size; } @@ -479,6 +498,7 @@ static size_t mov_write_audio(const struct mov_t* mov, const struct mov_sample_e else if(MOV_OBJECT_OPUS == entry->object_type_indication) size += mov_write_dops(mov); + //size += mov_write_btrt(mov, entry); mov_write_size(mov, offset, size); /* update size */ return size; } @@ -488,7 +508,7 @@ static int mov_write_subtitle(const struct mov_t* mov, const struct mov_sample_e int size; uint64_t offset; - size = 8 /* Box */ + 8 /* SampleEntry */ + entry->extra_data_size; + size = 8 /* Box */ + 8 /* SampleEntry */; offset = mov_buffer_tell(&mov->io); mov_buffer_w32(&mov->io, 0); /* size */ @@ -506,6 +526,8 @@ static int mov_write_subtitle(const struct mov_t* mov, const struct mov_sample_e { mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); size += entry->extra_data_size; + + size += mov_write_btrt(mov, entry); } mov_write_size(mov, offset, size); /* update size */ diff --git a/libmov/source/mov-stts.c b/libmov/source/mov-stts.c index 520ab962..08e73c2c 100644 --- a/libmov/source/mov-stts.c +++ b/libmov/source/mov-stts.c @@ -152,7 +152,7 @@ uint32_t mov_build_stts(struct mov_track_t* track) { assert(track->samples[i + 1].dts >= track->samples[i].dts || i + 1 == track->sample_count); delta = (uint32_t)(i + 1 < track->sample_count && track->samples[i + 1].dts > track->samples[i].dts ? track->samples[i + 1].dts - track->samples[i].dts : 1); - if (NULL != sample && delta == sample->samples_per_chunk) + if (NULL != sample && (delta == sample->samples_per_chunk || i + 1 == track->sample_count) ) { track->samples[i].first_chunk = 0; assert(sample->first_chunk > 0); diff --git a/libmov/source/mov-tag.c b/libmov/source/mov-tag.c index ee0d1e68..869a2490 100644 --- a/libmov/source/mov-tag.c +++ b/libmov/source/mov-tag.c @@ -26,6 +26,7 @@ static struct mov_object_tag s_tags[] = { { MOV_OBJECT_TEXT, MOV_TAG('t', 'x', '3', 'g') }, { MOV_OBJECT_TEXT, MOV_TAG('t', 'e', 'x', 't') }, { MOV_OBJECT_TEXT, MOV_TAG('c', '6', '0', '8') }, + { MOV_OBJECT_CHAPTER, MOV_TAG('t', 'e', 'x', 't') }, { MOV_OBJECT_OPUS, MOV_OPUS }, { MOV_OBJECT_VP8, MOV_VP8 }, { MOV_OBJECT_VP9, MOV_VP9 }, diff --git a/libmov/source/mov-track.c b/libmov/source/mov-track.c index 4efcab9f..415bd4f7 100644 --- a/libmov/source/mov-track.c +++ b/libmov/source/mov-track.c @@ -176,6 +176,28 @@ int mov_add_video(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, uint int mov_add_subtitle(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, uint32_t timescale, uint8_t object, const void* extra_data, size_t extra_data_size) { + static const uint8_t chapter_extra_data[] = { + // TextSampleEntry + 0x00, 0x00, 0x00, 0x01, // displayFlags + 0x00, 0x00, // horizontal + vertical justification + 0x00, 0x00, 0x00, 0x00, // bgColourRed/Green/Blue/Alpha + // BoxRecord + 0x00, 0x00, 0x00, 0x00, // defTextBoxTop/Left + 0x00, 0x00, 0x00, 0x00, // defTextBoxBottom/Right + // StyleRecord + 0x00, 0x00, 0x00, 0x00, // startChar + endChar + 0x00, 0x01, // fontID + 0x00, 0x00, // fontStyleFlags + fontSize + 0x00, 0x00, 0x00, 0x00, // fgColourRed/Green/Blue/Alpha + // FontTableBox + 0x00, 0x00, 0x00, 0x0D, // box size + 'f', 't', 'a', 'b', // box atom name + 0x00, 0x01, // entry count + // FontRecord + 0x00, 0x01, // font ID + 0x00, // font name length + }; + struct mov_sample_entry_t* subtitle; subtitle = &track->stsd.entries[0]; @@ -185,12 +207,12 @@ int mov_add_subtitle(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, u assert(0 != mov_object_to_tag(object)); track->tag = mov_object_to_tag(object); - track->handler_type = MOV_SBTL; + track->handler_type = track->tag == MOV_TAG('t', 'e', 'x', 't') ? MOV_TEXT : MOV_SBTL; track->handler_descr = "SubtitleHandler"; track->stsd.entry_count = 1; track->offset = 0; - track->tkhd.flags = MOV_TKHD_FLAG_TRACK_ENABLE | MOV_TKHD_FLAG_TRACK_IN_MOVIE; + track->tkhd.flags = (track->tag == MOV_TAG('t', 'e', 'x', 't') ? 0 : MOV_TKHD_FLAG_TRACK_ENABLE) | MOV_TKHD_FLAG_TRACK_IN_MOVIE; track->tkhd.track_ID = mvhd->next_track_ID; track->tkhd.creation_time = mvhd->creation_time; track->tkhd.modification_time = mvhd->modification_time; @@ -205,6 +227,12 @@ int mov_add_subtitle(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, u track->mdhd.language = 0x55c4; track->mdhd.duration = 0; // placeholder + if (object == MOV_OBJECT_CHAPTER && 0 == extra_data_size) + { + extra_data = chapter_extra_data; + extra_data_size = sizeof(chapter_extra_data); + } + subtitle->extra_data = malloc(extra_data_size + 1); if (NULL == subtitle->extra_data) return -ENOMEM; @@ -268,6 +296,10 @@ size_t mov_write_minf(const struct mov_t* mov) { size += mov_write_smhd(mov); } + else if (MOV_TEXT == track->handler_type) + { + size += mov_write_gmhd(mov); + } else if (MOV_SUBT == track->handler_type || MOV_SBTL == track->handler_type) { size += mov_write_nmhd(mov); @@ -312,8 +344,9 @@ size_t mov_write_trak(const struct mov_t* mov) mov_buffer_write(&mov->io, "trak", 4); size += mov_write_tkhd(mov); - //size += mov_write_tref(mov); size += mov_write_edts(mov); + if(mov->track->chpl_track != 0) + size += mov_write_tref(mov); size += mov_write_mdia(mov); mov_write_size(mov, offset, size); /* update size */ @@ -338,3 +371,15 @@ size_t mov_write_edts(const struct mov_t* mov) mov_write_size(mov, offset, size); /* update size */ return size; } + +size_t mov_write_tref(const struct mov_t* mov) +{ + mov_buffer_w32(&mov->io, 20); /* size */ + mov_buffer_write(&mov->io, "tref", 4); + + mov_buffer_w32(&mov->io, 12); /* size */ + mov_buffer_write(&mov->io, "chap", 4); + mov_buffer_w32(&mov->io, mov->track->chpl_track); + + return 20; +} diff --git a/libmov/source/mov-udta.c b/libmov/source/mov-udta.c index 2b861cbf..5129b761 100644 --- a/libmov/source/mov-udta.c +++ b/libmov/source/mov-udta.c @@ -26,6 +26,7 @@ int mov_udta_meta_write(const struct mov_udta_meta_t* meta, void* data, int byte struct mov_memory_buffer_t ptr; uint64_t pmeta, pilst, n; + ptr.bytes = 0; ptr.maxsize = bytes; ptr.capacity = bytes; ptr.off = 0; @@ -72,3 +73,76 @@ int mov_udta_meta_write(const struct mov_udta_meta_t* meta, void* data, int byte return (int)ptr.bytes; } + +int mov_udta_chapter_write(const struct mov_udta_chapter_t* chapters, int count, void* data, int bytes) +{ + struct mov_ioutil_t w; + struct mov_memory_buffer_t ptr; + uint64_t pmeta, pilst, pchpl, n; + int i; + static const uint8_t ilst[] = { 0x00, 0x00, 0x00, 0x25, 0xa9, 0x74, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x1d, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x76, 0x66, 0x36, 0x30, 0x2e, 0x32, 0x30, 0x2e, 0x31, 0x30, 0x30 }; + + ptr.bytes = 0; + ptr.maxsize = bytes; + ptr.capacity = bytes; + ptr.off = 0; + ptr.ptr = (uint8_t*)data; + memset(&w, 0, sizeof(w)); + memcpy(&w.io, mov_memory_buffer(), sizeof(w.io)); + w.param = &ptr; + + pmeta = mov_buffer_tell(&w); + mov_buffer_w32(&w, 0); // placeholder + mov_buffer_write(&w, "meta", 4); + mov_buffer_w32(&w, 0); /* version & flags */ + + mov_buffer_w32(&w, 33); + mov_buffer_write(&w, "hdlr", 4); + mov_buffer_w32(&w, 0); /* version & flags */ + mov_buffer_w32(&w, 0); + mov_buffer_write(&w, "mdir", 4); + mov_buffer_write(&w, "appl", 4); + mov_buffer_w32(&w, 0); + mov_buffer_w32(&w, 0); + mov_buffer_w8(&w, 0); + + mov_buffer_w32(&w, 0x2d); // placeholder + mov_buffer_write(&w, "ilst", 4); + mov_buffer_write(&w, ilst, sizeof(ilst)); + + // update meta box size + n = mov_buffer_tell(&w); + mov_buffer_seek(&w, pmeta); + mov_buffer_w32(&w, (uint32_t)(n - pmeta)); + mov_buffer_seek(&w, n); // rewind + + pchpl = mov_buffer_tell(&w); + mov_buffer_w32(&w, 0); // placeholder + mov_buffer_write(&w, "chpl", 4); + mov_buffer_w8(&w, 1); /* version */ + mov_buffer_w24(&w, 0); /* flags */ + mov_buffer_w32(&w, 0); /* unknown*/ + + mov_buffer_w8(&w, (uint8_t)count); + for (i = 0; i < count; i++) + { + mov_buffer_w64(&w, chapters[i].timestamp); + if (chapters[i].title && *chapters[i].title && strlen(chapters[i].title) <= 255) + { + mov_buffer_w8(&w, (uint8_t)strlen(chapters[i].title)); + mov_buffer_write(&w, chapters[i].title, strlen(chapters[i].title)); + } + else + { + mov_buffer_w8(&w, 0); + } + } + + // update chpl box size + n = mov_buffer_tell(&w); + mov_buffer_seek(&w, pchpl); + mov_buffer_w32(&w, (uint32_t)(n - pchpl)); + mov_buffer_seek(&w, n); // rewind + + return (int)ptr.bytes; +} diff --git a/libmov/source/mov-writer.c b/libmov/source/mov-writer.c index d760e098..2d3df742 100644 --- a/libmov/source/mov-writer.c +++ b/libmov/source/mov-writer.c @@ -318,6 +318,7 @@ int mov_writer_add_subtitle(struct mov_writer_t* writer, uint8_t object, const v { struct mov_t* mov; struct mov_track_t* track; + uint32_t i; mov = &writer->mov; track = mov_add_track(mov); @@ -327,6 +328,9 @@ int mov_writer_add_subtitle(struct mov_writer_t* writer, uint8_t object, const v if (0 != mov_add_subtitle(track, &mov->mvhd, 1000, object, extra_data, extra_data_size)) return -ENOMEM; + for (i = 1; object == MOV_OBJECT_CHAPTER && i < mov->mvhd.next_track_ID; i++) + mov->tracks[i - 1].chpl_track = mov->mvhd.next_track_ID; + mov->mvhd.next_track_ID++; return mov->track_count++; } diff --git a/libmov/test/mov-writer-test.cpp b/libmov/test/mov-writer-test.cpp index 529794d6..08158f09 100644 --- a/libmov/test/mov-writer-test.cpp +++ b/libmov/test/mov-writer-test.cpp @@ -118,6 +118,21 @@ static int mov_writer_add_cover(mov_writer_t* mov, const char* cover) return mov_writer_add_udta(mov, s_udta, n); } +static int mov_writer_add_chapter(mov_writer_t* mov) +{ + struct mov_udta_chapter_t chapters[] = { + {0 * 10000000, "chapter 01"}, + {10 * 10000000, "chapter 02"}, + {20 * 10000000, "chapter 03"}, + {30 * 10000000, "chapter 04"}, + {40 * 10000000, "chapter 05"}, + }; + static uint8_t s_udta[1024]; + int n = mov_udta_chapter_write(chapters, sizeof(chapters) / sizeof(chapters[0]), s_udta, sizeof(s_udta)); + assert(n < sizeof(s_udta)); // check buffer size + return mov_writer_add_udta(mov, s_udta, n); +} + void mov_writer_test(int w, int h, const char* inflv, const char* outmp4) { int r, type; @@ -127,7 +142,6 @@ void mov_writer_test(int w, int h, const char* inflv, const char* outmp4) FILE* fp = fopen(outmp4, "wb+"); void* flv = flv_reader_create(inflv); mov_writer_t* mov = mov_writer_create(mov_file_buffer(), fp, MOV_FLAG_FASTSTART); - mov_writer_add_cover(mov, "cover.jpg"); s_width = w; s_height = h; @@ -137,6 +151,9 @@ void mov_writer_test(int w, int h, const char* inflv, const char* outmp4) assert(r >= 0); } + //mov_writer_add_cover(mov, "cover.jpg"); + //mov_writer_add_chapter(mov); + mov_writer_destroy(mov); flv_reader_destroy(flv); fclose(fp);