|  |  | @@ -70,6 +70,7 @@ typedef struct mkv_seekhead { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | typedef struct { | 
		
	
		
			
			|  |  |  | uint64_t        pts; | 
		
	
		
			
			|  |  |  | int             stream_idx; | 
		
	
		
			
			|  |  |  | int             tracknum; | 
		
	
		
			
			|  |  |  | int64_t         cluster_pos;        ///< file offset of the cluster containing the block | 
		
	
		
			
			|  |  |  | int64_t         relative_pos;       ///< relative offset from the position of the cluster containing the block | 
		
	
	
		
			
				|  |  | @@ -114,6 +115,8 @@ typedef struct MatroskaMuxContext { | 
		
	
		
			
			|  |  |  | int cluster_size_limit; | 
		
	
		
			
			|  |  |  | int64_t cues_pos; | 
		
	
		
			
			|  |  |  | int64_t cluster_time_limit; | 
		
	
		
			
			|  |  |  | int is_dash; | 
		
	
		
			
			|  |  |  | int dash_track_number; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | uint32_t chapter_id_offset; | 
		
	
		
			
			|  |  |  | int wrote_chapters; | 
		
	
	
		
			
				|  |  | @@ -399,8 +402,8 @@ static mkv_cues * mkv_start_cues(int64_t segment_offset) | 
		
	
		
			
			|  |  |  | return cues; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t cluster_pos, int64_t relative_pos, | 
		
	
		
			
			|  |  |  | int64_t duration) | 
		
	
		
			
			|  |  |  | static int mkv_add_cuepoint(mkv_cues *cues, int stream, int tracknum, int64_t ts, | 
		
	
		
			
			|  |  |  | int64_t cluster_pos, int64_t relative_pos, int64_t duration) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | mkv_cuepoint *entries = cues->entries; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -413,7 +416,8 @@ static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t clus | 
		
	
		
			
			|  |  |  | cues->entries = entries; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].pts           = ts; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].tracknum      = stream + 1; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].stream_idx    = stream; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].tracknum      = tracknum; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].cluster_pos   = cluster_pos - cues->segment_offset; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries].relative_pos  = relative_pos; | 
		
	
		
			
			|  |  |  | cues->entries[cues->num_entries++].duration    = duration; | 
		
	
	
		
			
				|  |  | @@ -443,7 +447,7 @@ static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, mkv_track *tracks | 
		
	
		
			
			|  |  |  | for (j = 0; j < num_tracks; j++) | 
		
	
		
			
			|  |  |  | tracks[j].has_cue = 0; | 
		
	
		
			
			|  |  |  | for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) { | 
		
	
		
			
			|  |  |  | int tracknum = entry[j].tracknum - 1; | 
		
	
		
			
			|  |  |  | int tracknum = entry[j].stream_idx; | 
		
	
		
			
			|  |  |  | av_assert0(tracknum>=0 && tracknum<num_tracks); | 
		
	
		
			
			|  |  |  | if (tracks[tracknum].has_cue) | 
		
	
		
			
			|  |  |  | continue; | 
		
	
	
		
			
				|  |  | @@ -646,8 +650,10 @@ static int mkv_write_tracks(AVFormatContext *s) | 
		
	
		
			
			|  |  |  | get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); | 
		
	
		
			
			|  |  |  | put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER     , i + 1); | 
		
	
		
			
			|  |  |  | put_ebml_uint (pb, MATROSKA_ID_TRACKUID        , i + 1); | 
		
	
		
			
			|  |  |  | put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER, | 
		
	
		
			
			|  |  |  | mkv->is_dash ? mkv->dash_track_number : i + 1); | 
		
	
		
			
			|  |  |  | put_ebml_uint (pb, MATROSKA_ID_TRACKUID, | 
		
	
		
			
			|  |  |  | mkv->is_dash ? mkv->dash_track_number : i + 1); | 
		
	
		
			
			|  |  |  | put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0);    // no lacing (yet) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) | 
		
	
	
		
			
				|  |  | @@ -1435,7 +1441,8 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | put_ebml_id(pb, blockid); | 
		
	
		
			
			|  |  |  | put_ebml_num(pb, size+4, 0); | 
		
	
		
			
			|  |  |  | avio_w8(pb, 0x80 | (pkt->stream_index + 1));     // this assumes stream_index is less than 126 | 
		
	
		
			
			|  |  |  | // this assumes stream_index is less than 126 | 
		
	
		
			
			|  |  |  | avio_w8(pb, 0x80 | (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1))); | 
		
	
		
			
			|  |  |  | avio_wb16(pb, ts - mkv->cluster_pts); | 
		
	
		
			
			|  |  |  | avio_w8(pb, flags); | 
		
	
		
			
			|  |  |  | avio_write(pb, data + offset, size); | 
		
	
	
		
			
				|  |  | @@ -1549,7 +1556,7 @@ static void mkv_flush_dynbuf(AVFormatContext *s) | 
		
	
		
			
			|  |  |  | mkv->dyn_bc = NULL; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_cue) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | MatroskaMuxContext *mkv = s->priv_data; | 
		
	
		
			
			|  |  |  | AVIOContext *pb = s->pb; | 
		
	
	
		
			
				|  |  | @@ -1606,8 +1613,13 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | end_ebml_master(pb, blockgroup); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if ((codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) || codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { | 
		
	
		
			
			|  |  |  | ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts, mkv->cluster_pos, relative_packet_pos, | 
		
	
		
			
			|  |  |  | if ((codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) || | 
		
	
		
			
			|  |  |  | codec->codec_type == AVMEDIA_TYPE_SUBTITLE || | 
		
	
		
			
			|  |  |  | add_cue) { | 
		
	
		
			
			|  |  |  | ret = mkv_add_cuepoint(mkv->cues, | 
		
	
		
			
			|  |  |  | pkt->stream_index, | 
		
	
		
			
			|  |  |  | mkv->is_dash ? mkv->dash_track_number : pkt->stream_index + 1, | 
		
	
		
			
			|  |  |  | ts, mkv->cluster_pos, relative_packet_pos, | 
		
	
		
			
			|  |  |  | codec->codec_type == AVMEDIA_TYPE_SUBTITLE ? duration : -1); | 
		
	
		
			
			|  |  |  | if (ret < 0) return ret; | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -1625,6 +1637,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | int64_t cluster_time; | 
		
	
		
			
			|  |  |  | AVIOContext *pb; | 
		
	
		
			
			|  |  |  | int ret; | 
		
	
		
			
			|  |  |  | int start_new_cluster; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (mkv->tracks[pkt->stream_index].write_dts) | 
		
	
		
			
			|  |  |  | cluster_time = pkt->dts - mkv->cluster_pts; | 
		
	
	
		
			
				|  |  | @@ -1642,11 +1655,26 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | cluster_size = avio_tell(pb); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (mkv->cluster_pos != -1 && | 
		
	
		
			
			|  |  |  | (cluster_size > mkv->cluster_size_limit || | 
		
	
		
			
			|  |  |  | cluster_time > mkv->cluster_time_limit || | 
		
	
		
			
			|  |  |  | (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && | 
		
	
		
			
			|  |  |  | cluster_size > 4 * 1024))) { | 
		
	
		
			
			|  |  |  | if (mkv->is_dash && codec_type == AVMEDIA_TYPE_VIDEO) { | 
		
	
		
			
			|  |  |  | // WebM DASH specification states that the first block of every cluster | 
		
	
		
			
			|  |  |  | // has to be a key frame. So for DASH video, we only create a cluster | 
		
	
		
			
			|  |  |  | // on seeing key frames. | 
		
	
		
			
			|  |  |  | start_new_cluster = keyframe; | 
		
	
		
			
			|  |  |  | } else if (mkv->is_dash && codec_type == AVMEDIA_TYPE_AUDIO && | 
		
	
		
			
			|  |  |  | cluster_time > mkv->cluster_time_limit) { | 
		
	
		
			
			|  |  |  | // For DASH audio, we create a Cluster based on cluster_time_limit | 
		
	
		
			
			|  |  |  | start_new_cluster = 1; | 
		
	
		
			
			|  |  |  | } else if (!mkv->is_dash && | 
		
	
		
			
			|  |  |  | (cluster_size > mkv->cluster_size_limit || | 
		
	
		
			
			|  |  |  | cluster_time > mkv->cluster_time_limit || | 
		
	
		
			
			|  |  |  | (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && | 
		
	
		
			
			|  |  |  | cluster_size > 4 * 1024))) { | 
		
	
		
			
			|  |  |  | start_new_cluster = 1; | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | start_new_cluster = 0; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (mkv->cluster_pos != -1 && start_new_cluster) { | 
		
	
		
			
			|  |  |  | av_log(s, AV_LOG_DEBUG, "Starting new cluster at offset %" PRIu64 | 
		
	
		
			
			|  |  |  | " bytes, pts %" PRIu64 "dts %" PRIu64 "\n", | 
		
	
		
			
			|  |  |  | avio_tell(pb), pkt->pts, pkt->dts); | 
		
	
	
		
			
				|  |  | @@ -1659,7 +1687,9 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // 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); | 
		
	
		
			
			|  |  |  | // for DASH audio, a CuePoint has to be added when there is a new cluster. | 
		
	
		
			
			|  |  |  | ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, | 
		
	
		
			
			|  |  |  | mkv->is_dash ? start_new_cluster : 0); | 
		
	
		
			
			|  |  |  | av_free_packet(&mkv->cur_audio_pkt); | 
		
	
		
			
			|  |  |  | if (ret < 0) { | 
		
	
		
			
			|  |  |  | av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); | 
		
	
	
		
			
				|  |  | @@ -1680,7 +1710,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) | 
		
	
		
			
			|  |  |  | ret = av_copy_packet_side_data(&mkv->cur_audio_pkt, &mkv->cur_audio_pkt); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } else | 
		
	
		
			
			|  |  |  | ret = mkv_write_packet_internal(s, pkt); | 
		
	
		
			
			|  |  |  | ret = mkv_write_packet_internal(s, pkt, 0); | 
		
	
		
			
			|  |  |  | return ret; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -1716,7 +1746,7 @@ static int mkv_write_trailer(AVFormatContext *s) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // 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); | 
		
	
		
			
			|  |  |  | ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, 0); | 
		
	
		
			
			|  |  |  | av_free_packet(&mkv->cur_audio_pkt); | 
		
	
		
			
			|  |  |  | if (ret < 0) { | 
		
	
		
			
			|  |  |  | av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); | 
		
	
	
		
			
				|  |  | @@ -1831,6 +1861,8 @@ static const AVOption options[] = { | 
		
	
		
			
			|  |  |  | { "reserve_index_space", "Reserve a given amount of space (in bytes) at the beginning of the file for the index (cues).", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT,   { .i64 = 0 },   0, INT_MAX,   FLAGS }, | 
		
	
		
			
			|  |  |  | { "cluster_size_limit",  "Store at most the provided amount of bytes in a cluster. ",                                     OFFSET(cluster_size_limit), AV_OPT_TYPE_INT  , { .i64 = -1 }, -1, INT_MAX,   FLAGS }, | 
		
	
		
			
			|  |  |  | { "cluster_time_limit",  "Store at most the provided number of milliseconds in a cluster.",                               OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, | 
		
	
		
			
			|  |  |  | { "dash", "Create a WebM file conforming to WebM DASH specification", OFFSET(is_dash), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, | 
		
	
		
			
			|  |  |  | { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 127, FLAGS }, | 
		
	
		
			
			|  |  |  | { NULL }, | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | 
 |