mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
ext/ffmpeg/: Port of FFMpeg muxers to 0.10.
Original commit message from CVS: Patch by: Michal Benes <michal dot benes at xeris dot cz> * ext/ffmpeg/Makefile.am: * ext/ffmpeg/gstffmpeg.c: (plugin_init): * ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_caps_to_codecid): * ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_base_init), (gst_ffmpegmux_init), (gst_ffmpegmux_finalize), (gst_ffmpegmux_request_new_pad), (gst_ffmpegmux_setcaps), (gst_ffmpegmux_collected), (gst_ffmpegmux_change_state), (gst_ffmpegmux_register): Port of FFMpeg muxers to 0.10. Still needs some loving in gstffmpegcodecmap to have them all supported with correct input formats. Closes #332339
This commit is contained in:
parent
df305f763b
commit
5312e1f777
5 changed files with 204 additions and 125 deletions
17
ChangeLog
17
ChangeLog
|
@ -1,3 +1,20 @@
|
|||
2006-09-08 Edward Hervey <edward@fluendo.com>
|
||||
|
||||
Patch by: Michal Benes <michal dot benes at xeris dot cz>
|
||||
|
||||
* ext/ffmpeg/Makefile.am:
|
||||
* ext/ffmpeg/gstffmpeg.c: (plugin_init):
|
||||
* ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_caps_to_codecid):
|
||||
* ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_base_init),
|
||||
(gst_ffmpegmux_init), (gst_ffmpegmux_finalize),
|
||||
(gst_ffmpegmux_request_new_pad), (gst_ffmpegmux_setcaps),
|
||||
(gst_ffmpegmux_collected), (gst_ffmpegmux_change_state),
|
||||
(gst_ffmpegmux_register):
|
||||
Port of FFMpeg muxers to 0.10.
|
||||
Still needs some loving in gstffmpegcodecmap to have them all supported
|
||||
with correct input formats.
|
||||
Closes #332339
|
||||
|
||||
2006-09-06 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
Patch by: Sebastien Moutte <sebastien at moutte dot net>
|
||||
|
|
|
@ -7,9 +7,9 @@ libgstffmpeg_la_SOURCES = gstffmpeg.c \
|
|||
gstffmpegdec.c \
|
||||
gstffmpegcfg.c \
|
||||
gstffmpegdemux.c \
|
||||
gstffmpegmux.c \
|
||||
gstffmpegdeinterlace.c \
|
||||
gstffmpegscale.c
|
||||
# gstffmpegmux.c
|
||||
|
||||
libgstffmpeg_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
|
||||
-I $(top_srcdir)/gst-libs/ext/ffmpeg/libavformat \
|
||||
|
|
|
@ -114,10 +114,10 @@ plugin_init (GstPlugin * plugin)
|
|||
gst_ffmpegenc_register (plugin);
|
||||
gst_ffmpegdec_register (plugin);
|
||||
gst_ffmpegdemux_register (plugin);
|
||||
gst_ffmpegmux_register (plugin);
|
||||
gst_ffmpegdeinterlace_register (plugin);
|
||||
gst_ffmpegscale_register (plugin);
|
||||
#if 0
|
||||
gst_ffmpegmux_register (plugin);
|
||||
gst_ffmpegcsp_register (plugin);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2110,6 +2110,20 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
|
|||
} else if (!strcmp (mimetype, "video/x-h264")) {
|
||||
id = CODEC_ID_H264;
|
||||
video = TRUE;
|
||||
} else if (!strcmp (mimetype, "video/x-flash-video")) {
|
||||
gint flvversion = 0;
|
||||
|
||||
if ((gst_structure_get_int (structure, "flvversion", &flvversion))) {
|
||||
switch (flvversion) {
|
||||
case 1:
|
||||
id = CODEC_ID_FLV1;
|
||||
video = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!strncmp (mimetype, "audio/x-gst_ff-", 15) ) {
|
||||
gchar ext[16];
|
||||
AVCodec *codec;
|
||||
|
@ -2151,6 +2165,11 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
|
|||
|
||||
GST_DEBUG ("The id=%d belongs to the caps %s", id, str);
|
||||
g_free (str);
|
||||
} else {
|
||||
gchar * str = gst_caps_to_string (caps);
|
||||
|
||||
GST_WARNING ("Couldn't figure out the id for caps %s", str);
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
|
@ -29,16 +29,29 @@
|
|||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstcollectpads.h>
|
||||
|
||||
#include "gstffmpeg.h"
|
||||
#include "gstffmpegcodecmap.h"
|
||||
|
||||
typedef struct _GstFFMpegMux GstFFMpegMux;
|
||||
typedef struct _GstFFMpegMuxPad GstFFMpegMuxPad;
|
||||
|
||||
struct _GstFFMpegMuxPad
|
||||
{
|
||||
GstCollectData collect; /* we extend the CollectData */
|
||||
|
||||
gint padnum;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct _GstFFMpegMux
|
||||
{
|
||||
GstElement element;
|
||||
|
||||
GstCollectPads *collect;
|
||||
/* We need to keep track of our pads, so we do so here. */
|
||||
GstPad *srcpad;
|
||||
|
||||
|
@ -47,10 +60,10 @@ struct _GstFFMpegMux
|
|||
|
||||
GstTagList *tags;
|
||||
|
||||
GstPad *sinkpads[MAX_STREAMS];
|
||||
gint videopads, audiopads;
|
||||
GstBuffer *bufferqueue[MAX_STREAMS];
|
||||
gboolean eos[MAX_STREAMS];
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegMuxClassParams
|
||||
|
@ -95,15 +108,14 @@ static GHashTable *global_plugins;
|
|||
|
||||
/* A number of functon prototypes are given so we can refer to them later. */
|
||||
static void gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass);
|
||||
static void gst_ffmpegmux_base_init (GstFFMpegMuxClass * klass);
|
||||
static void gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux);
|
||||
static void gst_ffmpegmux_base_init (gpointer g_class);
|
||||
static void gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class);
|
||||
static void gst_ffmpegmux_finalize (GObject * object);
|
||||
|
||||
static GstPadLinkReturn
|
||||
gst_ffmpegmux_connect (GstPad * pad, const GstCaps * caps);
|
||||
static gboolean gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps);
|
||||
static GstPad *gst_ffmpegmux_request_new_pad (GstElement * element,
|
||||
GstPadTemplate * templ, const gchar * name);
|
||||
static void gst_ffmpegmux_loop (GstElement * element);
|
||||
static GstFlowReturn gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data);
|
||||
|
||||
static GstStateChangeReturn gst_ffmpegmux_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
@ -113,10 +125,11 @@ static GstElementClass *parent_class = NULL;
|
|||
/*static guint gst_ffmpegmux_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_base_init (GstFFMpegMuxClass * klass)
|
||||
gst_ffmpegmux_base_init (gpointer g_class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
||||
GstFFMpegMuxClass *klass = (GstFFMpegMuxClass *) g_class;
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||
GstElementDetails details;
|
||||
GstFFMpegMuxClassParams *params;
|
||||
GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl;
|
||||
|
@ -172,16 +185,19 @@ gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass)
|
|||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux)
|
||||
gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class)
|
||||
{
|
||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (ffmpegmux);
|
||||
GstElementClass *klass = GST_ELEMENT_CLASS (g_class);
|
||||
GstFFMpegMuxClass *oclass = (GstFFMpegMuxClass *) klass;
|
||||
GstPadTemplate *templ = gst_element_class_get_pad_template (klass, "src");
|
||||
|
||||
ffmpegmux->srcpad = gst_pad_new_from_template (templ, "src");
|
||||
gst_element_set_loop_function (GST_ELEMENT (ffmpegmux), gst_ffmpegmux_loop);
|
||||
gst_element_add_pad (GST_ELEMENT (ffmpegmux), ffmpegmux->srcpad);
|
||||
|
||||
ffmpegmux->collect = gst_collect_pads_new ();
|
||||
gst_collect_pads_set_function (ffmpegmux->collect,
|
||||
(GstCollectPadsFunction) gst_ffmpegmux_collected, ffmpegmux);
|
||||
|
||||
ffmpegmux->context = g_new0 (AVFormatContext, 1);
|
||||
ffmpegmux->context->oformat = oclass->in_plugin;
|
||||
ffmpegmux->context->nb_streams = 0;
|
||||
|
@ -202,6 +218,7 @@ gst_ffmpegmux_finalize (GObject * object)
|
|||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) object;
|
||||
|
||||
g_free (ffmpegmux->context);
|
||||
gst_object_unref (ffmpegmux->collect);
|
||||
|
||||
if (G_OBJECT_CLASS (parent_class)->finalize)
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
|
@ -214,11 +231,12 @@ gst_ffmpegmux_request_new_pad (GstElement * element,
|
|||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
|
||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
|
||||
GstFFMpegMuxClass *oclass = (GstFFMpegMuxClass *) klass;
|
||||
GstFFMpegMuxPad *collect_pad;
|
||||
gchar *padname;
|
||||
GstPad *pad;
|
||||
AVStream *st;
|
||||
enum CodecType type;
|
||||
gint padnum, bitrate = 0, framesize = 0;
|
||||
gint bitrate = 0, framesize = 0;
|
||||
|
||||
g_return_val_if_fail (templ != NULL, NULL);
|
||||
g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
|
||||
|
@ -241,20 +259,20 @@ gst_ffmpegmux_request_new_pad (GstElement * element,
|
|||
|
||||
/* create pad */
|
||||
pad = gst_pad_new_from_template (templ, padname);
|
||||
padnum = ffmpegmux->context->nb_streams;
|
||||
ffmpegmux->sinkpads[padnum] = pad;
|
||||
gst_pad_set_link_function (pad, gst_ffmpegmux_connect);
|
||||
collect_pad = (GstFFMpegMuxPad *)
|
||||
gst_collect_pads_add_pad (ffmpegmux->collect, pad, sizeof (GstFFMpegMuxPad));
|
||||
collect_pad->padnum = ffmpegmux->context->nb_streams;
|
||||
|
||||
gst_pad_set_setcaps_function (pad, gst_ffmpegmux_setcaps);
|
||||
gst_element_add_pad (element, pad);
|
||||
|
||||
/* AVStream needs to be created */
|
||||
st = av_new_stream (ffmpegmux->context, padnum);
|
||||
st = av_new_stream (ffmpegmux->context, collect_pad->padnum);
|
||||
st->codec->codec_type = type;
|
||||
st->codec->codec_id = CODEC_ID_NONE; /* this is a check afterwards */
|
||||
st->stream_copy = 1; /* we're not the actual encoder */
|
||||
st->codec->bit_rate = bitrate;
|
||||
st->codec->frame_size = framesize;
|
||||
st->time_base.den = GST_SECOND;
|
||||
st->time_base.num = 1;
|
||||
/* we fill in codec during capsnego */
|
||||
|
||||
/* we love debug output (c) (tm) (r) */
|
||||
|
@ -265,63 +283,40 @@ gst_ffmpegmux_request_new_pad (GstElement * element,
|
|||
return pad;
|
||||
}
|
||||
|
||||
static GstPadLinkReturn
|
||||
gst_ffmpegmux_connect (GstPad * pad, const GstCaps * caps)
|
||||
/**
|
||||
* gst_ffmpegmux_setcaps
|
||||
* @pad: #GstPad
|
||||
* @caps: New caps.
|
||||
*
|
||||
* Set caps to pad.
|
||||
*
|
||||
* Returns: #TRUE on success.
|
||||
*/
|
||||
static gboolean
|
||||
gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (gst_pad_get_parent (pad));
|
||||
gint i;
|
||||
GstFFMpegMuxPad *collect_pad;
|
||||
AVStream *st;
|
||||
|
||||
/*g_return_val_if_fail (ffmpegmux->opened == FALSE,
|
||||
GST_PAD_LINK_REFUSED); */
|
||||
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
|
||||
if (pad == ffmpegmux->sinkpads[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ffmpegmux->context->nb_streams) {
|
||||
g_warning ("Unknown pad given during capsnego: %p", pad);
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
}
|
||||
st = ffmpegmux->context->streams[i];
|
||||
collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
|
||||
|
||||
st = ffmpegmux->context->streams[collect_pad->padnum];
|
||||
|
||||
/* for the format-specific guesses, we'll go to
|
||||
* our famous codec mapper */
|
||||
if (gst_ffmpeg_caps_to_codecid (caps, st->codec) != CODEC_ID_NONE) {
|
||||
ffmpegmux->eos[i] = FALSE;
|
||||
return GST_PAD_LINK_OK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_loop (GstElement * element)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
|
||||
gint i, bufnum;
|
||||
|
||||
/* start by filling an internal queue of buffers */
|
||||
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
|
||||
GstPad *pad = ffmpegmux->sinkpads[i];
|
||||
/* This is old tags handling code. It is not yet ported to 0.10 because
|
||||
collectpads do not support events easily. */
|
||||
|
||||
/* check for "pull'ability" */
|
||||
while (pad != NULL &&
|
||||
GST_PAD_IS_USABLE (pad) &&
|
||||
ffmpegmux->eos[i] == FALSE && ffmpegmux->bufferqueue[i] == NULL) {
|
||||
GstData *data;
|
||||
|
||||
/* we can pull a buffer! */
|
||||
data = gst_pad_pull (pad);
|
||||
if (GST_IS_EVENT (data)) {
|
||||
GstEvent *event = GST_EVENT (data);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_EOS:
|
||||
/* flag EOS on this stream */
|
||||
ffmpegmux->eos[i] = TRUE;
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
#if 0
|
||||
case GST_EVENT_TAG:
|
||||
if (ffmpegmux->tags) {
|
||||
gst_tag_list_insert (ffmpegmux->tags,
|
||||
|
@ -332,41 +327,52 @@ gst_ffmpegmux_loop (GstElement * element)
|
|||
}
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
default:
|
||||
gst_pad_event_default (pad, event);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ffmpegmux->bufferqueue[i] = GST_BUFFER (data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data;
|
||||
GSList *collected;
|
||||
GstFFMpegMuxPad *best_pad;
|
||||
GstClockTime best_time;
|
||||
|
||||
/* open "file" (gstreamer protocol to next element) */
|
||||
if (!ffmpegmux->opened) {
|
||||
const GstTagList *iface_tags;
|
||||
int open_flags = URL_WRONLY;
|
||||
|
||||
/* we do need all streams to have started capsnego,
|
||||
* or things will go horribly wrong */
|
||||
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
|
||||
AVStream *st = ffmpegmux->context->streams[i];
|
||||
for (collected = ffmpegmux->collect->data; collected;
|
||||
collected = g_slist_next (collected)) {
|
||||
GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
|
||||
AVStream *st = ffmpegmux->context->streams[collect_pad->padnum];
|
||||
|
||||
/* check whether the pad has successfully completed capsnego */
|
||||
if (st->codec->codec_id == CODEC_ID_NONE) {
|
||||
GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
|
||||
("no caps set on stream %d (%s)", i,
|
||||
GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL),
|
||||
("no caps set on stream %d (%s)", collect_pad->padnum,
|
||||
(st->codec->codec_type == CODEC_TYPE_VIDEO) ?
|
||||
"video" : "audio"));
|
||||
return;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
/* set framerate for audio */
|
||||
if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
|
||||
st->codec->frame_size =
|
||||
st->codec->sample_rate *
|
||||
GST_BUFFER_DURATION (ffmpegmux->bufferqueue[i]) / GST_SECOND;
|
||||
GstBuffer *buffer;
|
||||
|
||||
buffer = gst_collect_pads_peek (ffmpegmux->collect,
|
||||
(GstCollectData *)collect_pad);
|
||||
if (buffer) {
|
||||
st->codec->frame_size =
|
||||
st->codec->sample_rate *
|
||||
GST_BUFFER_DURATION (buffer) / GST_SECOND;
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* TODO: tags not ported to gst 0.10 */
|
||||
/* tags */
|
||||
iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (ffmpegmux));
|
||||
if (ffmpegmux->tags || iface_tags) {
|
||||
|
@ -413,23 +419,24 @@ gst_ffmpegmux_loop (GstElement * element)
|
|||
}
|
||||
gst_tag_list_free (tags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set the streamheader flag for gstffmpegprotocol if codec supports it */
|
||||
if (!strcmp (ffmpegmux->context->oformat->name, "flv") ) {
|
||||
open_flags |= GST_FFMPEG_URL_STREAMHEADER;
|
||||
}
|
||||
/* set the streamheader flag for gstffmpegprotocol if codec supports it */
|
||||
if (!strcmp (ffmpegmux->context->oformat->name, "flv") ) {
|
||||
open_flags |= GST_FFMPEG_URL_STREAMHEADER;
|
||||
}
|
||||
|
||||
if (url_fopen (&ffmpegmux->context->pb,
|
||||
ffmpegmux->context->filename, open_flags) < 0) {
|
||||
GST_ELEMENT_ERROR (element, LIBRARY, TOO_LAZY, (NULL),
|
||||
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
|
||||
("Failed to open stream context in ffmux"));
|
||||
return;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
|
||||
GST_ELEMENT_ERROR (element, LIBRARY, INIT, (NULL),
|
||||
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, INIT, (NULL),
|
||||
("Failed to initialize muxer"));
|
||||
return;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* we're now opened */
|
||||
|
@ -437,9 +444,9 @@ gst_ffmpegmux_loop (GstElement * element)
|
|||
|
||||
/* now open the mux format */
|
||||
if (av_write_header (ffmpegmux->context) < 0) {
|
||||
GST_ELEMENT_ERROR (element, LIBRARY, SETTINGS, (NULL),
|
||||
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL),
|
||||
("Failed to write file header - check codec settings"));
|
||||
return;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* flush the header so it will be used as streamheader */
|
||||
|
@ -448,49 +455,62 @@ gst_ffmpegmux_loop (GstElement * element)
|
|||
|
||||
/* take the one with earliest timestamp,
|
||||
* and push it forward */
|
||||
bufnum = -1;
|
||||
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
|
||||
best_pad = NULL;
|
||||
best_time = GST_CLOCK_TIME_NONE;
|
||||
for (collected = ffmpegmux->collect->data; collected;
|
||||
collected = g_slist_next (collected)) {
|
||||
GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
|
||||
GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect,
|
||||
(GstCollectData *)collect_pad);
|
||||
|
||||
/* if there's no buffer, just continue */
|
||||
if (ffmpegmux->bufferqueue[i] == NULL) {
|
||||
if (buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if we have no buffer yet, just use the first one */
|
||||
if (bufnum == -1) {
|
||||
bufnum = i;
|
||||
continue;
|
||||
if (best_pad == NULL) {
|
||||
best_pad = collect_pad;
|
||||
best_time = GST_BUFFER_TIMESTAMP (buffer);
|
||||
goto next_pad;
|
||||
}
|
||||
|
||||
/* if we do have one, only use this one if it's older */
|
||||
if (GST_BUFFER_TIMESTAMP (ffmpegmux->bufferqueue[i]) <
|
||||
GST_BUFFER_TIMESTAMP (ffmpegmux->bufferqueue[bufnum])) {
|
||||
bufnum = i;
|
||||
if (GST_BUFFER_TIMESTAMP (buffer) < best_time) {
|
||||
best_time = GST_BUFFER_TIMESTAMP (buffer);
|
||||
best_pad = collect_pad;
|
||||
}
|
||||
|
||||
next_pad:
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
/* Mux buffers with invalid timestamp first */
|
||||
if (!GST_CLOCK_TIME_IS_VALID (best_time))
|
||||
break;
|
||||
}
|
||||
|
||||
/* now handle the buffer, or signal EOS if we have
|
||||
* no buffers left */
|
||||
if (bufnum >= 0) {
|
||||
if (best_pad != NULL) {
|
||||
GstBuffer *buf;
|
||||
AVPacket pkt;
|
||||
AVRational bq = { 1, 1000000000 };
|
||||
|
||||
/* push out current buffer */
|
||||
buf = ffmpegmux->bufferqueue[bufnum];
|
||||
ffmpegmux->bufferqueue[bufnum] = NULL;
|
||||
buf = gst_collect_pads_pop (ffmpegmux->collect,
|
||||
(GstCollectData *)best_pad);
|
||||
|
||||
ffmpegmux->context->streams[bufnum]->codec->frame_number++;
|
||||
ffmpegmux->context->streams[best_pad->padnum]->codec->frame_number++;
|
||||
|
||||
/* set time */
|
||||
pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
|
||||
ffmpegmux->context->streams[bufnum]->time_base);
|
||||
ffmpegmux->context->streams[best_pad->padnum]->time_base);
|
||||
pkt.dts = pkt.pts;
|
||||
pkt.data = GST_BUFFER_DATA (buf);
|
||||
pkt.size = GST_BUFFER_SIZE (buf);
|
||||
pkt.stream_index = bufnum;
|
||||
pkt.stream_index = best_pad->padnum;
|
||||
pkt.flags = 0;
|
||||
|
||||
if (!(GST_BUFFER_FLAGS (buf) & GST_BUFFER_DELTA_UNIT))
|
||||
if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
|
||||
pkt.flags |= PKT_FLAG_KEY;
|
||||
|
||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||
|
@ -503,18 +523,40 @@ gst_ffmpegmux_loop (GstElement * element)
|
|||
/* close down */
|
||||
av_write_trailer (ffmpegmux->context);
|
||||
ffmpegmux->opened = FALSE;
|
||||
put_flush_packet (&ffmpegmux->context->pb);
|
||||
url_fclose (&ffmpegmux->context->pb);
|
||||
gst_element_set_eos (element);
|
||||
gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
|
||||
return GST_FLOW_UNEXPECTED;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstFlowReturn ret;
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
gst_collect_pads_start (ffmpegmux->collect);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_collect_pads_stop (ffmpegmux->collect);
|
||||
if (ffmpegmux->tags) {
|
||||
gst_tag_list_free (ffmpegmux->tags);
|
||||
ffmpegmux->tags = NULL;
|
||||
|
@ -524,12 +566,13 @@ gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition)
|
|||
url_fclose (&ffmpegmux->context->pb);
|
||||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (GST_ELEMENT_CLASS (parent_class)->change_state)
|
||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
return GST_STATE_CHANGE_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
|
@ -585,7 +628,7 @@ gst_ffmpegmux_register (GstPlugin * plugin)
|
|||
}
|
||||
if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
|
||||
&video_ids, &audio_ids)) {
|
||||
gst_caps_free (srccaps);
|
||||
gst_caps_unref (srccaps);
|
||||
goto next;
|
||||
}
|
||||
videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
|
||||
|
@ -605,11 +648,11 @@ gst_ffmpegmux_register (GstPlugin * plugin)
|
|||
/* if it's already registered, drop it */
|
||||
if (g_type_from_name (type_name)) {
|
||||
g_free (type_name);
|
||||
gst_caps_free (srccaps);
|
||||
gst_caps_unref (srccaps);
|
||||
if (audiosinkcaps)
|
||||
gst_caps_free (audiosinkcaps);
|
||||
gst_caps_unref (audiosinkcaps);
|
||||
if (videosinkcaps)
|
||||
gst_caps_free (videosinkcaps);
|
||||
gst_caps_unref (videosinkcaps);
|
||||
goto next;
|
||||
}
|
||||
|
||||
|
@ -629,11 +672,11 @@ gst_ffmpegmux_register (GstPlugin * plugin)
|
|||
&tag_setter_info);
|
||||
if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type)) {
|
||||
g_free (type_name);
|
||||
gst_caps_free (srccaps);
|
||||
gst_caps_unref (srccaps);
|
||||
if (audiosinkcaps)
|
||||
gst_caps_free (audiosinkcaps);
|
||||
gst_caps_unref (audiosinkcaps);
|
||||
if (videosinkcaps)
|
||||
gst_caps_free (videosinkcaps);
|
||||
gst_caps_unref (videosinkcaps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue