|
@@ -55,8 +55,8 @@
|
|
|
#include "libavcodec/mpeg4audio.h"
|
|
|
|
|
|
/* Level 1 elements we create a SeekHead entry for:
|
|
|
- * Info, Tracks, Chapters, Attachments, Tags and Cues */
|
|
|
-#define MAX_SEEKHEAD_ENTRIES 6
|
|
|
+ * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */
|
|
|
+#define MAX_SEEKHEAD_ENTRIES 7
|
|
|
|
|
|
#define IS_SEEKABLE(pb, mkv) (((pb)->seekable & AVIO_SEEKABLE_NORMAL) && \
|
|
|
!(mkv)->is_live)
|
|
@@ -142,8 +142,8 @@ typedef struct MatroskaMuxContext {
|
|
|
unsigned nb_attachments;
|
|
|
int have_video;
|
|
|
|
|
|
- uint32_t chapter_id_offset;
|
|
|
int wrote_chapters;
|
|
|
+ int wrote_tags;
|
|
|
|
|
|
int reserve_cues_space;
|
|
|
int cluster_size_limit;
|
|
@@ -1546,6 +1546,8 @@ static int mkv_write_tags(AVFormatContext *s)
|
|
|
ebml_master tag, *tagp = IS_SEEKABLE(s->pb, mkv) ? &tag : NULL;
|
|
|
int i, ret;
|
|
|
|
|
|
+ mkv->wrote_tags = 1;
|
|
|
+
|
|
|
ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL);
|
|
|
|
|
|
if (mkv_check_tag(s->metadata, 0)) {
|
|
@@ -1586,21 +1588,6 @@ static int mkv_write_tags(AVFormatContext *s)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (mkv->mode != MODE_WEBM) {
|
|
|
- for (i = 0; i < s->nb_chapters; i++) {
|
|
|
- AVChapter *ch = s->chapters[i];
|
|
|
-
|
|
|
- if (!mkv_check_tag(ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID))
|
|
|
- continue;
|
|
|
-
|
|
|
- ret = mkv_write_tag(mkv, ch->metadata, &mkv->tags.bc, NULL,
|
|
|
- MATROSKA_ID_TAGTARGETS_CHAPTERUID,
|
|
|
- (uint32_t)ch->id + (uint64_t)mkv->chapter_id_offset);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
if (mkv->nb_attachments && mkv->mode != MODE_WEBM) {
|
|
|
for (i = 0; i < s->nb_streams; i++) {
|
|
|
const mkv_track *track = &mkv->tracks[i];
|
|
@@ -1629,8 +1616,9 @@ static int mkv_write_tags(AVFormatContext *s)
|
|
|
static int mkv_write_chapters(AVFormatContext *s)
|
|
|
{
|
|
|
MatroskaMuxContext *mkv = s->priv_data;
|
|
|
- AVIOContext *dyn_cp = NULL, *pb = s->pb;
|
|
|
+ AVIOContext *dyn_cp = NULL, *dyn_tags = NULL, **tags, *pb = s->pb;
|
|
|
ebml_master editionentry;
|
|
|
+ uint64_t chapter_id_offset = 0;
|
|
|
AVRational scale = {1, 1E9};
|
|
|
int i, ret;
|
|
|
|
|
@@ -1639,7 +1627,7 @@ static int mkv_write_chapters(AVFormatContext *s)
|
|
|
|
|
|
for (i = 0; i < s->nb_chapters; i++)
|
|
|
if (!s->chapters[i]->id) {
|
|
|
- mkv->chapter_id_offset = 1;
|
|
|
+ chapter_id_offset = 1;
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -1648,8 +1636,13 @@ static int mkv_write_chapters(AVFormatContext *s)
|
|
|
return ret;
|
|
|
|
|
|
editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0);
|
|
|
- if (mkv->mode != MODE_WEBM)
|
|
|
+ if (mkv->mode != MODE_WEBM) {
|
|
|
put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1);
|
|
|
+ /* If mkv_write_tags() has already been called, then any tags
|
|
|
+ * corresponding to chapters will be put into a new Tags element. */
|
|
|
+ tags = mkv->wrote_tags ? &dyn_tags : &mkv->tags.bc;
|
|
|
+ } else
|
|
|
+ tags = NULL;
|
|
|
|
|
|
for (i = 0; i < s->nb_chapters; i++) {
|
|
|
ebml_master chapteratom, chapterdisplay;
|
|
@@ -1661,13 +1654,13 @@ static int mkv_write_chapters(AVFormatContext *s)
|
|
|
av_log(s, AV_LOG_ERROR,
|
|
|
"Invalid chapter start (%"PRId64") or end (%"PRId64").\n",
|
|
|
chapterstart, chapterend);
|
|
|
- ffio_free_dyn_buf(&dyn_cp);
|
|
|
- return AVERROR_INVALIDDATA;
|
|
|
+ ret = AVERROR_INVALIDDATA;
|
|
|
+ goto fail;
|
|
|
}
|
|
|
|
|
|
chapteratom = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERATOM, 0);
|
|
|
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID,
|
|
|
- (uint32_t)c->id + (uint64_t)mkv->chapter_id_offset);
|
|
|
+ (uint32_t)c->id + chapter_id_offset);
|
|
|
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMESTART, chapterstart);
|
|
|
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMEEND, chapterend);
|
|
|
if ((t = av_dict_get(c->metadata, "title", NULL, 0))) {
|
|
@@ -1677,12 +1670,34 @@ static int mkv_write_chapters(AVFormatContext *s)
|
|
|
end_ebml_master(dyn_cp, chapterdisplay);
|
|
|
}
|
|
|
end_ebml_master(dyn_cp, chapteratom);
|
|
|
+
|
|
|
+ if (tags && mkv_check_tag(c->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID)) {
|
|
|
+ ret = mkv_write_tag(mkv, c->metadata, tags, NULL,
|
|
|
+ MATROSKA_ID_TAGTARGETS_CHAPTERUID,
|
|
|
+ (uint32_t)c->id + chapter_id_offset);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
}
|
|
|
end_ebml_master(dyn_cp, editionentry);
|
|
|
mkv->wrote_chapters = 1;
|
|
|
|
|
|
- return end_ebml_master_crc32(pb, &dyn_cp, mkv,
|
|
|
- MATROSKA_ID_CHAPTERS, 0, 0, 1);
|
|
|
+ ret = end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS, 0, 0, 1);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail;
|
|
|
+ if (dyn_tags)
|
|
|
+ return end_ebml_master_crc32(pb, &dyn_tags, mkv,
|
|
|
+ MATROSKA_ID_TAGS, 0, 0, 1);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+fail:
|
|
|
+ if (tags) {
|
|
|
+ /* tags == &mkv->tags.bc can only happen if mkv->tags.bc was
|
|
|
+ * initially NULL, so we never free older tags. */
|
|
|
+ ffio_free_dyn_buf(tags);
|
|
|
+ }
|
|
|
+ ffio_free_dyn_buf(&dyn_cp);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static const char *get_mimetype(const AVStream *st)
|
|
@@ -1878,7 +1893,8 @@ static int mkv_write_header(AVFormatContext *s)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- /* Must come after mkv_write_chapters() because of chapter_id_offset */
|
|
|
+ /* Must come after mkv_write_chapters() to write chapter tags
|
|
|
+ * into the same Tags element as the other tags. */
|
|
|
ret = mkv_write_tags(s);
|
|
|
if (ret < 0)
|
|
|
return ret;
|