ext/ffmpeg/: Add simple query functions. Seeking is only cosmetic, it's not actually...
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>
Tue, 18 Jan 2005 21:33:42 +0000 (21:33 +0000)
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>
Tue, 18 Jan 2005 21:33:42 +0000 (21:33 +0000)
Original commit message from CVS:
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_init),
(gst_ffmpegdec_query), (gst_ffmpegdec_event), (gst_ffmpegdec_open),
(gst_ffmpegdec_negotiate), (gst_ffmpegdec_chain):
* ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_init),
(gst_ffmpegdemux_close), (gst_ffmpegdemux_src_query),
(gst_ffmpegdemux_loop):
Add simple query functions. Seeking is only cosmetic, it's not
actually filled in yet (in ffmpegdec).

ChangeLog
ext/ffmpeg/gstffmpegdec.c
ext/ffmpeg/gstffmpegdemux.c

index 0f387e1..623e05b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2005-01-18  Ronald S. Bultje  <rbultje@ronald.bitfreak.net>
+
+       * ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_init),
+       (gst_ffmpegdec_query), (gst_ffmpegdec_event), (gst_ffmpegdec_open),
+       (gst_ffmpegdec_negotiate), (gst_ffmpegdec_chain):
+       * ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_init),
+       (gst_ffmpegdemux_close), (gst_ffmpegdemux_src_query),
+       (gst_ffmpegdemux_loop):
+         Add simple query functions. Seeking is only cosmetic, it's not
+         actually filled in yet (in ffmpegdec).
+
 2005-01-17  Ronald S. Bultje  <rbultje@ronald.bitfreak.net>
 
        * ext/ffmpeg/gstffmpegenc.c: (gst_ffmpegenc_init),
index eb76a98..18e2ad7 100644 (file)
@@ -49,6 +49,14 @@ struct _GstFFMpegDec
   AVCodecContext *context;
   AVFrame *picture;
   gboolean opened;
+  union {
+    struct {
+      gint width, height, fps, fps_base;
+    } video;
+    struct {
+      gint channels, samplerate;
+    } audio;
+  } format; 
 
   /* parsing */
   AVCodecParserContext *pctx;
@@ -106,6 +114,10 @@ static void gst_ffmpegdec_class_init (GstFFMpegDecClass * klass);
 static void gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec);
 static void gst_ffmpegdec_dispose (GObject * object);
 
+static gboolean gst_ffmpegdec_query (GstPad * pad, GstQueryType type,
+    GstFormat * fmt, gint64 * value);
+static gboolean gst_ffmpegdec_event (GstPad * pad, GstEvent * event);
+
 static GstPadLinkReturn gst_ffmpegdec_connect (GstPad * pad,
     const GstCaps * caps);
 static void gst_ffmpegdec_chain (GstPad * pad, GstData * data);
@@ -192,10 +204,14 @@ gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
   ffmpegdec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
   gst_pad_set_link_function (ffmpegdec->sinkpad, gst_ffmpegdec_connect);
   gst_pad_set_chain_function (ffmpegdec->sinkpad, gst_ffmpegdec_chain);
+  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad);
+
   ffmpegdec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
   gst_pad_use_explicit_caps (ffmpegdec->srcpad);
-
-  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad);
+  gst_pad_set_event_function (ffmpegdec->srcpad,
+      GST_DEBUG_FUNCPTR (gst_ffmpegdec_event));
+  gst_pad_set_query_function (ffmpegdec->srcpad,
+      GST_DEBUG_FUNCPTR (gst_ffmpegdec_query));
   gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad);
 
   /* some ffmpeg data */
@@ -223,6 +239,45 @@ gst_ffmpegdec_dispose (GObject * object)
   av_free (ffmpegdec->picture);
 }
 
+static gboolean
+gst_ffmpegdec_query (GstPad * pad, GstQueryType type,
+    GstFormat * fmt, gint64 * value)
+{
+  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) gst_pad_get_parent (pad);
+  GstPad *peer = GST_PAD_PEER (ffmpegdec->sinkpad);
+  GstFormat bfmt = GST_FORMAT_BYTES;
+
+  if (!peer)
+    return FALSE;
+  else if (gst_pad_query (peer, type, fmt, value))
+    return TRUE;
+  /* ok, do bitrate calc... */
+  else if ((type != GST_QUERY_POSITION && type != GST_QUERY_TOTAL) ||
+           *fmt != GST_FORMAT_TIME || ffmpegdec->context->bit_rate == 0 ||
+           !gst_pad_query (peer, type, &bfmt, value))
+    return FALSE;
+
+  if (ffmpegdec->pcache && type == GST_QUERY_POSITION)
+    *value -= GST_BUFFER_SIZE (ffmpegdec->pcache);
+  *value *= GST_SECOND / ffmpegdec->context->bit_rate;
+
+  return TRUE;
+}
+
+static gboolean
+gst_ffmpegdec_event (GstPad * pad, GstEvent * event)
+{
+  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) gst_pad_get_parent (pad);
+  GstPad *peer = GST_PAD_PEER (ffmpegdec->sinkpad);
+
+  if (!peer)
+    return FALSE;
+  else if (gst_pad_send_event (peer, event))
+    return TRUE;
+  else 
+    return FALSE; /* .. */
+}
+
 static void
 gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
 {
@@ -272,10 +327,29 @@ gst_ffmpegdec_open (GstFFMpegDec *ffmpegdec)
     return FALSE;
   }
 
-  /* open a parser if we can - exclude mpeg4 for now... */
-  if (oclass->in_plugin->id != CODEC_ID_MPEG4)
+  /* open a parser if we can - exclude mpeg4, because it is already
+   * framed (divx), mp3 because it doesn't work (?) and mjpeg because
+   * of $(see mpeg4)... */
+  if (oclass->in_plugin->id != CODEC_ID_MPEG4 &&
+      oclass->in_plugin->id != CODEC_ID_MJPEG &&
+      oclass->in_plugin->id != CODEC_ID_MP3)
     ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id);
 
+  switch (oclass->in_plugin->type) {
+    case CODEC_TYPE_VIDEO:
+      ffmpegdec->format.video.width = 0;
+      ffmpegdec->format.video.height = 0;
+      ffmpegdec->format.video.fps = 0;
+      ffmpegdec->format.video.fps_base = 0;
+      break;
+    case CODEC_TYPE_AUDIO:
+      ffmpegdec->format.audio.samplerate = 0;
+      ffmpegdec->format.audio.channels = 0;
+      break;
+    default:
+      break;
+  }
+
   return TRUE;
 }
 
@@ -400,6 +474,25 @@ gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
       (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
   GstCaps *caps;
 
+  switch (oclass->in_plugin->type) {
+    case CODEC_TYPE_VIDEO:
+      if (ffmpegdec->format.video.width == ffmpegdec->context->width &&
+          ffmpegdec->format.video.height == ffmpegdec->context->height &&
+          ffmpegdec->format.video.fps == ffmpegdec->context->frame_rate &&
+          ffmpegdec->format.video.fps_base ==
+              ffmpegdec->context->frame_rate_base)
+        return TRUE;
+      break;
+    case CODEC_TYPE_AUDIO:
+      if (ffmpegdec->format.audio.samplerate ==
+              ffmpegdec->context->sample_rate &&
+          ffmpegdec->format.audio.channels == ffmpegdec->context->channels)
+        return TRUE;
+      break;
+    default:
+      break;
+  }
+
   caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
       ffmpegdec->context);
 
@@ -472,12 +565,7 @@ gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
 
   /* parse cache joining */
   if (ffmpegdec->pcache) {
-GST_LOG ("Joining %p[%lld/%d]&&%p[%lld/%d]",
-        ffmpegdec->pcache, GST_BUFFER_OFFSET (ffmpegdec->pcache),
-        GST_BUFFER_SIZE (ffmpegdec->pcache), inbuf,
-        GST_BUFFER_OFFSET (inbuf), GST_BUFFER_SIZE (inbuf));
     inbuf = gst_buffer_join (ffmpegdec->pcache, inbuf);
-GST_LOG ("done");
     ffmpegdec->pcache = NULL;
     bdata = GST_BUFFER_DATA (inbuf);
     bsize = GST_BUFFER_SIZE (inbuf);
@@ -498,8 +586,7 @@ GST_LOG ("done");
 
   do {
     /* parse, if at all possible */
-    if (ffmpegdec->pctx && ffmpegdec->context->codec_id != CODEC_ID_MP3 && 
-       ffmpegdec->context->codec_id != CODEC_ID_MJPEG) {
+    if (ffmpegdec->pctx) {
       gint res;
 
       res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
@@ -606,11 +693,9 @@ GST_LOG ("done");
     if (have_data) {
       GST_DEBUG ("Decoded data, now pushing");
 
-      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
-        if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
-          gst_buffer_unref (outbuf);
-          return;
-        }
+      if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
+        gst_buffer_unref (outbuf);
+        return;
       }
 
       if (GST_PAD_IS_USABLE (ffmpegdec->srcpad))
@@ -626,7 +711,8 @@ GST_LOG ("done");
     }
   } while (bsize > 0);
 
-  if (ffmpegdec->pctx && bsize > 0) {
+  if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) &&
+      bsize > 0) {
     GST_DEBUG ("Keeping %d bytes of data", bsize);
 
     ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
index 9796ba4..562b1d2 100644 (file)
@@ -219,7 +219,7 @@ gst_ffmpegdemux_init (GstFFMpegDemux * demux)
   for (n = 0; n < MAX_STREAMS; n++) {
     demux->srcpads[n] = NULL;
     demux->handled[n] = FALSE;
-    demux->last_ts[n] = 0;
+    demux->last_ts[n] = GST_CLOCK_TIME_NONE;
   }
   demux->videopads = 0;
   demux->audiopads = 0;
@@ -240,7 +240,7 @@ gst_ffmpegdemux_close (GstFFMpegDemux * demux)
       demux->srcpads[n] = NULL;
     }
     demux->handled[n] = FALSE;
-    demux->last_ts[n] = 0;
+    demux->last_ts[n] = GST_CLOCK_TIME_NONE;
   }
   demux->videopads = 0;
   demux->audiopads = 0;
@@ -358,44 +358,62 @@ gst_ffmpegdemux_src_query (GstPad * pad,
 {
   GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
   AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
-  gboolean res = TRUE;
-
-  if (!stream || (*fmt == GST_FORMAT_DEFAULT &&
-          stream->codec.codec_type != CODEC_TYPE_VIDEO))
-    return FALSE;
+  gboolean res = FALSE;
 
   switch (type) {
     case GST_QUERY_TOTAL:
       switch (*fmt) {
         case GST_FORMAT_TIME:
-          *value = stream->duration * (GST_SECOND / AV_TIME_BASE);
+          if (stream) {
+            *value = stream->duration * (GST_SECOND / AV_TIME_BASE);
+            res = TRUE;
+          }
           break;
         case GST_FORMAT_DEFAULT:
-          if (stream->codec_info_nb_frames) {
+          if (stream->codec_info_nb_frames &&
+              stream->codec.codec_type == CODEC_TYPE_VIDEO) {
             *value = stream->codec_info_nb_frames;
-            break;
-          }                     /* else fall-through */
+            res = TRUE;
+          }
+          break;
+        case GST_FORMAT_BYTES:
+          if (demux->videopads + demux->audiopads == 1 &&
+              GST_PAD_PEER (demux->sinkpad) != NULL) {
+            res = gst_pad_query (GST_PAD_PEER (demux->sinkpad),
+                type, fmt, value);
+          }
+          break;
         default:
-          res = FALSE;
           break;
       }
       break;
     case GST_QUERY_POSITION:
       switch (*fmt) {
         case GST_FORMAT_TIME:
-          *value = demux->last_ts[stream->index];
+          if (stream &&
+              GST_CLOCK_TIME_IS_VALID (demux->last_ts[stream->index])) {
+            *value = demux->last_ts[stream->index];
+            res = TRUE;
+          }
           break;
         case GST_FORMAT_DEFAULT:
-          res = gst_pad_convert (pad, GST_FORMAT_TIME,
-              demux->last_ts[stream->index], fmt, value);
+          if (stream && stream->codec.codec_type == CODEC_TYPE_VIDEO &&
+              GST_CLOCK_TIME_IS_VALID (demux->last_ts[stream->index])) {
+            res = gst_pad_convert (pad, GST_FORMAT_TIME,
+                demux->last_ts[stream->index], fmt, value);
+          }
           break;
+        case GST_FORMAT_BYTES:
+          if (demux->videopads + demux->audiopads == 1 &&
+              GST_PAD_PEER (demux->sinkpad) != NULL) {
+            res = gst_pad_query (GST_PAD_PEER (demux->sinkpad),
+                type, fmt, value);
+          }
         default:
-          res = FALSE;
           break;
       }
       break;
     default:
-      res = FALSE;
       break;
   }
 
@@ -629,6 +647,7 @@ gst_ffmpegdemux_loop (GstElement * element)
     if (pkt.pts != AV_NOPTS_VALUE) {
       GST_BUFFER_TIMESTAMP (outbuf) = (gdouble) (pkt.pts +
           stream->start_time) * GST_SECOND / AV_TIME_BASE;
+      demux->last_ts[stream->index] = GST_BUFFER_TIMESTAMP (outbuf);
     }
 
     if (pkt.flags & PKT_FLAG_KEY) {