From 2f8386388329aee07197b885c1d1d65eba81c6b1 Mon Sep 17 00:00:00 2001 From: mosu Date: Fri, 26 Nov 2004 16:36:03 +0000 Subject: Added support for MPEG-1 and MPEG-2 in Matroska. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@14055 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpdemux/demux_mkv.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 4 deletions(-) (limited to 'libmpdemux/demux_mkv.c') diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 889c25c09e..233b76a0aa 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -92,6 +92,13 @@ typedef struct mkv_track int subtitle_type; + /* The timecodes of video frames might have to be reordered if they're + in display order (the timecodes, not the frames themselves!). In this + case demux packets have to be cached with the help of these variables. */ + int reorder_timecodes; + demux_packet_t **cached_dps; + int num_cached_dps, num_allocated_dps; + /* generic content encoding support */ mkv_content_encoding_t *encodings; int num_encodings; @@ -429,6 +436,33 @@ vobsub_parse_forced_subs (mkv_track_t *t, const char *start) return 8; } +/** \brief Free cached demux packets + * + * Reordering the timecodes requires caching of demux packets. This function + * frees all these cached packets and the memory for the cached pointers + * itself. + * + * \param demuxer The demuxer for which the cache is to be freed. + */ +static void +free_cached_dps (demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + mkv_track_t *track; + int i, k; + + for (k = 0; k < mkv_d->num_tracks; k++) + { + track = mkv_d->tracks[k]; + for (i = 0; i < track->num_cached_dps; i++) + free_demux_packet (track->cached_dps[i]); + free(track->cached_dps); + track->cached_dps = NULL; + track->num_cached_dps = 0; + track->num_allocated_dps = 0; + } +} + static int demux_mkv_parse_idx (mkv_track_t *t) { @@ -1626,6 +1660,16 @@ demux_mkv_open_video (demuxer_t *demuxer, mkv_track_t *track) #endif /* USE_QTX_CODECS */ } + else if (!strcmp(track->codec_id, MKV_V_MPEG1)) + { + bih->biCompression = mmioFOURCC('m', 'p', 'g', '1'); + track->reorder_timecodes = 1; + } + else if (!strcmp(track->codec_id, MKV_V_MPEG2)) + { + bih->biCompression = mmioFOURCC('m', 'p', 'g', '2'); + track->reorder_timecodes = 1; + } else { mp_msg (MSGT_DEMUX,MSGL_WARN,"[mkv] Unknown/unsupported CodecID " @@ -2344,6 +2388,7 @@ demux_close_mkv (demuxer_t *demuxer) if (mkv_d) { int i; + free_cached_dps (demuxer); if (mkv_d->tracks) { for (i=0; inum_tracks; i++) @@ -2739,9 +2784,89 @@ handle_realaudio (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer, ds_add_packet (demuxer->audio, dp); } +/** Reorder timecodes and add cached demux packets to the queues. + * + * Timecode reordering is needed if a video track contains B frames that + * are timestamped in display order (e.g. MPEG-1, MPEG-2 or "native" MPEG-4). + * MPlayer doesn't like timestamps in display order. This function adjusts + * the timestamp of cached frames (which are exactly one I/P frame followed + * by one or more B frames) so that they are in coding order again. + * + * Example: The track with 25 FPS contains four frames with the timecodes + * I at 0ms, P at 120ms, B at 40ms and B at 80ms. As soon as the next I + * or P frame arrives these timecodes can be changed to I at 0ms, P at 40ms, + * B at 80ms and B at 120ms. + * + * \param demuxer The Matroska demuxer struct for this instance. + * \param track The track structure whose cache should be handled. + */ +static void +flush_cached_dps (demuxer_t *demuxer, mkv_track_t *track) +{ + float tmp_pts; + int i; + + if (track->num_cached_dps == 0) + return; + tmp_pts = track->cached_dps[0]->pts; + for (i = 1; i < track->num_cached_dps; i++) + track->cached_dps[i - 1]->pts = track->cached_dps[i]->pts; + track->cached_dps[track->num_cached_dps - 1]->pts = tmp_pts; + + for (i = 0; i < track->num_cached_dps; i++) + ds_add_packet (demuxer->video, track->cached_dps[i]); + track->num_cached_dps = 0; +} + +/** Cache video frames if timecodes have to be reordered. + * + * Timecode reordering is needed if a video track contains B frames that + * are timestamped in display order (e.g. MPEG-1, MPEG-2 or "native" MPEG-4). + * This function takes in a Matroska block read from the file, allocates a + * demux packet for it, fills in its values, allocates space for storing + * pointers to the cached demux packets and adds the packet to it. If + * the packet contains an I or a P frame then ::flush_cached_dps is called + * in order to send the old cached frames downstream. + * + * \param demuxer The Matroska demuxer struct for this instance. + * \param track The packet is meant for this track. + * \param buffer The actual frame contents. + * \param size The frame size in bytes. + * \param block_bref A relative timecode (backward reference). If it is \c 0 + * then the frame is an I frame. + * \param block_fref A relative timecode (forward reference). If it is \c 0 + * then the frame is either an I frame or a P frame depending on the value + * of \a block_bref. Otherwise it's a B frame. + */ +static void +handle_video_bframes (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer, + uint32_t size, int block_bref, int block_fref) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + demux_packet_t *dp; + + dp = new_demux_packet (size); + memcpy(dp->buffer, buffer, size); + dp->pos = demuxer->filepos; + dp->pts = mkv_d->last_pts; + if (block_fref == 0) /* I or P frame */ + flush_cached_dps (demuxer, track); + if (block_bref != 0) /* I frame, don't cache it */ + dp->flags = 0x10; + if ((track->num_cached_dps + 1) > track->num_allocated_dps) + { + track->cached_dps = (demux_packet_t **) + realloc(track->cached_dps, (track->num_cached_dps + 10) * + sizeof(demux_packet_t *)); + track->num_allocated_dps += 10; + } + track->cached_dps[track->num_cached_dps] = dp; + track->num_cached_dps++; +} + static int handle_block (demuxer_t *demuxer, uint8_t *block, uint64_t length, - uint64_t block_duration, int64_t block_bref) + uint64_t block_duration, int64_t block_bref, int64_t block_fref) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track = NULL; @@ -2847,6 +2972,9 @@ handle_block (demuxer_t *demuxer, uint8_t *block, uint64_t length, handle_realvideo (demuxer, track, block, lace_size[i], block_bref); else if (ds == demuxer->audio && track->realmedia) handle_realaudio (demuxer, track, block, lace_size[i], block_bref); + else if (ds == demuxer->video && track->reorder_timecodes) + handle_video_bframes (demuxer, track, block, lace_size[i], + block_bref, block_fref); else { int modified, size = lace_size[i]; @@ -2896,7 +3024,7 @@ demux_mkv_fill_buffer (demuxer_t *demuxer) while (mkv_d->cluster_size > 0) { uint64_t block_duration = 0, block_length = 0; - int64_t block_bref = 0; + int64_t block_bref = 0, block_fref = 0; uint8_t *block = NULL; while (mkv_d->blockgroup_size > 0) @@ -2925,8 +3053,10 @@ demux_mkv_fill_buffer (demuxer_t *demuxer) int64_t num = ebml_read_int (s, &l); if (num == EBML_INT_INVALID) return 0; - if (num < 0) + if (num <= 0) block_bref = num; + else + block_fref = num; break; } @@ -2944,7 +3074,7 @@ demux_mkv_fill_buffer (demuxer_t *demuxer) if (block) { int res = handle_block (demuxer, block, block_length, - block_duration, block_bref); + block_duration, block_bref, block_fref); free (block); if (res < 0) return 0; @@ -2998,6 +3128,7 @@ demux_mkv_fill_buffer (demuxer_t *demuxer) void demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, int flags) { + free_cached_dps (demuxer); if (!(flags & 2)) /* time in secs */ { void resync_audio_stream(sh_audio_t *sh_audio); -- cgit v1.2.3