changeset 14054:53ea955d19fa

Added support for MPEG-1 and MPEG-2 in Matroska.
author mosu
date Fri, 26 Nov 2004 16:36:03 +0000
parents 9f89feb07542
children efe58d68689e
files libmpdemux/demux_mkv.c libmpdemux/matroska.h
diffstat 2 files changed, 137 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/demux_mkv.c	Fri Nov 26 10:32:53 2004 +0000
+++ b/libmpdemux/demux_mkv.c	Fri Nov 26 16:36:03 2004 +0000
@@ -92,6 +92,13 @@
 
   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 @@
   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 @@
 #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 @@
   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 @@
   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_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 @@
       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 @@
                     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 @@
           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 @@
 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);
--- a/libmpdemux/matroska.h	Fri Nov 26 10:32:53 2004 +0000
+++ b/libmpdemux/matroska.h	Fri Nov 26 16:36:03 2004 +0000
@@ -45,6 +45,8 @@
 #define MKV_V_SORENSONV3 "V_SORENSON/V3"
 #define MKV_V_CINEPAK    "V_CINEPAK"
 #define MKV_V_QUICKTIME  "V_QUICKTIME"
+#define MKV_V_MPEG1      "V_MPEG1"
+#define MKV_V_MPEG2      "V_MPEG2"
 
 #define MKV_S_TEXTASCII  "S_TEXT/ASCII"
 #define MKV_S_TEXTUTF8   "S_TEXT/UTF8"