diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index cfced72ada..55932373f5 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -81,6 +81,8 @@ typedef struct mkv_cues { typedef struct mkv_track { int write_dts; + int sample_rate; + int64_t sample_rate_offset; int64_t ts_offset; } mkv_track; @@ -521,20 +523,36 @@ static int put_flac_codecpriv(AVFormatContext *s, 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; } @@ -790,7 +808,8 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, bit_depth = av_get_bytes_per_sample(par->format) << 3; 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; } @@ -890,6 +909,8 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, 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); @@ -909,6 +930,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n"); break; } + ret = mkv_write_codecprivate(s, pb, par, native_id, qt_id); if (ret < 0) return ret; @@ -1531,6 +1553,48 @@ static void mkv_flush_dynbuf(AVFormatContext *s) mkv->dyn_bc = NULL; } +static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; + mkv_track *track = &mkv->tracks[pkt->stream_index]; + uint8_t *side_data; + int side_data_size = 0, ret; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &side_data_size); + + switch (par->codec_id) { + case AV_CODEC_ID_AAC: + if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { + int 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. + curpos = avio_tell(s->pb); + avio_seek(s->pb, track->sample_rate_offset, SEEK_SET); + put_ebml_float(s->pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate); + put_ebml_float(s->pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); + avio_seek(s->pb, 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; + default: + if (side_data_size) + av_log(s, AV_LOG_DEBUG, "Ignoring new extradata in a packet for stream %d.\n", pkt->stream_index); + break; + } + + return 0; +} + static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; @@ -1605,6 +1669,10 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb; int ret; + ret = mkv_check_new_extra_data(s, pkt); + if (ret < 0) + return ret; + if (mkv->tracks[pkt->stream_index].write_dts) cluster_time = pkt->dts - mkv->cluster_pts; else