avprotocol: Port from the URL protocol handler to saner public API

This commit is contained in:
Christiaan Welvaart 2012-12-12 12:09:32 +00:00 committed by Sebastian Dröge
parent 4c46f11d5f
commit 338b147374
5 changed files with 124 additions and 144 deletions

View file

@ -30,7 +30,6 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavformat/url.h>
#include "gstav.h" #include "gstav.h"
#include "gstavutils.h" #include "gstavutils.h"
@ -150,9 +149,6 @@ plugin_init (GstPlugin * plugin)
gst_ffmpegaudioresample_register (plugin); gst_ffmpegaudioresample_register (plugin);
#endif #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. */ /* Now we can return the pointer to the newly created Plugin object. */
return TRUE; return TRUE;
} }

View file

@ -26,7 +26,6 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavformat/url.h>
#include <gst/gst.h> #include <gst/gst.h>
@ -56,10 +55,13 @@ int gst_ffmpeg_avcodec_open (AVCodecContext *avctx, AVCodec *codec);
int gst_ffmpeg_avcodec_close (AVCodecContext *avctx); int gst_ffmpeg_avcodec_close (AVCodecContext *avctx);
int gst_ffmpeg_av_find_stream_info(AVFormatContext *ic); 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; G_END_DECLS
extern URLProtocol gstpipe_protocol;
/* use GST_FFMPEG URL_STREAMHEADER with URL_WRONLY if the first /* use GST_FFMPEG URL_STREAMHEADER with URL_WRONLY if the first
* buffer should be used as streamheader property on the pad's caps. */ * buffer should be used as streamheader property on the pad's caps. */

View file

@ -332,8 +332,11 @@ gst_ffmpegdemux_close (GstFFMpegDemux * demux)
demux->audiopads = 0; demux->audiopads = 0;
/* close demuxer context from ffmpeg */ /* close demuxer context from ffmpeg */
av_close_input_file (demux->context); if (demux->seekable)
demux->context = NULL; gst_ffmpegdata_close (demux->context->pb);
else
gst_ffmpeg_pipe_close (demux->context->pb);
avformat_close_input (&demux->context);
GST_OBJECT_LOCK (demux); GST_OBJECT_LOCK (demux);
demux->opened = FALSE; demux->opened = FALSE;
@ -1115,9 +1118,9 @@ gst_ffmpegdemux_read_tags (GstFFMpegDemux * demux)
static gboolean static gboolean
gst_ffmpegdemux_open (GstFFMpegDemux * demux) gst_ffmpegdemux_open (GstFFMpegDemux * demux)
{ {
AVIOContext *iocontext = NULL;
GstFFMpegDemuxClass *oclass = GstFFMpegDemuxClass *oclass =
(GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux); (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux);
gchar *location;
gint res, n_streams, i; gint res, n_streams, i;
#if 0 #if 0
/* Re-enable once converted to new AVMetaData API /* Re-enable once converted to new AVMetaData API
@ -1133,15 +1136,14 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux)
/* open via our input protocol hack */ /* open via our input protocol hack */
if (demux->seekable) if (demux->seekable)
location = g_strdup_printf ("gstreamer://%p", demux->sinkpad); res = gst_ffmpegdata_open (demux->sinkpad, AVIO_FLAG_READ, &iocontext);
else else
location = g_strdup_printf ("gstpipe://%p", &demux->ffpipe); res = gst_ffmpeg_pipe_open (&demux->ffpipe, AVIO_FLAG_READ, &iocontext);
GST_DEBUG_OBJECT (demux, "about to call av_open_input_file %s", location);
res = avformat_open_input (&demux->context, location, demux->context = avformat_alloc_context ();
oclass->in_plugin, NULL); 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); GST_DEBUG_OBJECT (demux, "av_open_input returned %d", res);
if (res < 0) if (res < 0)
goto open_failed; goto open_failed;

View file

@ -24,6 +24,7 @@
#include <string.h> #include <string.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstcollectpads.h> #include <gst/base/gstcollectpads.h>
@ -58,6 +59,7 @@ struct _GstFFMpegMux
/* event_function is the collectpads default eventfunction */ /* event_function is the collectpads default eventfunction */
GstPadEventFunction event_function; GstPadEventFunction event_function;
int max_delay; int max_delay;
int preload;
}; };
typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass; typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass;
@ -294,7 +296,7 @@ gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass)
g_object_class_install_property (gobject_class, PROP_PRELOAD, g_object_class_install_property (gobject_class, PROP_PRELOAD,
g_param_spec_int ("preload", "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)); 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MAXDELAY, 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 = g_new0 (AVFormatContext, 1);
ffmpegmux->context->oformat = oclass->in_plugin; ffmpegmux->context->oformat = oclass->in_plugin;
ffmpegmux->context->nb_streams = 0; ffmpegmux->context->nb_streams = 0;
g_snprintf (ffmpegmux->context->filename,
sizeof (ffmpegmux->context->filename),
"gstreamer://%p", ffmpegmux->srcpad);
ffmpegmux->opened = FALSE; ffmpegmux->opened = FALSE;
ffmpegmux->videopads = 0; ffmpegmux->videopads = 0;
@ -345,6 +344,7 @@ gst_ffmpegmux_set_property (GObject * object, guint prop_id,
switch (prop_id) { switch (prop_id) {
case PROP_PRELOAD: case PROP_PRELOAD:
src->preload = g_value_get_int (value);
break; break;
case PROP_MAXDELAY: case PROP_MAXDELAY:
src->max_delay = g_value_get_int (value); 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) { switch (prop_id) {
case PROP_PRELOAD: case PROP_PRELOAD:
g_value_set_int (value, src->preload);
break; break;
case PROP_MAXDELAY: case PROP_MAXDELAY:
g_value_set_int (value, src->max_delay); 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); collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
st = ffmpegmux->context->streams[collect_pad->padnum]; st = ffmpegmux->context->streams[collect_pad->padnum];
av_opt_set_int (&ffmpegmux->context, "preload", ffmpegmux->preload, 0);
ffmpegmux->context->max_delay = ffmpegmux->max_delay; ffmpegmux->context->max_delay = ffmpegmux->max_delay;
/* for the format-specific guesses, we'll go to /* 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; open_flags |= GST_FFMPEG_URL_STREAMHEADER;
} }
if (avio_open (&ffmpegmux->context->pb, if (gst_ffmpegdata_open (ffmpegmux->srcpad, open_flags,
ffmpegmux->context->filename, open_flags) < 0) { &ffmpegmux->context->pb) < 0) {
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL), GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
("Failed to open stream context in avmux")); ("Failed to open stream context in avmux"));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
@ -763,7 +765,7 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
av_write_trailer (ffmpegmux->context); av_write_trailer (ffmpegmux->context);
ffmpegmux->opened = FALSE; ffmpegmux->opened = FALSE;
avio_flush (ffmpegmux->context->pb); 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 ()); gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
return GST_FLOW_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)); gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux));
if (ffmpegmux->opened) { if (ffmpegmux->opened) {
ffmpegmux->opened = FALSE; ffmpegmux->opened = FALSE;
avio_close (ffmpegmux->context->pb); gst_ffmpegdata_close (ffmpegmux->context->pb);
} }
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:

View file

@ -25,7 +25,6 @@
#include <errno.h> #include <errno.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavformat/url.h>
#include <gst/gst.h> #include <gst/gst.h>
@ -44,61 +43,14 @@ struct _GstProtocolInfo
}; };
static int static int
gst_ffmpegdata_open (URLContext * h, const char *filename, int flags) gst_ffmpegdata_peek (void *priv_data, unsigned char *buf, int size)
{
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)
{ {
GstProtocolInfo *info; GstProtocolInfo *info;
GstBuffer *inbuf = NULL; GstBuffer *inbuf = NULL;
GstFlowReturn ret; GstFlowReturn ret;
int total = 0; int total = 0;
g_return_val_if_fail ((h->flags & AVIO_FLAG_READ), AVERROR (EIO)); info = (GstProtocolInfo *) priv_data;
info = (GstProtocolInfo *) h->priv_data;
GST_DEBUG ("Pulling %d bytes at position %" G_GUINT64_FORMAT, size, GST_DEBUG ("Pulling %d bytes at position %" G_GUINT64_FORMAT, size,
info->offset); info->offset);
@ -130,17 +82,17 @@ gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size)
} }
static int 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; gint res;
GstProtocolInfo *info; GstProtocolInfo *info;
info = (GstProtocolInfo *) h->priv_data; info = (GstProtocolInfo *) priv_data;
GST_DEBUG ("Reading %d bytes of data at position %" G_GUINT64_FORMAT, size, GST_DEBUG ("Reading %d bytes of data at position %" G_GUINT64_FORMAT, size,
info->offset); info->offset);
res = gst_ffmpegdata_peek (h, buf, size); res = gst_ffmpegdata_peek (priv_data, buf, size);
if (res >= 0) if (res >= 0)
info->offset += res; info->offset += res;
@ -150,15 +102,13 @@ gst_ffmpegdata_read (URLContext * h, unsigned char *buf, int size)
} }
static int 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; GstProtocolInfo *info;
GstBuffer *outbuf; GstBuffer *outbuf;
GST_DEBUG ("Writing %d bytes", size); GST_DEBUG ("Writing %d bytes", size);
info = (GstProtocolInfo *) h->priv_data; info = (GstProtocolInfo *) priv_data;
g_return_val_if_fail ((h->flags & AVIO_FLAG_WRITE), -EIO);
/* create buffer and push data further */ /* create buffer and push data further */
outbuf = gst_buffer_new_and_alloc (size); 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 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; GstProtocolInfo *info;
guint64 newpos = 0, oldpos; 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", GST_DEBUG ("Seeking to %" G_GINT64_FORMAT ", whence=%d",
(gint64) pos, whence); (gint64) pos, whence);
info = (GstProtocolInfo *) h->priv_data; info = (GstProtocolInfo *) priv_data;
/* TODO : if we are push-based, we need to return sensible info */ /* 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 */ /* sinkpad */
switch (whence) { switch (whence) {
case SEEK_SET: case SEEK_SET:
@ -214,9 +165,7 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence)
/* FIXME : implement case for push-based behaviour */ /* FIXME : implement case for push-based behaviour */
if (whence != AVSEEK_SIZE) if (whence != AVSEEK_SIZE)
info->offset = newpos; info->offset = newpos;
} } else if (GST_PAD_IS_SRC (info->pad)) {
if ((h->flags & AVIO_FLAG_WRITE)) {
GstSegment segment; GstSegment segment;
oldpos = info->offset; oldpos = info->offset;
@ -242,6 +191,8 @@ gst_ffmpegdata_seek (URLContext * h, int64_t pos, int whence)
segment.time = newpos; segment.time = newpos;
gst_pad_push_event (info->pad, gst_event_new_segment (&segment)); 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 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; return newpos;
} }
static int int
gst_ffmpegdata_close (URLContext * h) gst_ffmpegdata_close (AVIOContext * h)
{ {
GstProtocolInfo *info; GstProtocolInfo *info;
info = (GstProtocolInfo *) h->priv_data; info = (GstProtocolInfo *) h->opaque;
if (info == NULL) if (info == NULL)
return 0; return 0;
GST_LOG ("Closing file"); GST_LOG ("Closing file");
if ((h->flags & AVIO_FLAG_WRITE)) { if (GST_PAD_IS_SRC (info->pad)) {
/* send EOS - that closes down the stream */ /* send EOS - that closes down the stream */
gst_pad_push_event (info->pad, gst_event_new_eos ()); gst_pad_push_event (info->pad, gst_event_new_eos ());
} }
/* clean up data */ /* clean up data */
g_free (info); g_free (info);
h->priv_data = NULL; h->opaque = NULL;
av_freep (&h->buffer);
av_free (h);
return 0; 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 = { info = g_new0 (GstProtocolInfo, 1);
/*.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->set_streamheader = flags & GST_FFMPEG_URL_STREAMHEADER;
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;
}
/* 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;
buffer = av_malloc (buffer_size);
if (buffer == NULL) {
GST_WARNING ("Failed to allocate buffer");
return -ENOMEM;
}
*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, /* specialized protocol for cross-thread pushing,
* based on ffmpeg's pipe protocol */ * based on ffmpeg's pipe protocol */
static int static int
gst_ffmpeg_pipe_open (URLContext * h, const char *filename, int flags) gst_ffmpeg_pipe_read (void *priv_data, uint8_t * buf, int size)
{
GstFFMpegPipe *ffpipe;
GST_LOG ("Opening %s", filename);
/* we don't support W together */
if ((flags & AVIO_FLAG_WRITE)) {
GST_WARNING ("Only read-only is supported");
return -EINVAL;
}
if (sscanf (&filename[10], "%p", &ffpipe) != 1) {
GST_WARNING ("could not decode pipe info from %s", filename);
return -EIO;
}
/* 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;
return 0;
}
static int
gst_ffmpeg_pipe_read (URLContext * h, unsigned char *buf, int size)
{ {
GstFFMpegPipe *ffpipe; GstFFMpegPipe *ffpipe;
guint available; guint available;
ffpipe = (GstFFMpegPipe *) h->priv_data; ffpipe = (GstFFMpegPipe *) priv_data;
GST_LOG ("requested size %d", size); GST_LOG ("requested size %d", size);
@ -351,22 +313,38 @@ gst_ffmpeg_pipe_read (URLContext * h, unsigned char *buf, int size)
return size; return size;
} }
static int int
gst_ffmpeg_pipe_close (URLContext * h) gst_ffmpeg_pipe_close (AVIOContext * h)
{ {
GST_LOG ("Closing pipe"); GST_LOG ("Closing pipe");
h->priv_data = NULL; h->opaque = NULL;
av_freep (&h->buffer);
av_free (h);
return 0; return 0;
} }
URLProtocol gstpipe_protocol = { int
"gstpipe", gst_ffmpeg_pipe_open (GstFFMpegPipe * ffpipe, int flags, AVIOContext ** context)
gst_ffmpeg_pipe_open, {
NULL, static const int buffer_size = 4096;
gst_ffmpeg_pipe_read, unsigned char *buffer = NULL;
NULL,
NULL, /* sanity check */
gst_ffmpeg_pipe_close, 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;
}