summaryrefslogtreecommitdiffstats
path: root/libmpdemux/demux_mkv.c
diff options
context:
space:
mode:
authormosu <mosu@b3059339-0415-0410-9bf9-f77b7e298cf2>2004-11-26 16:36:03 +0000
committermosu <mosu@b3059339-0415-0410-9bf9-f77b7e298cf2>2004-11-26 16:36:03 +0000
commit2f8386388329aee07197b885c1d1d65eba81c6b1 (patch)
tree6c571e03a55b39cd906508ad28479a1b94427732 /libmpdemux/demux_mkv.c
parentabe36816296f4b11378549d8012ba74a9091fc3f (diff)
downloadmpv-2f8386388329aee07197b885c1d1d65eba81c6b1.tar.bz2
mpv-2f8386388329aee07197b885c1d1d65eba81c6b1.tar.xz
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
Diffstat (limited to 'libmpdemux/demux_mkv.c')
-rw-r--r--libmpdemux/demux_mkv.c139
1 files changed, 135 insertions, 4 deletions
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; i<mkv_d->num_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);