avidemux: Also extract IDIT tags present too early
[platform/upstream/gst-plugins-good.git] / gst / avi / gstavidemux.c
index 4474f3c..6bcdffd 100644 (file)
 
 #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
 
+#define GST_AVI_KEYFRAME 1
+#define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
+#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
+#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
+
+
 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
 #define GST_CAT_DEFAULT avidemux_debug
 
@@ -145,13 +151,6 @@ gst_avi_demux_get_type (void)
 static void
 gst_avi_demux_base_init (GstAviDemuxClass * klass)
 {
-  static const GstElementDetails gst_avi_demux_details =
-      GST_ELEMENT_DETAILS ("Avi demuxer",
-      "Codec/Demuxer",
-      "Demultiplex an avi file into audio and video",
-      "Erik Walthinsen <omega@cse.ogi.edu>\n"
-      "Wim Taymans <wim.taymans@chello.be>\n"
-      "Thijs Vermeir <thijsvermeir@gmail.com>");
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl;
   GstCaps *audcaps, *vidcaps, *subcaps;
@@ -175,7 +174,12 @@ gst_avi_demux_base_init (GstAviDemuxClass * klass)
   gst_element_class_add_pad_template (element_class, subsrctempl);
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_templ));
-  gst_element_class_set_details (element_class, &gst_avi_demux_details);
+  gst_element_class_set_details_simple (element_class, "Avi demuxer",
+      "Codec/Demuxer",
+      "Demultiplex an avi file into audio and video",
+      "Erik Walthinsen <omega@cse.ogi.edu>, "
+      "Wim Taymans <wim.taymans@chello.be>, "
+      "Thijs Vermeir <thijsvermeir@gmail.com>");
 }
 
 static void
@@ -285,6 +289,10 @@ gst_avi_demux_reset (GstAviDemux * avi)
     gst_object_unref (avi->element_index);
   avi->element_index = NULL;
 
+  if (avi->close_seg_event) {
+    gst_event_unref (avi->close_seg_event);
+    avi->close_seg_event = NULL;
+  }
   if (avi->seg_event) {
     gst_event_unref (avi->seg_event);
     avi->seg_event = NULL;
@@ -640,9 +648,9 @@ gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
 
     if (before) {
       if (entry) {
+        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
         GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
             G_GUINT64_FORMAT, i, val);
-        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
         if (val < min)
           min = val;
       }
@@ -707,9 +715,9 @@ gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
 
     if (before) {
       if (entry) {
+        val = stream->index[index].offset;
         GST_DEBUG_OBJECT (avi,
             "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
-        val = stream->index[index].offset;
         if (val < min)
           min = val;
       }
@@ -1984,22 +1992,36 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
             GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
             break;
           case GST_RIFF_FCC_auds:
-            stream->is_vbr = (stream->strh->samplesize == 0)
-                && stream->strh->scale > 1;
             res =
                 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
                 &stream->extradata);
+            stream->is_vbr = (stream->strh->samplesize == 0)
+                && stream->strh->scale > 1
+                && stream->strf.auds->blockalign != 1;
             sub = NULL;
             GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
                 stream->is_vbr, res);
             /* we need these or we have no way to come up with timestamps */
-            if ((stream->is_vbr && !stream->strf.auds->av_bps) ||
-                (!stream->is_vbr && (!stream->strh->scale ||
+            if ((!stream->is_vbr && !stream->strf.auds->av_bps) ||
+                (stream->is_vbr && (!stream->strh->scale ||
                         !stream->strh->rate))) {
               GST_WARNING_OBJECT (element,
                   "invalid audio header, ignoring stream");
               goto fail;
             }
+            /* some more sanity checks */
+            if (stream->is_vbr) {
+              if (stream->strf.auds->blockalign <= 4) {
+                /* that would mean (too) many frames per chunk,
+                 * so not likely set as expected */
+                GST_DEBUG_OBJECT (element,
+                    "suspicious blockalign %d for VBR audio; "
+                    "overriding to 1 frame per chunk",
+                    stream->strf.auds->blockalign);
+                /* this should top any likely value */
+                stream->strf.auds->blockalign = (1 << 12);
+              }
+            }
             break;
           case GST_RIFF_FCC_iavs:
             stream->is_vbr = TRUE;
@@ -2073,6 +2095,9 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
         }
         GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name);
         break;
+      case GST_RIFF_IDIT:
+        gst_avi_demux_parse_idit (avi, sub);
+        break;
       default:
         if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
             tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
@@ -2348,7 +2373,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
 static guint
 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
 {
-  return stream->idx_n - 1;
+  return stream->idx_n;
 }
 
 /* find a previous entry in the index with the given flags */
@@ -2751,7 +2776,7 @@ gst_avi_demux_stream_index_push (GstAviDemux * avi)
   GST_DEBUG ("will parse index chunk size %u for tag %"
       GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
 
-  avi->offset = avi->first_movi_offset - 8;
+  avi->offset = avi->first_movi_offset;
   gst_avi_demux_parse_index (avi, buf);
 
 #ifndef GST_DISABLE_GST_DEBUG
@@ -3358,9 +3383,12 @@ header_wrong_avih:
 }
 
 static void
-gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d)
+gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d,
+    gint h, gint min, gint s)
 {
   GDate *date;
+  GstDateTime *dt;
+
   date = g_date_new_dmy (d, m, y);
   if (!g_date_valid (date)) {
     /* bogus date */
@@ -3369,12 +3397,19 @@ gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d)
     return;
   }
 
+  dt = gst_date_time_new_local_time (y, m, d, h, min, s);
+
   if (avi->globaltags == NULL)
     avi->globaltags = gst_tag_list_new ();
 
   gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date,
       NULL);
   g_date_free (date);
+  if (dt) {
+    gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
+        dt, NULL);
+    gst_date_time_unref (dt);
+  }
 }
 
 static void
@@ -3388,7 +3423,7 @@ gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data)
     GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
     return;
   }
-  gst_avi_demux_add_date_tag (avi, y, m, d);
+  gst_avi_demux_add_date_tag (avi, y, m, d, 0, 0, 0);
 }
 
 static gint
@@ -3439,7 +3474,7 @@ gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data)
     return;
   }
   month = get_month_num (monthstr, strlen (monthstr));
-  gst_avi_demux_add_date_tag (avi, year, month, day);
+  gst_avi_demux_add_date_tag (avi, year, month, day, hour, min, sec);
 }
 
 static void
@@ -3914,7 +3949,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
   GstAviStream *stream;
 
   seek_time = segment->last_stop;
-  keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
+  keyframe = ! !(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
 
   GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
       " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
@@ -4059,6 +4094,7 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
    * actually never fails. */
   gst_avi_demux_do_seek (avi, &seeksegment);
 
+  gst_event_replace (&avi->close_seg_event, NULL);
   if (flush) {
     GstEvent *fevent = gst_event_new_flush_stop ();
 
@@ -4066,16 +4102,13 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
     gst_avi_demux_push_event (avi, gst_event_ref (fevent));
     gst_pad_push_event (avi->sinkpad, fevent);
   } else if (avi->segment_running) {
-    GstEvent *seg;
-
     /* we are running the current segment and doing a non-flushing seek,
      * close the segment first based on the last_stop. */
     GST_DEBUG_OBJECT (avi, "closing running segment %" G_GINT64_FORMAT
         " to %" G_GINT64_FORMAT, avi->segment.start, avi->segment.last_stop);
-    seg = gst_event_new_new_segment_full (TRUE,
+    avi->close_seg_event = gst_event_new_new_segment_full (TRUE,
         avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
         avi->segment.start, avi->segment.last_stop, avi->segment.time);
-    gst_avi_demux_push_event (avi, seg);
   }
 
   /* now update the real segment info */
@@ -4182,7 +4215,7 @@ avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
   gst_segment_set_seek (&seeksegment, rate, format, flags,
       cur_type, cur, stop_type, stop, &update);
 
-  keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
+  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
   cur = seeksegment.last_stop;
 
   GST_DEBUG_OBJECT (avi,
@@ -4483,7 +4516,7 @@ gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream,
   new_entry = old_entry + 1;
 
   /* see if we reached the end */
-  if (new_entry > stream->stop_entry) {
+  if (new_entry >= stream->stop_entry) {
     if (avi->segment.rate < 0.0) {
       if (stream->step_entry == stream->start_entry) {
         /* we stepped all the way to the start, eos */
@@ -4855,9 +4888,13 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
        * through the whole file */
       if (avi->abort_buffering) {
         avi->abort_buffering = FALSE;
-        gst_adapter_flush (avi->adapter, 8);
+        if (size) {
+          gst_adapter_flush (avi->adapter, 8);
+          return GST_FLOW_OK;
+        }
+      } else {
+        return GST_FLOW_OK;
       }
-      return GST_FLOW_OK;
     }
     GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u",
         GST_FOURCC_ARGS (tag), size);
@@ -4886,9 +4923,13 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
       if (saw_desired_kf) {
         gst_adapter_flush (avi->adapter, 8);
         /* get buffer */
-        buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
-        /* patch the size */
-        GST_BUFFER_SIZE (buf) = size;
+        if (size) {
+          buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
+          /* patch the size */
+          GST_BUFFER_SIZE (buf) = size;
+        } else {
+          buf = NULL;
+        }
       } else {
         GST_DEBUG_OBJECT (avi,
             "Desired keyframe not yet reached, flushing chunk");
@@ -4962,6 +5003,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
             stream->discont = FALSE;
           }
           res = gst_pad_push (stream->pad, buf);
+          buf = NULL;
 
           /* combine flows */
           res = gst_avi_demux_combine_flows (avi, stream, res);
@@ -5051,6 +5093,10 @@ gst_avi_demux_loop (GstPad * pad)
       avi->state = GST_AVI_DEMUX_MOVI;
       break;
     case GST_AVI_DEMUX_MOVI:
+      if (G_UNLIKELY (avi->close_seg_event)) {
+        gst_avi_demux_push_event (avi, avi->close_seg_event);
+        avi->close_seg_event = NULL;
+      }
       if (G_UNLIKELY (avi->seg_event)) {
         gst_avi_demux_push_event (avi, avi->seg_event);
         avi->seg_event = NULL;
@@ -5076,13 +5122,13 @@ gst_avi_demux_loop (GstPad * pad)
   return;
 
   /* ERRORS */
-pause:
-  GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
-  avi->segment_running = FALSE;
-  gst_pad_pause_task (avi->sinkpad);
+pause:{
+
+    gboolean push_eos = FALSE;
+    GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
+    avi->segment_running = FALSE;
+    gst_pad_pause_task (avi->sinkpad);
 
-  if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) {
-    gboolean push_eos = TRUE;
 
     if (res == GST_FLOW_UNEXPECTED) {
       /* handle end-of-stream/segment */
@@ -5098,13 +5144,17 @@ pause:
             (GST_ELEMENT_CAST (avi),
             gst_message_new_segment_done (GST_OBJECT_CAST (avi),
                 GST_FORMAT_TIME, stop));
-        push_eos = FALSE;
+      } else {
+        push_eos = TRUE;
       }
-    } else {
-      /* for fatal errors we post an error message */
+    } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_UNEXPECTED) {
+      /* for fatal errors we post an error message, wrong-state is
+       * not fatal because it happens due to flushes and only means
+       * that we should stop now. */
       GST_ELEMENT_ERROR (avi, STREAM, FAILED,
           (_("Internal data stream error.")),
           ("streaming stopped, reason %s", gst_flow_get_name (res)));
+      push_eos = TRUE;
     }
     if (push_eos) {
       GST_INFO_OBJECT (avi, "sending eos");
@@ -5150,6 +5200,10 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
       }
       break;
     case GST_AVI_DEMUX_MOVI:
+      if (G_UNLIKELY (avi->close_seg_event)) {
+        gst_avi_demux_push_event (avi, avi->close_seg_event);
+        avi->close_seg_event = NULL;
+      }
       if (G_UNLIKELY (avi->seg_event)) {
         gst_avi_demux_push_event (avi, avi->seg_event);
         avi->seg_event = NULL;
@@ -5341,6 +5395,7 @@ gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
 
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      avi->have_index = FALSE;
       gst_avi_demux_reset (avi);
       break;
     default: