summaryrefslogtreecommitdiff
path: root/libavformat/matroskaenc.c
diff options
context:
space:
mode:
authorJames Zern <jzern@google.com>2010-06-04 22:40:50 +0000
committerDavid Conrad <lessen42@gmail.com>2010-06-04 22:40:50 +0000
commitac9baa716b8369d7df225f25f9e5eee7bd512b80 (patch)
treeeb7b2b842917b57e6949634869850fe9b0d57e8d /libavformat/matroskaenc.c
parent3ad0b189f74e2f1beee2f4d1a4a99b16689b866a (diff)
matroskaenc: Mux clusters better
Start them on keyframes when reasonable, and delay writing audio packets to help ensure that there's audio samples available for the first frame in clusters. Patch by James Zern <jzern at google> Originally committed as revision 23473 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavformat/matroskaenc.c')
-rw-r--r--libavformat/matroskaenc.c73
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;
}