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 <libavformat/avformat.h>
#include <libavformat/url.h>
#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;
}

View file

@ -26,7 +26,6 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/url.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_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. */

View file

@ -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;

View file

@ -24,6 +24,7 @@
#include <string.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
@ -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:

View file

@ -25,7 +25,6 @@
#include <errno.h>
#include <libavformat/avformat.h>
#include <libavformat/url.h>
#include <gst/gst.h>
@ -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;
/* 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,
* based on ffmpeg's pipe protocol */
static int
gst_ffmpeg_pipe_open (URLContext * h, const char *filename, int flags)
{
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)
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;
}