typedef struct mkv_track {
int write_dts;
int has_cue;
+ int sample_rate;
+ int64_t sample_rate_offset;
int64_t codecpriv_offset;
int64_t ts_offset;
} mkv_track;
return 0;
}
-static int get_aac_sample_rates(AVFormatContext *s, AVCodecParameters *par,
+static int get_aac_sample_rates(AVFormatContext *s, uint8_t *extradata, int extradata_size,
int *sample_rate, int *output_sample_rate)
{
MPEG4AudioConfig mp4ac;
+ int ret;
- if (avpriv_mpeg4audio_get_config(&mp4ac, par->extradata,
- par->extradata_size * 8, 1) < 0) {
+ ret = avpriv_mpeg4audio_get_config(&mp4ac, extradata,
+ extradata_size * 8, 1);
+ /* Don't abort if the failure is because of missing extradata. Assume in that
+ * case a bitstream filter will provide the muxer with the extradata in the
+ * first packet.
+ * Abort however if s->pb is not seekable, as we would not be able to seek back
+ * to write the sample rate elements once the extradata shows up, anyway. */
+ if (ret < 0 && (extradata_size || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL))) {
av_log(s, AV_LOG_ERROR,
"Error parsing AAC extradata, unable to determine samplerate.\n");
return AVERROR(EINVAL);
}
- *sample_rate = mp4ac.sample_rate;
- *output_sample_rate = mp4ac.ext_sample_rate;
+ if (ret < 0) {
+ /* This will only happen when this function is called while writing the
+ * header and no extradata is available. The space for this element has
+ * to be reserved for when this function is called again after the
+ * extradata shows up in the first packet, as there's no way to know if
+ * output_sample_rate will be different than sample_rate or not. */
+ *output_sample_rate = *sample_rate;
+ } else {
+ *sample_rate = mp4ac.sample_rate;
+ *output_sample_rate = mp4ac.ext_sample_rate;
+ }
return 0;
}
-static int mkv_write_native_codecprivate(AVFormatContext *s,
+static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb,
AVCodecParameters *par,
AVIOContext *dyn_cp)
{
avio_write(dyn_cp, par->extradata + 12,
par->extradata_size - 12);
break;
+ case AV_CODEC_ID_AAC:
+ if (par->extradata_size)
+ avio_write(dyn_cp, par->extradata, par->extradata_size);
+ else
+ put_ebml_void(pb, MAX_PCE_SIZE + 2 + 4);
+ break;
default:
if (par->codec_id == AV_CODEC_ID_PRORES &&
ff_codec_get_id(ff_codec_movvideo_tags, par->codec_tag) == AV_CODEC_ID_PRORES) {
return ret;
if (native_id) {
- ret = mkv_write_native_codecprivate(s, par, dyn_cp);
+ ret = mkv_write_native_codecprivate(s, pb, par, dyn_cp);
} else if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
if (qt_id) {
if (!par->codec_tag)
ret = AVERROR(EINVAL);
}
- ff_put_bmp_header(dyn_cp, par, ff_codec_bmp_tags, 0, 0);
+ ff_put_bmp_header(dyn_cp, par, 0, 0);
}
} else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
unsigned int tag;
}
if (par->codec_id == AV_CODEC_ID_AAC) {
- ret = get_aac_sample_rates(s, par, &sample_rate, &output_sample_rate);
+ ret = get_aac_sample_rates(s, par->extradata, par->extradata_size, &sample_rate,
+ &output_sample_rate);
if (ret < 0)
return ret;
}
subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0);
put_ebml_uint (pb, MATROSKA_ID_AUDIOCHANNELS , par->channels);
+
+ mkv->tracks[i].sample_rate_offset = avio_tell(pb);
put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate);
if (output_sample_rate)
put_ebml_float(pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate);
if (ret < 0) return ret;
editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0);
- put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1);
- put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGHIDDEN , 0);
+ if (mkv->mode != MODE_WEBM) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGHIDDEN , 0);
+ }
for (i = 0; i < s->nb_chapters; i++) {
ebml_master chapteratom, chapterdisplay;
AVChapter *c = s->chapters[i];
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID, c->id + mkv->chapter_id_offset);
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMESTART, chapterstart);
put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMEEND, chapterend);
- put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0);
- put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGENABLED, 1);
+ if (mkv->mode != MODE_WEBM) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGENABLED, 1);
+ }
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);
}
}
- for (i = 0; i < s->nb_chapters; i++) {
- AVChapter *ch = s->chapters[i];
+ 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;
+ if (!mkv_check_tag(ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID))
+ continue;
- ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset, &mkv->tags);
- if (ret < 0) return ret;
+ ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset, &mkv->tags);
+ if (ret < 0)
+ return ret;
+ }
}
- if (mkv->have_attachments) {
+ if (mkv->have_attachments && mkv->mode != MODE_WEBM) {
for (i = 0; i < mkv->attachments->num_entries; i++) {
mkv_attachment *attachment = &mkv->attachments->entries[i];
AVStream *st = s->streams[attachment->stream_idx];
for (i = 0; i < s->nb_chapters; i++)
mkv->chapter_id_offset = FFMAX(mkv->chapter_id_offset, 1LL - s->chapters[i]->id);
- if (mkv->mode != MODE_WEBM) {
- ret = mkv_write_chapters(s);
- if (ret < 0)
- goto fail;
+ ret = mkv_write_chapters(s);
+ if (ret < 0)
+ goto fail;
+ if (mkv->mode != MODE_WEBM) {
ret = mkv_write_attachments(s);
if (ret < 0)
goto fail;
-
- ret = mkv_write_tags(s);
- if (ret < 0)
- goto fail;
}
+ ret = mkv_write_tags(s);
+ if (ret < 0)
+ goto fail;
+
if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
mkv_write_seekhead(pb, mkv);
uint8_t track_number = (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1));
ebml_master block_group, block_additions, block_more;
+ ts += mkv->tracks[pkt->stream_index].ts_offset;
+
av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
"pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", keyframe %d\n",
avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration,
&side_data_size);
switch (par->codec_id) {
+ case AV_CODEC_ID_AAC:
+ if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
+ int filler, output_sample_rate = 0;
+ int64_t curpos;
+ ret = get_aac_sample_rates(s, side_data, side_data_size, &track->sample_rate,
+ &output_sample_rate);
+ if (ret < 0)
+ return ret;
+ if (!output_sample_rate)
+ output_sample_rate = track->sample_rate; // Space is already reserved, so it's this or a void element.
+ av_freep(&par->extradata);
+ ret = ff_alloc_extradata(par, side_data_size);
+ if (ret < 0)
+ return ret;
+ memcpy(par->extradata, side_data, side_data_size);
+ curpos = avio_tell(mkv->tracks_bc);
+ avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET);
+ mkv_write_codecprivate(s, mkv->tracks_bc, par, 1, 0);
+ filler = MAX_PCE_SIZE + 2 + 4 - (avio_tell(mkv->tracks_bc) - track->codecpriv_offset);
+ if (filler)
+ put_ebml_void(mkv->tracks_bc, filler);
+ avio_seek(mkv->tracks_bc, track->sample_rate_offset, SEEK_SET);
+ put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate);
+ put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate);
+ avio_seek(mkv->tracks_bc, curpos, SEEK_SET);
+ } else if (!par->extradata_size && !track->sample_rate) {
+ // No extradata (codecpar or packet side data).
+ av_log(s, AV_LOG_ERROR, "Error parsing AAC extradata, unable to determine samplerate.\n");
+ return AVERROR(EINVAL);
+ }
+ break;
case AV_CODEC_ID_FLAC:
if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
AVCodecParameters *codecpriv_par;
end_ebml_master_crc32(pb, &mkv->dyn_bc, mkv, mkv->cluster);
}
- if (mkv->mode != MODE_WEBM) {
- ret = mkv_write_chapters(s);
- if (ret < 0)
- return ret;
- }
+ ret = mkv_write_chapters(s);
+ if (ret < 0)
+ return ret;
+
if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
if (mkv->cues->num_entries) {