From 338b147374f4e4557b532c89b9eb6b6b907fa955 Mon Sep 17 00:00:00 2001 From: Christiaan Welvaart Date: Wed, 12 Dec 2012 12:09:32 +0000 Subject: [PATCH] avprotocol: Port from the URL protocol handler to saner public API --- ext/libav/gstav.c | 4 - ext/libav/gstav.h | 10 +- ext/libav/gstavdemux.c | 20 ++-- ext/libav/gstavmux.c | 18 ++-- ext/libav/gstavprotocol.c | 202 +++++++++++++++++--------------------- 5 files changed, 117 insertions(+), 137 deletions(-) diff --git a/ext/libav/gstav.c b/ext/libav/gstav.c index 5413a38681..f3d3c35b1e 100644 --- a/ext/libav/gstav.c +++ b/ext/libav/gstav.c @@ -30,7 +30,6 @@ #include #include -#include #include "gstav.h" #include "gstavutils.h" @@ -150,9 +149,6 @@ plugin_init (GstPlugin * plugin) gst_ffmpegaudioresample_register (plugin); #endif - ffurl_register_protocol (&gstreamer_protocol, sizeof (URLProtocol)); - ffurl_register_protocol (&gstpipe_protocol, sizeof (URLProtocol)); - /* Now we can return the pointer to the newly created Plugin object. */ return TRUE; } diff --git a/ext/libav/gstav.h b/ext/libav/gstav.h index 8b6d40b6b0..e6b1636981 100644 --- a/ext/libav/gstav.h +++ b/ext/libav/gstav.h @@ -26,7 +26,6 @@ #include #include -#include #include @@ -56,10 +55,13 @@ int gst_ffmpeg_avcodec_open (AVCodecContext *avctx, AVCodec *codec); int gst_ffmpeg_avcodec_close (AVCodecContext *avctx); int gst_ffmpeg_av_find_stream_info(AVFormatContext *ic); -G_END_DECLS +int gst_ffmpegdata_open (GstPad * pad, int flags, AVIOContext ** context); +int gst_ffmpegdata_close (AVIOContext * h); +typedef struct _GstFFMpegPipe GstFFMpegPipe; +int gst_ffmpeg_pipe_open (GstFFMpegPipe *ffpipe, int flags, AVIOContext ** context); +int gst_ffmpeg_pipe_close (AVIOContext * h); -extern URLProtocol gstreamer_protocol; -extern URLProtocol gstpipe_protocol; +G_END_DECLS /* use GST_FFMPEG URL_STREAMHEADER with URL_WRONLY if the first * buffer should be used as streamheader property on the pad's caps. */ diff --git a/ext/libav/gstavdemux.c b/ext/libav/gstavdemux.c index a964964c6f..2204b7729f 100644 --- a/ext/libav/gstavdemux.c +++ b/ext/libav/gstavdemux.c @@ -332,8 +332,11 @@ gst_ffmpegdemux_close (GstFFMpegDemux * demux) demux->audiopads = 0; /* close demuxer context from ffmpeg */ - av_close_input_file (demux->context); - demux->context = NULL; + if (demux->seekable) + gst_ffmpegdata_close (demux->context->pb); + else + gst_ffmpeg_pipe_close (demux->context->pb); + avformat_close_input (&demux->context); GST_OBJECT_LOCK (demux); demux->opened = FALSE; @@ -1115,9 +1118,9 @@ gst_ffmpegdemux_read_tags (GstFFMpegDemux * demux) static gboolean gst_ffmpegdemux_open (GstFFMpegDemux * demux) { + AVIOContext *iocontext = NULL; GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux); - gchar *location; gint res, n_streams, i; #if 0 /* Re-enable once converted to new AVMetaData API @@ -1133,15 +1136,14 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux) /* open via our input protocol hack */ if (demux->seekable) - location = g_strdup_printf ("gstreamer://%p", demux->sinkpad); + res = gst_ffmpegdata_open (demux->sinkpad, AVIO_FLAG_READ, &iocontext); else - location = g_strdup_printf ("gstpipe://%p", &demux->ffpipe); - GST_DEBUG_OBJECT (demux, "about to call av_open_input_file %s", location); + res = gst_ffmpeg_pipe_open (&demux->ffpipe, AVIO_FLAG_READ, &iocontext); - res = avformat_open_input (&demux->context, location, - oclass->in_plugin, NULL); + demux->context = avformat_alloc_context (); + demux->context->pb = iocontext; + res = avformat_open_input (&demux->context, NULL, oclass->in_plugin, NULL); - g_free (location); GST_DEBUG_OBJECT (demux, "av_open_input returned %d", res); if (res < 0) goto open_failed; diff --git a/ext/libav/gstavmux.c b/ext/libav/gstavmux.c index fb981fe7c2..299e9f2e61 100644 --- a/ext/libav/gstavmux.c +++ b/ext/libav/gstavmux.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -58,6 +59,7 @@ struct _GstFFMpegMux /* event_function is the collectpads default eventfunction */ GstPadEventFunction event_function; int max_delay; + int preload; }; typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass; @@ -294,7 +296,7 @@ gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass) g_object_class_install_property (gobject_class, PROP_PRELOAD, g_param_spec_int ("preload", "preload", - "Set the initial demux-decode delay (in microseconds) (DEPRECATED)", + "Set the initial demux-decode delay (in microseconds)", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_MAXDELAY, @@ -325,9 +327,6 @@ gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class) ffmpegmux->context = g_new0 (AVFormatContext, 1); ffmpegmux->context->oformat = oclass->in_plugin; ffmpegmux->context->nb_streams = 0; - g_snprintf (ffmpegmux->context->filename, - sizeof (ffmpegmux->context->filename), - "gstreamer://%p", ffmpegmux->srcpad); ffmpegmux->opened = FALSE; ffmpegmux->videopads = 0; @@ -345,6 +344,7 @@ gst_ffmpegmux_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_PRELOAD: + src->preload = g_value_get_int (value); break; case PROP_MAXDELAY: src->max_delay = g_value_get_int (value); @@ -365,6 +365,7 @@ gst_ffmpegmux_get_property (GObject * object, guint prop_id, GValue * value, switch (prop_id) { case PROP_PRELOAD: + g_value_set_int (value, src->preload); break; case PROP_MAXDELAY: g_value_set_int (value, src->max_delay); @@ -470,6 +471,7 @@ gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps) collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad); st = ffmpegmux->context->streams[collect_pad->padnum]; + av_opt_set_int (&ffmpegmux->context, "preload", ffmpegmux->preload, 0); ffmpegmux->context->max_delay = ffmpegmux->max_delay; /* for the format-specific guesses, we'll go to @@ -637,8 +639,8 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data) open_flags |= GST_FFMPEG_URL_STREAMHEADER; } - if (avio_open (&ffmpegmux->context->pb, - ffmpegmux->context->filename, open_flags) < 0) { + if (gst_ffmpegdata_open (ffmpegmux->srcpad, open_flags, + &ffmpegmux->context->pb) < 0) { GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL), ("Failed to open stream context in avmux")); return GST_FLOW_ERROR; @@ -763,7 +765,7 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data) av_write_trailer (ffmpegmux->context); ffmpegmux->opened = FALSE; avio_flush (ffmpegmux->context->pb); - avio_close (ffmpegmux->context->pb); + gst_ffmpegdata_close (ffmpegmux->context->pb); gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ()); return GST_FLOW_EOS; } @@ -801,7 +803,7 @@ gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition) gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux)); if (ffmpegmux->opened) { ffmpegmux->opened = FALSE; - avio_close (ffmpegmux->context->pb); + gst_ffmpegdata_close (ffmpegmux->context->pb); } break; case GST_STATE_CHANGE_READY_TO_NULL: diff --git a/ext/libav/gstavprotocol.c b/ext/libav/gstavprotocol.c index a2f0b8395b..ef4f8cab09 100644 --- a/ext/libav/gstavprotocol.c +++ b/ext/libav/gstavprotocol.c @@ -25,7 +25,6 @@ #include #include -#include #include @@ -44,61 +43,14 @@ struct _GstProtocolInfo }; static int -gst_ffmpegdata_open (URLContext * h, const char *filename, int flags) -{ - GstProtocolInfo *info; - GstPad *pad; - - GST_LOG ("Opening %s", filename); - - info = g_new0 (GstProtocolInfo, 1); - - info->set_streamheader = flags & GST_FFMPEG_URL_STREAMHEADER; - flags &= ~GST_FFMPEG_URL_STREAMHEADER; - h->flags &= ~GST_FFMPEG_URL_STREAMHEADER; - - /* we don't support R/W together */ - if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { - GST_WARNING ("Only read-only or write-only are supported"); - return -EINVAL; - } - - if (sscanf (&filename[12], "%p", &pad) != 1) { - GST_WARNING ("could not decode pad from %s", filename); - return -EIO; - } - - /* make sure we're a pad and that we're of the right type */ - g_return_val_if_fail (GST_IS_PAD (pad), -EINVAL); - - if ((flags & AVIO_FLAG_READ)) { - g_return_val_if_fail (GST_PAD_IS_SINK (pad), -EINVAL); - } - if ((flags & AVIO_FLAG_WRITE)) { - g_return_val_if_fail (GST_PAD_IS_SRC (pad), -EINVAL); - } - - info->eos = FALSE; - info->pad = pad; - info->offset = 0; - - h->priv_data = (void *) info; - h->is_streamed = FALSE; - h->max_packet_size = 0; - - return 0; -} - -static int -gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size) +gst_ffmpegdata_peek (void *priv_data, unsigned char *buf, int size) { GstProtocolInfo *info; GstBuffer *inbuf = NULL; GstFlowReturn ret; int total = 0; - g_return_val_if_fail ((h->flags & AVIO_FLAG_READ), AVERROR (EIO)); - info = (GstProtocolInfo *) h->priv_data; + info = (GstProtocolInfo *) priv_data; GST_DEBUG ("Pulling %d bytes at position %" G_GUINT64_FORMAT, size, info->offset); @@ -130,17 +82,17 @@ gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size) } static int -gst_ffmpegdata_read (URLContext * h, unsigned char *buf, int size) +gst_ffmpegdata_read (void *priv_data, unsigned char *buf, int size) { gint res; GstProtocolInfo *info; - info = (GstProtocolInfo *) h->priv_data; + info = (GstProtocolInfo *) priv_data; GST_DEBUG ("Reading %d bytes of data at position %" G_GUINT64_FORMAT, size, info->offset); - res = gst_ffmpegdata_peek (h, buf, size); + res = gst_ffmpegdata_peek (priv_data, buf, size); if (res >= 0) info->offset += res; @@ -150,15 +102,13 @@ gst_ffmpegdata_read (URLContext * h, unsigned char *buf, int size) } static int -gst_ffmpegdata_write (URLContext * h, const unsigned char *buf, int size) +gst_ffmpegdata_write (void *priv_data, uint8_t * buf, int size) { GstProtocolInfo *info; GstBuffer *outbuf; GST_DEBUG ("Writing %d bytes", size); - info = (GstProtocolInfo *) h->priv_data; - - g_return_val_if_fail ((h->flags & AVIO_FLAG_WRITE), -EIO); + info = (GstProtocolInfo *) priv_data; /* create buffer and push data further */ outbuf = gst_buffer_new_and_alloc (size); @@ -173,7 +123,7 @@ gst_ffmpegdata_write (URLContext * h, const unsigned char *buf, int size) } static int64_t -gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence) +gst_ffmpegdata_seek (void *priv_data, int64_t pos, int whence) { GstProtocolInfo *info; guint64 newpos = 0, oldpos; @@ -181,10 +131,11 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence) GST_DEBUG ("Seeking to %" G_GINT64_FORMAT ", whence=%d", (gint64) pos, whence); - info = (GstProtocolInfo *) h->priv_data; + info = (GstProtocolInfo *) priv_data; /* TODO : if we are push-based, we need to return sensible info */ - if ((h->flags & AVIO_FLAG_READ)) { + + if (GST_PAD_IS_SINK (info->pad)) { /* sinkpad */ switch (whence) { case SEEK_SET: @@ -214,9 +165,7 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence) /* FIXME : implement case for push-based behaviour */ if (whence != AVSEEK_SIZE) info->offset = newpos; - } - - if ((h->flags & AVIO_FLAG_WRITE)) { + } else if (GST_PAD_IS_SRC (info->pad)) { GstSegment segment; oldpos = info->offset; @@ -242,6 +191,8 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence) segment.time = newpos; gst_pad_push_event (info->pad, gst_event_new_segment (&segment)); } + } else { + g_assert_not_reached (); } GST_DEBUG ("Now at offset %" G_GUINT64_FORMAT " (returning %" G_GUINT64_FORMAT @@ -249,79 +200,90 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence) return newpos; } -static int -gst_ffmpegdata_close (URLContext * h) +int +gst_ffmpegdata_close (AVIOContext * h) { GstProtocolInfo *info; - info = (GstProtocolInfo *) h->priv_data; + info = (GstProtocolInfo *) h->opaque; if (info == NULL) return 0; GST_LOG ("Closing file"); - if ((h->flags & AVIO_FLAG_WRITE)) { + if (GST_PAD_IS_SRC (info->pad)) { /* send EOS - that closes down the stream */ gst_pad_push_event (info->pad, gst_event_new_eos ()); } /* clean up data */ g_free (info); - h->priv_data = NULL; + h->opaque = NULL; + + av_freep (&h->buffer); + av_free (h); return 0; } +int +gst_ffmpegdata_open (GstPad * pad, int flags, AVIOContext ** context) +{ + GstProtocolInfo *info; + static const int buffer_size = 4096; + unsigned char *buffer = NULL; -URLProtocol gstreamer_protocol = { - /*.name = */ "gstreamer", - /*.url_open = */ gst_ffmpegdata_open, - /*.url_open2 = */ NULL, - /*.url_read = */ gst_ffmpegdata_read, - /*.url_write = */ gst_ffmpegdata_write, - /*.url_seek = */ gst_ffmpegdata_seek, - /*.url_close = */ gst_ffmpegdata_close, -}; + info = g_new0 (GstProtocolInfo, 1); + info->set_streamheader = flags & GST_FFMPEG_URL_STREAMHEADER; + flags &= ~GST_FFMPEG_URL_STREAMHEADER; -/* specialized protocol for cross-thread pushing, - * based on ffmpeg's pipe protocol */ + /* we don't support R/W together */ + if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { + GST_WARNING ("Only read-only or write-only are supported"); + return -EINVAL; + } -static int -gst_ffmpeg_pipe_open (URLContext * h, const char *filename, int flags) -{ - GstFFMpegPipe *ffpipe; + /* make sure we're a pad and that we're of the right type */ + g_return_val_if_fail (GST_IS_PAD (pad), -EINVAL); - GST_LOG ("Opening %s", filename); + if ((flags & AVIO_FLAG_READ)) + g_return_val_if_fail (GST_PAD_IS_SINK (pad), -EINVAL); + if ((flags & AVIO_FLAG_WRITE)) + g_return_val_if_fail (GST_PAD_IS_SRC (pad), -EINVAL); - /* we don't support W together */ - if ((flags & AVIO_FLAG_WRITE)) { - GST_WARNING ("Only read-only is supported"); - return -EINVAL; - } + info->eos = FALSE; + info->pad = pad; + info->offset = 0; - if (sscanf (&filename[10], "%p", &ffpipe) != 1) { - GST_WARNING ("could not decode pipe info from %s", filename); - return -EIO; + buffer = av_malloc (buffer_size); + if (buffer == NULL) { + GST_WARNING ("Failed to allocate buffer"); + return -ENOMEM; } - /* sanity check */ - g_return_val_if_fail (GST_IS_ADAPTER (ffpipe->adapter), -EINVAL); - - h->priv_data = (void *) ffpipe; - h->is_streamed = TRUE; - h->max_packet_size = 0; + *context = + avio_alloc_context (buffer, buffer_size, flags, (void *) info, + gst_ffmpegdata_read, gst_ffmpegdata_write, gst_ffmpegdata_seek); + (*context)->seekable = AVIO_SEEKABLE_NORMAL; + if (!(flags & AVIO_FLAG_WRITE)) { + (*context)->buf_ptr = (*context)->buf_end; + (*context)->write_flag = 0; + } return 0; } +/* specialized protocol for cross-thread pushing, + * based on ffmpeg's pipe protocol */ + static int -gst_ffmpeg_pipe_read (URLContext * h, unsigned char *buf, int size) +gst_ffmpeg_pipe_read (void *priv_data, uint8_t * buf, int size) { GstFFMpegPipe *ffpipe; guint available; - ffpipe = (GstFFMpegPipe *) h->priv_data; + ffpipe = (GstFFMpegPipe *) priv_data; GST_LOG ("requested size %d", size); @@ -351,22 +313,38 @@ gst_ffmpeg_pipe_read (URLContext * h, unsigned char *buf, int size) return size; } -static int -gst_ffmpeg_pipe_close (URLContext * h) +int +gst_ffmpeg_pipe_close (AVIOContext * h) { GST_LOG ("Closing pipe"); - h->priv_data = NULL; + h->opaque = NULL; + av_freep (&h->buffer); + av_free (h); return 0; } -URLProtocol gstpipe_protocol = { - "gstpipe", - gst_ffmpeg_pipe_open, - NULL, - gst_ffmpeg_pipe_read, - NULL, - NULL, - gst_ffmpeg_pipe_close, -}; +int +gst_ffmpeg_pipe_open (GstFFMpegPipe * ffpipe, int flags, AVIOContext ** context) +{ + static const int buffer_size = 4096; + unsigned char *buffer = NULL; + + /* sanity check */ + g_return_val_if_fail (GST_IS_ADAPTER (ffpipe->adapter), -EINVAL); + + buffer = av_malloc (buffer_size); + if (buffer == NULL) { + GST_WARNING ("Failed to allocate buffer"); + return -ENOMEM; + } + + *context = + avio_alloc_context (buffer, buffer_size, 0, (void *) ffpipe, + gst_ffmpeg_pipe_read, NULL, NULL); + (*context)->seekable = 0; + (*context)->buf_ptr = (*context)->buf_end; + + return 0; +} -- 2.34.1