+static int mkv_new_chapter_ids_needed(const AVFormatContext *s)
+{
+ for (unsigned i = 0; i < s->nb_chapters; i++) {
+ if (!s->chapters[i]->id)
+ return 1;
+ for (unsigned j = 0; j < i; j++)
+ if (s->chapters[j]->id == s->chapters[i]->id)
+ return 1;
+ }
+ return 0;
+}
+
+static int mkv_write_chapters(AVFormatContext *s)
+{
+ MatroskaMuxContext *mkv = s->priv_data;
+ AVIOContext *dyn_cp = NULL, *dyn_tags = NULL, **tags, *pb = s->pb;
+ ebml_master editionentry;
+ AVRational scale = {1, 1E9};
+ int ret, create_new_ids;
+
+ if (!s->nb_chapters || mkv->wrote_chapters)
+ return 0;
+
+ ret = start_ebml_master_crc32(&dyn_cp, mkv);
+ if (ret < 0)
+ return ret;
+
+ editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0);
+ 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;
+
+ create_new_ids = mkv_new_chapter_ids_needed(s);
+
+ for (unsigned i = 0; i < s->nb_chapters; i++) {
+ ebml_master chapteratom, chapterdisplay;
+ const AVChapter *c = s->chapters[i];
+ int64_t chapterstart = av_rescale_q(c->start, c->time_base, scale);
+ int64_t chapterend = av_rescale_q(c->end, c->time_base, scale);
+ const AVDictionaryEntry *t;
+ uint64_t uid = create_new_ids ? i + 1ULL : c->id;
+ if (chapterstart < 0 || chapterstart > chapterend || chapterend < 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Invalid chapter start (%"PRId64") or end (%"PRId64").\n",
+ chapterstart, chapterend);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ chapteratom = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERATOM, 0);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID, uid);
+ 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))) {
+ chapterdisplay = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERDISPLAY, 0);
+ put_ebml_string(dyn_cp, MATROSKA_ID_CHAPSTRING, t->value);
+ put_ebml_string(dyn_cp, MATROSKA_ID_CHAPLANG , "und");
+ 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, uid);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+ end_ebml_master(dyn_cp, editionentry);
+ mkv->wrote_chapters = 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;
+}
+