mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
parent
91ac27e6de
commit
965f23f4f1
7 changed files with 396 additions and 19 deletions
|
@ -28,4 +28,5 @@ noinst_HEADERS = \
|
||||||
gstffmpeg.h \
|
gstffmpeg.h \
|
||||||
gstffmpegcodecmap.h \
|
gstffmpegcodecmap.h \
|
||||||
gstffmpegenc.h \
|
gstffmpegenc.h \
|
||||||
gstffmpegcfg.h
|
gstffmpegcfg.h \
|
||||||
|
gstffmpegpipe.h
|
||||||
|
|
|
@ -152,6 +152,7 @@ plugin_init (GstPlugin * plugin)
|
||||||
gst_ffmpegaudioresample_register (plugin);
|
gst_ffmpegaudioresample_register (plugin);
|
||||||
|
|
||||||
register_protocol (&gstreamer_protocol);
|
register_protocol (&gstreamer_protocol);
|
||||||
|
register_protocol (&gstpipe_protocol);
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
|
@ -61,6 +61,7 @@ int gst_ffmpeg_av_find_stream_info(AVFormatContext *ic);
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
extern URLProtocol gstreamer_protocol;
|
extern URLProtocol gstreamer_protocol;
|
||||||
|
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. */
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
#include "gstffmpeg.h"
|
#include "gstffmpeg.h"
|
||||||
#include "gstffmpegcodecmap.h"
|
#include "gstffmpegcodecmap.h"
|
||||||
|
#include "gstffmpegpipe.h"
|
||||||
|
|
||||||
typedef struct _GstFFMpegDemux GstFFMpegDemux;
|
typedef struct _GstFFMpegDemux GstFFMpegDemux;
|
||||||
typedef struct _GstFFStream GstFFStream;
|
typedef struct _GstFFStream GstFFStream;
|
||||||
|
@ -83,6 +84,11 @@ struct _GstFFMpegDemux
|
||||||
|
|
||||||
/* cached seek in READY */
|
/* cached seek in READY */
|
||||||
GstEvent *seek_event;
|
GstEvent *seek_event;
|
||||||
|
|
||||||
|
/* push mode data */
|
||||||
|
GstFFMpegPipe ffpipe;
|
||||||
|
GstTask *task;
|
||||||
|
GStaticRecMutex *task_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _GstFFMpegDemuxClassParams
|
typedef struct _GstFFMpegDemuxClassParams
|
||||||
|
@ -107,8 +113,12 @@ struct _GstFFMpegDemuxClass
|
||||||
static void gst_ffmpegdemux_class_init (GstFFMpegDemuxClass * klass);
|
static void gst_ffmpegdemux_class_init (GstFFMpegDemuxClass * klass);
|
||||||
static void gst_ffmpegdemux_base_init (GstFFMpegDemuxClass * klass);
|
static void gst_ffmpegdemux_base_init (GstFFMpegDemuxClass * klass);
|
||||||
static void gst_ffmpegdemux_init (GstFFMpegDemux * demux);
|
static void gst_ffmpegdemux_init (GstFFMpegDemux * demux);
|
||||||
|
static void gst_ffmpegdemux_finalize (GObject * object);
|
||||||
|
|
||||||
static void gst_ffmpegdemux_loop (GstPad * pad);
|
static gboolean gst_ffmpegdemux_sink_event (GstPad * sinkpad, GstEvent * event);
|
||||||
|
static GstFlowReturn gst_ffmpegdemux_chain (GstPad * sinkpad, GstBuffer * buf);
|
||||||
|
|
||||||
|
static void gst_ffmpegdemux_loop (GstFFMpegDemux * demux);
|
||||||
static gboolean gst_ffmpegdemux_sink_activate (GstPad * sinkpad);
|
static gboolean gst_ffmpegdemux_sink_activate (GstPad * sinkpad);
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
|
gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
|
||||||
|
@ -220,6 +230,8 @@ gst_ffmpegdemux_class_init (GstFFMpegDemuxClass * klass)
|
||||||
|
|
||||||
parent_class = g_type_class_peek_parent (klass);
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ffmpegdemux_finalize);
|
||||||
|
|
||||||
gstelement_class->change_state = gst_ffmpegdemux_change_state;
|
gstelement_class->change_state = gst_ffmpegdemux_change_state;
|
||||||
gstelement_class->send_event = gst_ffmpegdemux_send_event;
|
gstelement_class->send_event = gst_ffmpegdemux_send_event;
|
||||||
}
|
}
|
||||||
|
@ -232,13 +244,26 @@ gst_ffmpegdemux_init (GstFFMpegDemux * demux)
|
||||||
gint n;
|
gint n;
|
||||||
|
|
||||||
demux->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
|
demux->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
|
||||||
gst_pad_set_activate_function (demux->sinkpad, gst_ffmpegdemux_sink_activate);
|
gst_pad_set_activate_function (demux->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate));
|
||||||
gst_pad_set_activatepull_function (demux->sinkpad,
|
gst_pad_set_activatepull_function (demux->sinkpad,
|
||||||
gst_ffmpegdemux_sink_activate_pull);
|
GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate_pull));
|
||||||
gst_pad_set_activatepush_function (demux->sinkpad,
|
gst_pad_set_activatepush_function (demux->sinkpad,
|
||||||
gst_ffmpegdemux_sink_activate_push);
|
GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate_push));
|
||||||
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
|
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
|
||||||
|
|
||||||
|
/* push based setup */
|
||||||
|
/* the following are not used in pull-based mode, so safe to set anyway */
|
||||||
|
gst_pad_set_event_function (demux->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_event));
|
||||||
|
gst_pad_set_chain_function (demux->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_ffmpegdemux_chain));
|
||||||
|
/* task for driving ffmpeg in loop function */
|
||||||
|
demux->task = gst_task_create ((GstTaskFunction) gst_ffmpegdemux_loop, demux);
|
||||||
|
demux->task_lock = g_new (GStaticRecMutex, 1);
|
||||||
|
g_static_rec_mutex_init (demux->task_lock);
|
||||||
|
gst_task_set_lock (demux->task, demux->task_lock);
|
||||||
|
|
||||||
demux->opened = FALSE;
|
demux->opened = FALSE;
|
||||||
demux->context = NULL;
|
demux->context = NULL;
|
||||||
|
|
||||||
|
@ -250,6 +275,29 @@ gst_ffmpegdemux_init (GstFFMpegDemux * demux)
|
||||||
|
|
||||||
demux->seek_event = NULL;
|
demux->seek_event = NULL;
|
||||||
gst_segment_init (&demux->segment, GST_FORMAT_TIME);
|
gst_segment_init (&demux->segment, GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
/* push based data */
|
||||||
|
demux->ffpipe.tlock = g_mutex_new ();
|
||||||
|
demux->ffpipe.cond = g_cond_new ();
|
||||||
|
demux->ffpipe.adapter = gst_adapter_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_ffmpegdemux_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstFFMpegDemux *demux;
|
||||||
|
|
||||||
|
demux = (GstFFMpegDemux *) object;
|
||||||
|
|
||||||
|
g_mutex_free (demux->ffpipe.tlock);
|
||||||
|
g_cond_free (demux->ffpipe.cond);
|
||||||
|
gst_object_unref (demux->ffpipe.adapter);
|
||||||
|
|
||||||
|
gst_object_unref (demux->task);
|
||||||
|
g_static_rec_mutex_free (demux->task_lock);
|
||||||
|
g_free (demux->task_lock);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -436,6 +484,11 @@ gst_ffmpegdemux_perform_seek (GstFFMpegDemux * demux, GstEvent * event)
|
||||||
gboolean update;
|
gboolean update;
|
||||||
GstSegment seeksegment;
|
GstSegment seeksegment;
|
||||||
|
|
||||||
|
if (!demux->seekable) {
|
||||||
|
GST_DEBUG_OBJECT (demux, "in push mode; ignoring seek");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (demux, "starting seek");
|
GST_DEBUG_OBJECT (demux, "starting seek");
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
|
@ -1050,7 +1103,10 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux)
|
||||||
gst_ffmpegdemux_close (demux);
|
gst_ffmpegdemux_close (demux);
|
||||||
|
|
||||||
/* open via our input protocol hack */
|
/* open via our input protocol hack */
|
||||||
location = g_strdup_printf ("gstreamer://%p", demux->sinkpad);
|
if (demux->seekable)
|
||||||
|
location = g_strdup_printf ("gstreamer://%p", demux->sinkpad);
|
||||||
|
else
|
||||||
|
location = g_strdup_printf ("gstpipe://%p", &demux->ffpipe);
|
||||||
GST_DEBUG_OBJECT (demux, "about to call av_open_input_file %s", location);
|
GST_DEBUG_OBJECT (demux, "about to call av_open_input_file %s", location);
|
||||||
|
|
||||||
res = av_open_input_file (&demux->context, location,
|
res = av_open_input_file (&demux->context, location,
|
||||||
|
@ -1178,9 +1234,8 @@ gst_ffmpegdemux_type_find (GstTypeFind * tf, gpointer priv)
|
||||||
|
|
||||||
/* Task */
|
/* Task */
|
||||||
static void
|
static void
|
||||||
gst_ffmpegdemux_loop (GstPad * pad)
|
gst_ffmpegdemux_loop (GstFFMpegDemux * demux)
|
||||||
{
|
{
|
||||||
GstFFMpegDemux *demux;
|
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
gint res;
|
gint res;
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
|
@ -1192,8 +1247,6 @@ gst_ffmpegdemux_loop (GstPad * pad)
|
||||||
gint outsize;
|
gint outsize;
|
||||||
gboolean rawvideo;
|
gboolean rawvideo;
|
||||||
|
|
||||||
demux = (GstFFMpegDemux *) (GST_PAD_PARENT (pad));
|
|
||||||
|
|
||||||
/* open file if we didn't so already */
|
/* open file if we didn't so already */
|
||||||
if (!demux->opened)
|
if (!demux->opened)
|
||||||
if (!gst_ffmpegdemux_open (demux))
|
if (!gst_ffmpegdemux_open (demux))
|
||||||
|
@ -1340,7 +1393,19 @@ pause:
|
||||||
GST_LOG_OBJECT (demux, "pausing task, reason %d (%s)", ret,
|
GST_LOG_OBJECT (demux, "pausing task, reason %d (%s)", ret,
|
||||||
gst_flow_get_name (ret));
|
gst_flow_get_name (ret));
|
||||||
demux->running = FALSE;
|
demux->running = FALSE;
|
||||||
gst_pad_pause_task (demux->sinkpad);
|
if (demux->seekable)
|
||||||
|
gst_pad_pause_task (demux->sinkpad);
|
||||||
|
else {
|
||||||
|
GstFFMpegPipe *ffpipe = &demux->ffpipe;
|
||||||
|
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
/* pause task and make sure loop stops */
|
||||||
|
gst_task_pause (demux->task);
|
||||||
|
g_static_rec_mutex_lock (demux->task_lock);
|
||||||
|
g_static_rec_mutex_unlock (demux->task_lock);
|
||||||
|
demux->ffpipe.srcresult = ret;
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
}
|
||||||
|
|
||||||
if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
|
if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
|
||||||
if (ret == GST_FLOW_UNEXPECTED) {
|
if (ret == GST_FLOW_UNEXPECTED) {
|
||||||
|
@ -1409,6 +1474,127 @@ no_buffer:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_ffmpegdemux_sink_event (GstPad * sinkpad, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstFFMpegDemux *demux;
|
||||||
|
GstFFMpegPipe *ffpipe;
|
||||||
|
gboolean result = TRUE;
|
||||||
|
|
||||||
|
demux = (GstFFMpegDemux *) (GST_PAD_PARENT (sinkpad));
|
||||||
|
ffpipe = &(demux->ffpipe);
|
||||||
|
|
||||||
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_FLUSH_START:
|
||||||
|
/* forward event */
|
||||||
|
gst_pad_event_default (sinkpad, event);
|
||||||
|
|
||||||
|
/* now unblock the chain function */
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
ffpipe->srcresult = GST_FLOW_WRONG_STATE;
|
||||||
|
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
/* loop might run into WRONG_STATE and end itself,
|
||||||
|
* but may also be waiting in a ffmpeg read
|
||||||
|
* trying to break that would make ffmpeg believe eos,
|
||||||
|
* so no harm to have the loop 'pausing' there ... */
|
||||||
|
goto done;
|
||||||
|
case GST_EVENT_FLUSH_STOP:
|
||||||
|
/* forward event */
|
||||||
|
gst_pad_event_default (sinkpad, event);
|
||||||
|
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
gst_adapter_clear (ffpipe->adapter);
|
||||||
|
ffpipe->srcresult = GST_FLOW_OK;
|
||||||
|
/* loop may have decided to end itself as a result of flush WRONG_STATE */
|
||||||
|
gst_task_start (demux->task);
|
||||||
|
demux->running = TRUE;
|
||||||
|
demux->flushing = FALSE;
|
||||||
|
GST_LOG_OBJECT (demux, "loop started");
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
goto done;
|
||||||
|
case GST_EVENT_EOS:
|
||||||
|
/* inform the src task that it can stop now */
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
ffpipe->eos = TRUE;
|
||||||
|
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
/* eat this event for now, task will send eos when finished */
|
||||||
|
gst_event_unref (event);
|
||||||
|
goto done;
|
||||||
|
default:
|
||||||
|
/* for a serialized event, wait until an earlier data is gone,
|
||||||
|
* though this is no guarantee as to when task is done with it */
|
||||||
|
if (GST_EVENT_IS_SERIALIZED (event)) {
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
while (!ffpipe->needed)
|
||||||
|
GST_FFMPEG_PIPE_WAIT (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = gst_pad_event_default (sinkpad, event);
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_ffmpegdemux_chain (GstPad * sinkpad, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
GstFFMpegDemux *demux;
|
||||||
|
GstFFMpegPipe *ffpipe;
|
||||||
|
|
||||||
|
demux = (GstFFMpegDemux *) (GST_PAD_PARENT (sinkpad));
|
||||||
|
ffpipe = &demux->ffpipe;
|
||||||
|
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (ffpipe->eos))
|
||||||
|
goto eos;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (ffpipe->srcresult != GST_FLOW_OK))
|
||||||
|
goto ignore;
|
||||||
|
|
||||||
|
gst_adapter_push (ffpipe->adapter, buffer);
|
||||||
|
buffer = NULL;
|
||||||
|
while (gst_adapter_available (ffpipe->adapter) >= ffpipe->needed) {
|
||||||
|
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_WAIT (ffpipe);
|
||||||
|
/* may have become flushing */
|
||||||
|
if (G_UNLIKELY (ffpipe->srcresult != GST_FLOW_OK))
|
||||||
|
goto ignore;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* special cases */
|
||||||
|
eos:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (demux, "ignoring buffer at end-of-stream");
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
}
|
||||||
|
ignore:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (demux, "ignoring buffer because src task encountered %s",
|
||||||
|
gst_flow_get_name (ffpipe->srcresult));
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
if (buffer)
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_ffmpegdemux_sink_activate (GstPad * sinkpad)
|
gst_ffmpegdemux_sink_activate (GstPad * sinkpad)
|
||||||
{
|
{
|
||||||
|
@ -1428,23 +1614,56 @@ gst_ffmpegdemux_sink_activate (GstPad * sinkpad)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* push mode:
|
||||||
|
* - not seekable
|
||||||
|
* - use gstpipe protocol, like ffmpeg's pipe protocol
|
||||||
|
* - (independently managed) task driving ffmpeg
|
||||||
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_ffmpegdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
|
gst_ffmpegdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
|
||||||
{
|
{
|
||||||
GstFFMpegDemux *demux;
|
GstFFMpegDemux *demux;
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
demux = (GstFFMpegDemux *) (gst_pad_get_parent (sinkpad));
|
demux = (GstFFMpegDemux *) (gst_pad_get_parent (sinkpad));
|
||||||
|
|
||||||
GST_ELEMENT_ERROR (demux, STREAM, NOT_IMPLEMENTED,
|
if (active) {
|
||||||
(NULL),
|
demux->ffpipe.eos = FALSE;
|
||||||
("failed to activate sinkpad in pull mode, push mode not implemented yet"));
|
demux->ffpipe.srcresult = GST_FLOW_OK;
|
||||||
|
demux->ffpipe.needed = 0;
|
||||||
|
demux->running = TRUE;
|
||||||
|
demux->seekable = FALSE;
|
||||||
|
res = gst_task_start (demux->task);
|
||||||
|
} else {
|
||||||
|
GstFFMpegPipe *ffpipe = &demux->ffpipe;
|
||||||
|
|
||||||
|
/* release chain and loop */
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
demux->ffpipe.srcresult = GST_FLOW_WRONG_STATE;
|
||||||
|
/* end streaming by making ffmpeg believe eos */
|
||||||
|
demux->ffpipe.eos = TRUE;
|
||||||
|
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
/* make sure streaming ends */
|
||||||
|
gst_task_stop (demux->task);
|
||||||
|
g_static_rec_mutex_lock (demux->task_lock);
|
||||||
|
g_static_rec_mutex_unlock (demux->task_lock);
|
||||||
|
res = gst_task_join (demux->task);
|
||||||
|
demux->running = FALSE;
|
||||||
|
demux->seekable = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
demux->seekable = FALSE;
|
|
||||||
gst_object_unref (demux);
|
gst_object_unref (demux);
|
||||||
|
|
||||||
return FALSE;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* pull mode:
|
||||||
|
* - seekable
|
||||||
|
* - use gstreamer protocol, like ffmpeg's file protocol
|
||||||
|
* - task driving ffmpeg based on sink pad
|
||||||
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||||
{
|
{
|
||||||
|
@ -1457,7 +1676,7 @@ gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||||
demux->running = TRUE;
|
demux->running = TRUE;
|
||||||
demux->seekable = TRUE;
|
demux->seekable = TRUE;
|
||||||
res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ffmpegdemux_loop,
|
res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ffmpegdemux_loop,
|
||||||
sinkpad);
|
demux);
|
||||||
} else {
|
} else {
|
||||||
demux->running = FALSE;
|
demux->running = FALSE;
|
||||||
res = gst_pad_stop_task (sinkpad);
|
res = gst_pad_stop_task (sinkpad);
|
||||||
|
@ -1494,6 +1713,7 @@ gst_ffmpegdemux_change_state (GstElement * element, GstStateChange transition)
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
gst_ffmpegdemux_close (demux);
|
gst_ffmpegdemux_close (demux);
|
||||||
|
gst_adapter_clear (demux->ffpipe.adapter);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -314,11 +314,11 @@ gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
* our famous codec mapper */
|
* our famous codec mapper */
|
||||||
if (gst_ffmpeg_caps_to_codecid (caps, st->codec) == CODEC_ID_NONE)
|
if (gst_ffmpeg_caps_to_codecid (caps, st->codec) == CODEC_ID_NONE)
|
||||||
goto not_accepted;
|
goto not_accepted;
|
||||||
|
|
||||||
/* copy over the aspect ratios, ffmpeg expects the stream aspect to match the
|
/* copy over the aspect ratios, ffmpeg expects the stream aspect to match the
|
||||||
* codec aspect. */
|
* codec aspect. */
|
||||||
st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
|
st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
|
||||||
|
|
||||||
GST_LOG_OBJECT (pad, "accepted caps %" GST_PTR_FORMAT, caps);
|
GST_LOG_OBJECT (pad, "accepted caps %" GST_PTR_FORMAT, caps);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
|
71
ext/ffmpeg/gstffmpegpipe.h
Normal file
71
ext/ffmpeg/gstffmpegpipe.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __GST_FFMPEGPIPE_H__
|
||||||
|
#define __GST_FFMPEGPIPE_H__
|
||||||
|
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* pipe protocol helpers */
|
||||||
|
#define GST_FFMPEG_PIPE_MUTEX_LOCK(m) G_STMT_START { \
|
||||||
|
GST_LOG_OBJECT (m, "locking tlock from thread %p", g_thread_self ()); \
|
||||||
|
g_mutex_lock (m->tlock); \
|
||||||
|
GST_LOG_OBJECT (m, "locked tlock from thread %p", g_thread_self ()); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define GST_FFMPEG_PIPE_MUTEX_UNLOCK(m) G_STMT_START { \
|
||||||
|
GST_LOG_OBJECT (m, "unlocking tlock from thread %p", g_thread_self ()); \
|
||||||
|
g_mutex_unlock (m->tlock); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define GST_FFMPEG_PIPE_WAIT(m) G_STMT_START { \
|
||||||
|
GST_LOG_OBJECT (m, "thread %p waiting", g_thread_self ()); \
|
||||||
|
g_cond_wait (m->cond, m->tlock); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define GST_FFMPEG_PIPE_SIGNAL(m) G_STMT_START { \
|
||||||
|
GST_LOG_OBJECT (m, "signalling from thread %p", g_thread_self ()); \
|
||||||
|
g_cond_signal (m->cond); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
typedef struct _GstFFMpegPipe GstFFMpegPipe;
|
||||||
|
|
||||||
|
struct _GstFFMpegPipe
|
||||||
|
{
|
||||||
|
/* lock for syncing */
|
||||||
|
GMutex *tlock;
|
||||||
|
/* with TLOCK */
|
||||||
|
/* signals counterpart thread to have a look */
|
||||||
|
GCond *cond;
|
||||||
|
/* seen eos */
|
||||||
|
gboolean eos;
|
||||||
|
/* flowreturn obtained by src task */
|
||||||
|
GstFlowReturn srcresult;
|
||||||
|
/* adpater collecting data */
|
||||||
|
GstAdapter *adapter;
|
||||||
|
/* amount needed in adapter by src task */
|
||||||
|
guint needed;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_FFMPEGPIPE_H__ */
|
|
@ -32,6 +32,7 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
#include "gstffmpeg.h"
|
#include "gstffmpeg.h"
|
||||||
|
#include "gstffmpegpipe.h"
|
||||||
|
|
||||||
typedef struct _GstProtocolInfo GstProtocolInfo;
|
typedef struct _GstProtocolInfo GstProtocolInfo;
|
||||||
|
|
||||||
|
@ -291,3 +292,85 @@ URLProtocol gstreamer_protocol = {
|
||||||
/*.url_seek = */ gst_ffmpegdata_seek,
|
/*.url_seek = */ gst_ffmpegdata_seek,
|
||||||
/*.url_close = */ gst_ffmpegdata_close,
|
/*.url_close = */ gst_ffmpegdata_close,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 != URL_RDONLY) {
|
||||||
|
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;
|
||||||
|
const guint8 *data;
|
||||||
|
guint available;
|
||||||
|
|
||||||
|
ffpipe = (GstFFMpegPipe *) h->priv_data;
|
||||||
|
|
||||||
|
GST_LOG ("requested size %d", size);
|
||||||
|
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||||
|
while ((available = gst_adapter_available (ffpipe->adapter)) < size
|
||||||
|
&& !ffpipe->eos) {
|
||||||
|
ffpipe->needed = size;
|
||||||
|
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||||
|
GST_FFMPEG_PIPE_WAIT (ffpipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
size = MIN (available, size);
|
||||||
|
if (size) {
|
||||||
|
data = gst_adapter_peek (ffpipe->adapter, size);
|
||||||
|
memcpy (buf, data, size);
|
||||||
|
gst_adapter_flush (ffpipe->adapter, size);
|
||||||
|
ffpipe->needed = 0;
|
||||||
|
}
|
||||||
|
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
gst_ffmpeg_pipe_close (URLContext * h)
|
||||||
|
{
|
||||||
|
GST_LOG ("Closing pipe");
|
||||||
|
|
||||||
|
h->priv_data = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
URLProtocol gstpipe_protocol = {
|
||||||
|
"gstpipe",
|
||||||
|
gst_ffmpeg_pipe_open,
|
||||||
|
gst_ffmpeg_pipe_read,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
gst_ffmpeg_pipe_close,
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue