diff options
Diffstat (limited to 'libavformat/matroskaenc.c')
-rw-r--r-- | libavformat/matroskaenc.c | 73 |
1 files changed, 67 insertions, 6 deletions
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 01e6e79f3f..2b2a4650da 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -81,6 +81,8 @@ typedef struct MatroskaMuxContext { mkv_track *tracks; struct AVMD5 *md5_ctx; + unsigned int audio_buffer_size; + AVPacket cur_audio_pkt; } MatroskaMuxContext; @@ -746,6 +748,10 @@ static int mkv_write_header(AVFormatContext *s) if (mkv->cues == NULL) return AVERROR(ENOMEM); + av_init_packet(&mkv->cur_audio_pkt); + mkv->cur_audio_pkt.size = 0; + mkv->audio_buffer_size = 0; + put_flush_packet(pb); return 0; } @@ -861,7 +867,7 @@ static void mkv_flush_dynbuf(AVFormatContext *s) mkv->dyn_bc = NULL; } -static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) +static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; ByteIOContext *pb = s->pb; @@ -910,9 +916,38 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; } - // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming - if ((url_is_streamed(s->pb) && (url_ftell(pb) > 32*1024 || ts > mkv->cluster_pts + 1000)) - || url_ftell(pb) > mkv->cluster_pos + 5*1024*1024 || ts > mkv->cluster_pts + 5000) { + mkv->duration = FFMAX(mkv->duration, ts + duration); + return 0; +} + +static int mkv_copy_packet(MatroskaMuxContext *mkv, const AVPacket *pkt) +{ + uint8_t *data = mkv->cur_audio_pkt.data; + mkv->cur_audio_pkt = *pkt; + mkv->cur_audio_pkt.data = av_fast_realloc(data, &mkv->audio_buffer_size, pkt->size); + if (!mkv->cur_audio_pkt.data) + return AVERROR(ENOMEM); + + memcpy(mkv->cur_audio_pkt.data, pkt->data, pkt->size); + mkv->cur_audio_pkt.size = pkt->size; + return 0; +} + +static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + ByteIOContext *pb = url_is_streamed(s->pb) ? mkv->dyn_bc : s->pb; + AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + int ret, keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY); + int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + int cluster_size = url_ftell(pb) - (url_is_streamed(s->pb) ? 0 : mkv->cluster_pos); + + // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or + // after 4k and on a keyframe + if (mkv->cluster_pos && + ((url_is_streamed(s->pb) && (cluster_size > 32*1024 || ts > mkv->cluster_pts + 1000)) + || cluster_size > 5*1024*1024 || ts > mkv->cluster_pts + 5000 + || (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe && cluster_size > 4*1024))) { av_log(s, AV_LOG_DEBUG, "Starting new cluster at offset %" PRIu64 " bytes, pts %" PRIu64 "\n", url_ftell(pb), ts); end_ebml_master(pb, mkv->cluster); @@ -921,8 +956,23 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) mkv_flush_dynbuf(s); } - mkv->duration = FFMAX(mkv->duration, ts + duration); - return 0; + // check if we have an audio packet cached + if (mkv->cur_audio_pkt.size > 0) { + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + mkv->cur_audio_pkt.size = 0; + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); + return ret; + } + } + + // buffer an audio packet to ensure the packet containing the video + // keyframe's timecode is contained in the same cluster for WebM + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) + ret = mkv_copy_packet(mkv, pkt); + else + ret = mkv_write_packet_internal(s, pkt); + return ret; } static int mkv_write_trailer(AVFormatContext *s) @@ -932,6 +982,16 @@ static int mkv_write_trailer(AVFormatContext *s) int64_t currentpos, second_seekhead, cuespos; int ret; + // check if we have an audio packet cached + if (mkv->cur_audio_pkt.size > 0) { + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + mkv->cur_audio_pkt.size = 0; + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); + return ret; + } + } + if (mkv->dyn_bc) { end_ebml_master(mkv->dyn_bc, mkv->cluster); mkv_flush_dynbuf(s); @@ -970,6 +1030,7 @@ static int mkv_write_trailer(AVFormatContext *s) end_ebml_master(pb, mkv->segment); av_free(mkv->md5_ctx); av_free(mkv->tracks); + av_destruct_packet(&mkv->cur_audio_pkt); put_flush_packet(pb); return 0; } |