diff --git a/ext/ffmpeg/Makefile.am b/ext/ffmpeg/Makefile.am index bae2a6d440..ac4a7e8aa9 100644 --- a/ext/ffmpeg/Makefile.am +++ b/ext/ffmpeg/Makefile.am @@ -11,7 +11,9 @@ libgstlibav_la_SOURCES = gstffmpeg.c \ gstffmpegcodecmap.c \ gstffmpegutils.c \ gstffmpegenc.c \ + gstffmpegvidenc.c \ gstffmpegdec.c \ + gstffmpegviddec.c \ gstffmpegcfg.c \ gstffmpegdemux.c \ gstffmpegmux.c \ @@ -39,5 +41,6 @@ noinst_HEADERS = \ gstffmpegcodecmap.h \ gstffmpegutils.h \ gstffmpegenc.h \ + gstffmpegvidenc.h \ gstffmpegcfg.h \ gstffmpegpipe.h diff --git a/ext/ffmpeg/gstffmpeg.c b/ext/ffmpeg/gstffmpeg.c index 899d4bea11..a0e79d01d3 100644 --- a/ext/ffmpeg/gstffmpeg.c +++ b/ext/ffmpeg/gstffmpeg.c @@ -138,8 +138,10 @@ plugin_init (GstPlugin * plugin) av_register_all (); - gst_ffmpegenc_register (plugin); - gst_ffmpegdec_register (plugin); + gst_ffmpegaudenc_register (plugin); + gst_ffmpegvidenc_register (plugin); + gst_ffmpegauddec_register (plugin); + gst_ffmpegviddec_register (plugin); gst_ffmpegdemux_register (plugin); gst_ffmpegmux_register (plugin); gst_ffmpegdeinterlace_register (plugin); diff --git a/ext/ffmpeg/gstffmpeg.h b/ext/ffmpeg/gstffmpeg.h index 602e1f1919..22ebc07168 100644 --- a/ext/ffmpeg/gstffmpeg.h +++ b/ext/ffmpeg/gstffmpeg.h @@ -44,8 +44,10 @@ extern gboolean _shut_up_I_am_probing; #endif extern gboolean gst_ffmpegdemux_register (GstPlugin * plugin); -extern gboolean gst_ffmpegdec_register (GstPlugin * plugin); -extern gboolean gst_ffmpegenc_register (GstPlugin * plugin); +extern gboolean gst_ffmpegauddec_register (GstPlugin * plugin); +extern gboolean gst_ffmpegviddec_register (GstPlugin * plugin); +extern gboolean gst_ffmpegaudenc_register (GstPlugin * plugin); +extern gboolean gst_ffmpegvidenc_register (GstPlugin * plugin); extern gboolean gst_ffmpegmux_register (GstPlugin * plugin); extern gboolean gst_ffmpegcsp_register (GstPlugin * plugin); #if 0 diff --git a/ext/ffmpeg/gstffmpegcfg.c b/ext/ffmpeg/gstffmpegcfg.c index 5d83281a68..4dee8adc57 100644 --- a/ext/ffmpeg/gstffmpegcfg.c +++ b/ext/ffmpeg/gstffmpegcfg.c @@ -26,7 +26,7 @@ #endif #include "gstffmpeg.h" -#include "gstffmpegenc.h" +#include "gstffmpegvidenc.h" #include "gstffmpegcfg.h" #include @@ -324,7 +324,7 @@ struct _GParamSpecData /* properties whose member offset is higher than the config base * can be copied directly at context configuration time; * and can also retrieve a default value from lavc */ -#define CONTEXT_CONFIG_OFFSET G_STRUCT_OFFSET (GstFFMpegEnc, config) +#define CONTEXT_CONFIG_OFFSET G_STRUCT_OFFSET (GstFFMpegVidEnc, config) /* additional info is named pointer specified by the quark */ static GQuark quark; @@ -339,7 +339,7 @@ static GList *property_list; default, include, exclude) \ G_STMT_START { \ GParamSpecData *_qdata = g_new0 (GParamSpecData, 1); \ - GstFFMpegEnc _enc; \ + GstFFMpegVidEnc _enc; \ _qdata->offset = G_STRUCT_OFFSET (struct_type, member); \ _qdata->size = sizeof (_enc.member); \ _qdata->lavc_default = default; \ @@ -350,7 +350,7 @@ G_STMT_START { \ } G_STMT_END #define gst_ffmpeg_add_pspec(pspec, member, default, include, exclude) \ - gst_ffmpeg_add_pspec_full (pspec, property_list, GstFFMpegEnc, member, \ + gst_ffmpeg_add_pspec_full (pspec, property_list, GstFFMpegVidEnc, member, \ default, include, exclude) /* ==== BEGIN CONFIGURATION SECTION ==== */ @@ -755,7 +755,7 @@ gst_ffmpeg_cfg_codec_has_pspec (enum CodecID codec_id, GParamSpec * pspec) /* install all properties for klass that have been registered in property_list */ void -gst_ffmpeg_cfg_install_property (GstFFMpegEncClass * klass, guint base) +gst_ffmpeg_cfg_install_property (GstFFMpegVidEncClass * klass, guint base) { GParamSpec *pspec; GList *list; @@ -878,7 +878,7 @@ gboolean gst_ffmpeg_cfg_set_property (GObject * object, const GValue * value, GParamSpec * pspec) { - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (object); + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) (object); GParamSpecData *qdata; qdata = g_param_spec_get_qdata (pspec, quark); @@ -941,7 +941,7 @@ gboolean gst_ffmpeg_cfg_get_property (GObject * object, GValue * value, GParamSpec * pspec) { - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (object); + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) (object); GParamSpecData *qdata; qdata = g_param_spec_get_qdata (pspec, quark); @@ -996,7 +996,7 @@ gst_ffmpeg_cfg_get_property (GObject * object, } void -gst_ffmpeg_cfg_set_defaults (GstFFMpegEnc * ffmpegenc) +gst_ffmpeg_cfg_set_defaults (GstFFMpegVidEnc * ffmpegenc) { GParamSpec **pspecs; guint num_props, i; @@ -1024,10 +1024,11 @@ gst_ffmpeg_cfg_set_defaults (GstFFMpegEnc * ffmpegenc) void -gst_ffmpeg_cfg_fill_context (GstFFMpegEnc * ffmpegenc, AVCodecContext * context) +gst_ffmpeg_cfg_fill_context (GstFFMpegVidEnc * ffmpegenc, + AVCodecContext * context) { - GstFFMpegEncClass *klass - = (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); + GstFFMpegVidEncClass *klass + = (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); GParamSpec *pspec; GParamSpecData *qdata; GList *list; @@ -1059,7 +1060,7 @@ gst_ffmpeg_cfg_fill_context (GstFFMpegEnc * ffmpegenc, AVCodecContext * context) } void -gst_ffmpeg_cfg_finalize (GstFFMpegEnc * ffmpegenc) +gst_ffmpeg_cfg_finalize (GstFFMpegVidEnc * ffmpegenc) { GParamSpec **pspecs; guint num_props, i; diff --git a/ext/ffmpeg/gstffmpegcfg.h b/ext/ffmpeg/gstffmpegcfg.h index 1dcaade9ab..5251eb26bf 100644 --- a/ext/ffmpeg/gstffmpegcfg.h +++ b/ext/ffmpeg/gstffmpegcfg.h @@ -25,7 +25,7 @@ G_BEGIN_DECLS void gst_ffmpeg_cfg_init (void); -void gst_ffmpeg_cfg_install_property (GstFFMpegEncClass * klass, guint base); +void gst_ffmpeg_cfg_install_property (GstFFMpegVidEncClass * klass, guint base); gboolean gst_ffmpeg_cfg_set_property (GObject * object, const GValue * value, GParamSpec * pspec); @@ -33,9 +33,9 @@ gboolean gst_ffmpeg_cfg_set_property (GObject * object, gboolean gst_ffmpeg_cfg_get_property (GObject * object, GValue * value, GParamSpec * pspec); -void gst_ffmpeg_cfg_fill_context (GstFFMpegEnc * ffmpegenc, AVCodecContext * context); -void gst_ffmpeg_cfg_set_defaults (GstFFMpegEnc * ffmpegenc); -void gst_ffmpeg_cfg_finalize (GstFFMpegEnc * ffmpegenc); +void gst_ffmpeg_cfg_fill_context (GstFFMpegVidEnc * ffmpegenc, AVCodecContext * context); +void gst_ffmpeg_cfg_set_defaults (GstFFMpegVidEnc * ffmpegenc); +void gst_ffmpeg_cfg_finalize (GstFFMpegVidEnc * ffmpegenc); G_END_DECLS diff --git a/ext/ffmpeg/gstffmpegcodecmap.c b/ext/ffmpeg/gstffmpegcodecmap.c index 6d03e73183..bb30875cdf 100644 --- a/ext/ffmpeg/gstffmpegcodecmap.c +++ b/ext/ffmpeg/gstffmpegcodecmap.c @@ -2076,6 +2076,128 @@ gst_ffmpeg_caps_to_pixfmt (const GstCaps * caps, } } +typedef struct +{ + GstVideoFormat format; + enum PixelFormat pixfmt; +} PixToFmt; + +/* FIXME : FILLME */ +static const PixToFmt pixtofmttable[] = { + /* GST_VIDEO_FORMAT_I420, */ + {GST_VIDEO_FORMAT_I420, PIX_FMT_YUV420P}, + /* Note : this should use a different chroma placement */ + {GST_VIDEO_FORMAT_I420, PIX_FMT_YUVJ420P}, + + /* GST_VIDEO_FORMAT_YV12, */ + /* GST_VIDEO_FORMAT_YUY2, */ + {GST_VIDEO_FORMAT_YUY2, PIX_FMT_YUYV422}, + /* GST_VIDEO_FORMAT_UYVY, */ + {GST_VIDEO_FORMAT_UYVY, PIX_FMT_UYVY422}, + /* GST_VIDEO_FORMAT_AYUV, */ + /* GST_VIDEO_FORMAT_RGBx, */ + /* GST_VIDEO_FORMAT_BGRx, */ + /* GST_VIDEO_FORMAT_xRGB, */ + /* GST_VIDEO_FORMAT_xBGR, */ + /* GST_VIDEO_FORMAT_RGBA, */ + {GST_VIDEO_FORMAT_RGBA, PIX_FMT_RGB32}, + /* GST_VIDEO_FORMAT_BGRA, */ + {GST_VIDEO_FORMAT_BGRA, PIX_FMT_BGR32}, + /* GST_VIDEO_FORMAT_ARGB, */ + /* GST_VIDEO_FORMAT_ABGR, */ + /* GST_VIDEO_FORMAT_RGB, */ + {GST_VIDEO_FORMAT_RGB, PIX_FMT_RGB24}, + /* GST_VIDEO_FORMAT_BGR, */ + {GST_VIDEO_FORMAT_BGR, PIX_FMT_BGR24}, + /* GST_VIDEO_FORMAT_Y41B, */ + {GST_VIDEO_FORMAT_Y41B, PIX_FMT_YUV410P}, + /* GST_VIDEO_FORMAT_Y42B, */ + {GST_VIDEO_FORMAT_Y42B, PIX_FMT_YUV422P}, + {GST_VIDEO_FORMAT_Y42B, PIX_FMT_YUVJ422P}, + /* GST_VIDEO_FORMAT_YVYU, */ + /* GST_VIDEO_FORMAT_Y444, */ + {GST_VIDEO_FORMAT_Y444, PIX_FMT_YUV444P}, + {GST_VIDEO_FORMAT_Y444, PIX_FMT_YUVJ444P}, + /* GST_VIDEO_FORMAT_v210, */ + /* GST_VIDEO_FORMAT_v216, */ + /* GST_VIDEO_FORMAT_NV12, */ + {GST_VIDEO_FORMAT_NV12, PIX_FMT_NV12}, + /* GST_VIDEO_FORMAT_NV21, */ + {GST_VIDEO_FORMAT_NV21, PIX_FMT_NV21}, + /* GST_VIDEO_FORMAT_GRAY8, */ + {GST_VIDEO_FORMAT_GRAY8, PIX_FMT_GRAY8}, + /* GST_VIDEO_FORMAT_GRAY16_BE, */ + {GST_VIDEO_FORMAT_GRAY16_BE, PIX_FMT_GRAY16BE}, + /* GST_VIDEO_FORMAT_GRAY16_LE, */ + {GST_VIDEO_FORMAT_GRAY16_LE, PIX_FMT_GRAY16LE}, + /* GST_VIDEO_FORMAT_v308, */ + /* GST_VIDEO_FORMAT_Y800, */ + /* GST_VIDEO_FORMAT_Y16, */ + /* GST_VIDEO_FORMAT_RGB16, */ + {GST_VIDEO_FORMAT_RGB16, PIX_FMT_RGB565}, + /* GST_VIDEO_FORMAT_BGR16, */ + /* GST_VIDEO_FORMAT_RGB15, */ + {GST_VIDEO_FORMAT_RGB15, PIX_FMT_RGB555}, + /* GST_VIDEO_FORMAT_BGR15, */ + /* GST_VIDEO_FORMAT_UYVP, */ + /* GST_VIDEO_FORMAT_A420, */ + {GST_VIDEO_FORMAT_A420, PIX_FMT_YUVA420P}, + /* GST_VIDEO_FORMAT_RGB8_PALETTED, */ + {GST_VIDEO_FORMAT_RGB8P, PIX_FMT_PAL8}, + /* GST_VIDEO_FORMAT_YUV9, */ + /* GST_VIDEO_FORMAT_YVU9, */ + /* GST_VIDEO_FORMAT_IYU1, */ + /* GST_VIDEO_FORMAT_ARGB64, */ + /* GST_VIDEO_FORMAT_AYUV64, */ + /* GST_VIDEO_FORMAT_r210, */ +}; + +GstVideoFormat +gst_ffmpeg_pixfmt_to_videoformat (enum PixelFormat pixfmt) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (pixtofmttable); i++) + if (pixtofmttable[i].pixfmt == pixfmt) + return pixtofmttable[i].format; + + GST_WARNING ("Unknown pixel format %d", pixfmt); + return GST_VIDEO_FORMAT_UNKNOWN; +} + +enum PixelFormat +gst_ffmpeg_videoformat_to_pixfmt (GstVideoFormat format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (pixtofmttable); i++) + if (pixtofmttable[i].format == format) + return pixtofmttable[i].pixfmt; + return PIX_FMT_NONE; +} + +void +gst_ffmpeg_videoinfo_to_context (GstVideoInfo * info, AVCodecContext * context) +{ + gint i, bpp = 0; + + context->width = GST_VIDEO_INFO_WIDTH (info); + context->height = GST_VIDEO_INFO_HEIGHT (info); + for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (info); i++) + bpp += GST_VIDEO_INFO_COMP_DEPTH (info, i); + context->bits_per_coded_sample = bpp; + + context->ticks_per_frame = 1; + context->time_base.den = GST_VIDEO_INFO_FPS_N (info); + context->time_base.num = GST_VIDEO_INFO_FPS_D (info); + + context->sample_aspect_ratio.num = GST_VIDEO_INFO_PAR_N (info); + context->sample_aspect_ratio.den = GST_VIDEO_INFO_PAR_D (info); + + context->pix_fmt = + gst_ffmpeg_videoformat_to_pixfmt (GST_VIDEO_INFO_FORMAT (info)); +} + /* Convert a GstCaps and a FFMPEG codec Type to a * AVCodecContext. If the context is ommitted, no fixed values * for video/audio size will be included in the context diff --git a/ext/ffmpeg/gstffmpegcodecmap.h b/ext/ffmpeg/gstffmpegcodecmap.h index 2c49afa869..2039631617 100644 --- a/ext/ffmpeg/gstffmpegcodecmap.h +++ b/ext/ffmpeg/gstffmpegcodecmap.h @@ -91,6 +91,13 @@ gst_ffmpeg_caps_with_codectype (enum AVMediaType type, const GstCaps *caps, AVCodecContext *context); +void +gst_ffmpeg_videoinfo_to_context (GstVideoInfo *info, + AVCodecContext *context); + +GstVideoFormat gst_ffmpeg_pixfmt_to_videoformat (enum PixelFormat pixfmt); +enum PixelFormat gst_ffmpeg_videoformat_to_pixfmt (GstVideoFormat format); + /* * _formatid_to_caps () is meant for muxers/demuxers, it * transforms a name (ffmpeg way of ID'ing these, why don't diff --git a/ext/ffmpeg/gstffmpegdec.c b/ext/ffmpeg/gstffmpegdec.c index 35f8c85601..1604e68916 100644 --- a/ext/ffmpeg/gstffmpegdec.c +++ b/ext/ffmpeg/gstffmpegdec.c @@ -31,9 +31,6 @@ #endif #include -#include -#include -#include #include "gstffmpeg.h" #include "gstffmpegcodecmap.h" @@ -41,7 +38,7 @@ GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE); -typedef struct _GstFFMpegDec GstFFMpegDec; +typedef struct _GstFFMpegAudDec GstFFMpegAudDec; #define MAX_TS_MASK 0xff @@ -58,7 +55,7 @@ typedef struct gint64 offset; } GstTSInfo; -struct _GstFFMpegDec +struct _GstFFMpegAudDec { GstElement element; @@ -68,55 +65,16 @@ struct _GstFFMpegDec /* decoding */ AVCodecContext *context; - AVFrame *picture; gboolean opened; - GstBufferPool *pool; - - /* from incoming caps */ - gint in_width; - gint in_height; - gint in_par_n; - gint in_par_d; - gint in_fps_n; - gint in_fps_d; - - /* current context */ - enum PixelFormat ctx_pix_fmt; - gint ctx_width; - gint ctx_height; - gint ctx_par_n; - gint ctx_par_d; - gint ctx_ticks; - gint ctx_time_d; - gint ctx_time_n; - gint ctx_interlaced; /* current output format */ - GstVideoInfo out_info; + gint channels, samplerate, depth; + GstAudioChannelPosition ffmpeg_layout[64], gst_layout[64]; - union - { - struct - { - gint channels; - gint samplerate; - gint depth; - - GstAudioChannelPosition ffmpeg_layout[64], gst_layout[64]; - } audio; - } format; - - - gboolean waiting_for_key; gboolean discont; gboolean clear_ts; /* for tracking DTS/PTS */ - gboolean has_b_frames; - GstClockTime last_dts; - GstClockTime last_diff; - guint last_frames; - GstClockTime last_out; GstClockTime next_out; /* parsing */ @@ -124,29 +82,10 @@ struct _GstFFMpegDec * See bug #566250 */ AVCodecParserContext *pctx; GstBuffer *pcache; - guint8 *padded; - guint padded_size; - - gboolean current_dr; /* if direct rendering is enabled */ - - /* some properties */ - enum AVDiscard skip_frame; - gint lowres; - gboolean direct_rendering; - gboolean debug_mv; - int max_threads; - - /* QoS stuff *//* with LOCK */ - gdouble proportion; - GstClockTime earliest_time; - gint64 processed; - gint64 dropped; /* clipping segment */ GstSegment segment; - gboolean is_realvideo; - GstTSInfo ts_info[MAX_TS_MASK + 1]; gint ts_idx; @@ -157,9 +96,9 @@ struct _GstFFMpegDec GstCaps *last_caps; }; -typedef struct _GstFFMpegDecClass GstFFMpegDecClass; +typedef struct _GstFFMpegAudDecClass GstFFMpegAudDecClass; -struct _GstFFMpegDecClass +struct _GstFFMpegAudDecClass { GstElementClass parent_class; @@ -171,7 +110,7 @@ struct _GstFFMpegDecClass static const GstTSInfo ts_info_none = { -1, -1, -1, -1 }; static const GstTSInfo * -gst_ts_info_store (GstFFMpegDec * dec, GstClockTime dts, GstClockTime pts, +gst_ts_info_store (GstFFMpegAudDec * dec, GstClockTime dts, GstClockTime pts, GstClockTime duration, gint64 offset) { gint idx = dec->ts_idx; @@ -186,7 +125,7 @@ gst_ts_info_store (GstFFMpegDec * dec, GstClockTime dts, GstClockTime pts, } static const GstTSInfo * -gst_ts_info_get (GstFFMpegDec * dec, gint idx) +gst_ts_info_get (GstFFMpegAudDec * dec, gint idx) { if (G_UNLIKELY (idx < 0 || idx > MAX_TS_MASK)) return GST_TS_INFO_NONE; @@ -195,127 +134,51 @@ gst_ts_info_get (GstFFMpegDec * dec, gint idx) } #define GST_TYPE_FFMPEGDEC \ - (gst_ffmpegdec_get_type()) + (gst_ffmpegauddec_get_type()) #define GST_FFMPEGDEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegDec)) -#define GST_FFMPEGDEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegDecClass)) + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegAudDec)) +#define GST_FFMPEGAUDDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegAudDecClass)) #define GST_IS_FFMPEGDEC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC)) -#define GST_IS_FFMPEGDEC_CLASS(klass) \ +#define GST_IS_FFMPEGAUDDEC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC)) -#define DEFAULT_LOWRES 0 -#define DEFAULT_SKIPFRAME 0 -#define DEFAULT_DIRECT_RENDERING TRUE -#define DEFAULT_DEBUG_MV FALSE -#define DEFAULT_MAX_THREADS 1 - -enum -{ - PROP_0, - PROP_LOWRES, - PROP_SKIPFRAME, - PROP_DIRECT_RENDERING, - PROP_DEBUG_MV, - PROP_MAX_THREADS, - PROP_LAST -}; - /* A number of function prototypes are given so we can refer to them later. */ -static void gst_ffmpegdec_base_init (GstFFMpegDecClass * klass); -static void gst_ffmpegdec_class_init (GstFFMpegDecClass * klass); -static void gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec); -static void gst_ffmpegdec_finalize (GObject * object); +static void gst_ffmpegauddec_base_init (GstFFMpegAudDecClass * klass); +static void gst_ffmpegauddec_class_init (GstFFMpegAudDecClass * klass); +static void gst_ffmpegauddec_init (GstFFMpegAudDec * ffmpegdec); +static void gst_ffmpegauddec_finalize (GObject * object); -static gboolean gst_ffmpegdec_src_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static gboolean gst_ffmpegdec_src_event (GstPad * pad, GstObject * parent, +static gboolean gst_ffmpegauddec_setcaps (GstFFMpegAudDec * ffmpegdec, + GstCaps * caps); +static gboolean gst_ffmpegauddec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event); - -static gboolean gst_ffmpegdec_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_ffmpegdec_sink_query (GstPad * pad, GstObject * parent, +static gboolean gst_ffmpegauddec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query); -static GstFlowReturn gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, +static GstFlowReturn gst_ffmpegauddec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf); -static GstStateChangeReturn gst_ffmpegdec_change_state (GstElement * element, +static GstStateChangeReturn gst_ffmpegauddec_change_state (GstElement * element, GstStateChange transition); -static void gst_ffmpegdec_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_ffmpegdec_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static gboolean gst_ffmpegdec_video_negotiate (GstFFMpegDec * ffmpegdec, - gboolean force); -static gboolean gst_ffmpegdec_audio_negotiate (GstFFMpegDec * ffmpegdec, +static gboolean gst_ffmpegauddec_negotiate (GstFFMpegAudDec * ffmpegdec, gboolean force); -/* some sort of bufferpool handling, but different */ -static int gst_ffmpegdec_get_buffer (AVCodecContext * context, - AVFrame * picture); -static void gst_ffmpegdec_release_buffer (AVCodecContext * context, - AVFrame * picture); - -static void gst_ffmpegdec_drain (GstFFMpegDec * ffmpegdec); +static void gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec); #define GST_FFDEC_PARAMS_QDATA g_quark_from_static_string("avdec-params") static GstElementClass *parent_class = NULL; -#define GST_FFMPEGDEC_TYPE_LOWRES (gst_ffmpegdec_lowres_get_type()) -static GType -gst_ffmpegdec_lowres_get_type (void) -{ - static GType ffmpegdec_lowres_type = 0; - - if (!ffmpegdec_lowres_type) { - static const GEnumValue ffmpegdec_lowres[] = { - {0, "0", "full"}, - {1, "1", "1/2-size"}, - {2, "2", "1/4-size"}, - {0, NULL, NULL}, - }; - - ffmpegdec_lowres_type = - g_enum_register_static ("GstLibAVDecLowres", ffmpegdec_lowres); - } - - return ffmpegdec_lowres_type; -} - -#define GST_FFMPEGDEC_TYPE_SKIPFRAME (gst_ffmpegdec_skipframe_get_type()) -static GType -gst_ffmpegdec_skipframe_get_type (void) -{ - static GType ffmpegdec_skipframe_type = 0; - - if (!ffmpegdec_skipframe_type) { - static const GEnumValue ffmpegdec_skipframe[] = { - {0, "0", "Skip nothing"}, - {1, "1", "Skip B-frames"}, - {2, "2", "Skip IDCT/Dequantization"}, - {5, "5", "Skip everything"}, - {0, NULL, NULL}, - }; - - ffmpegdec_skipframe_type = - g_enum_register_static ("GstLibAVDecSkipFrame", ffmpegdec_skipframe); - } - - return ffmpegdec_skipframe_type; -} - static void -gst_ffmpegdec_base_init (GstFFMpegDecClass * klass) +gst_ffmpegauddec_base_init (GstFFMpegAudDecClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstPadTemplate *sinktempl, *srctempl; GstCaps *sinkcaps, *srccaps; AVCodec *in_plugin; - gchar *longname, *classification, *description; + gchar *longname, *description; in_plugin = (AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), @@ -324,16 +187,13 @@ gst_ffmpegdec_base_init (GstFFMpegDecClass * klass) /* construct the element details struct */ longname = g_strdup_printf ("libav %s decoder", in_plugin->long_name); - classification = g_strdup_printf ("Codec/Decoder/%s", - (in_plugin->type == AVMEDIA_TYPE_VIDEO) ? "Video" : "Audio"); description = g_strdup_printf ("libav %s decoder", in_plugin->name); gst_element_class_set_metadata (element_class, longname, - classification, description, + "Codec/Decoder/Audio", description, "Wim Taymans , " "Ronald Bultje , " "Edward Hervey "); g_free (longname); - g_free (classification); g_free (description); /* get the caps */ @@ -342,12 +202,8 @@ gst_ffmpegdec_base_init (GstFFMpegDecClass * klass) GST_DEBUG ("Couldn't get sink caps for decoder '%s'", in_plugin->name); sinkcaps = gst_caps_from_string ("unknown/unknown"); } - if (in_plugin->type == AVMEDIA_TYPE_VIDEO) { - srccaps = gst_caps_from_string ("video/x-raw"); - } else { - srccaps = gst_ffmpeg_codectype_to_audio_caps (NULL, - in_plugin->id, FALSE, in_plugin); - } + srccaps = gst_ffmpeg_codectype_to_audio_caps (NULL, + in_plugin->id, FALSE, in_plugin); if (!srccaps) { GST_DEBUG ("Couldn't get source caps for decoder '%s'", in_plugin->name); srccaps = gst_caps_from_string ("unknown/unknown"); @@ -367,237 +223,73 @@ gst_ffmpegdec_base_init (GstFFMpegDecClass * klass) } static void -gst_ffmpegdec_class_init (GstFFMpegDecClass * klass) +gst_ffmpegauddec_class_init (GstFFMpegAudDecClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); - gobject_class->finalize = gst_ffmpegdec_finalize; + gobject_class->finalize = gst_ffmpegauddec_finalize; - gobject_class->set_property = gst_ffmpegdec_set_property; - gobject_class->get_property = gst_ffmpegdec_get_property; - - if (klass->in_plugin->type == AVMEDIA_TYPE_VIDEO) { - int caps; - - g_object_class_install_property (gobject_class, PROP_SKIPFRAME, - g_param_spec_enum ("skip-frame", "Skip frames", - "Which types of frames to skip during decoding", - GST_FFMPEGDEC_TYPE_SKIPFRAME, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_LOWRES, - g_param_spec_enum ("lowres", "Low resolution", - "At which resolution to decode images", GST_FFMPEGDEC_TYPE_LOWRES, - 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_DIRECT_RENDERING, - g_param_spec_boolean ("direct-rendering", "Direct Rendering", - "Enable direct rendering", DEFAULT_DIRECT_RENDERING, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_DEBUG_MV, - g_param_spec_boolean ("debug-mv", "Debug motion vectors", - "Whether ffmpeg should print motion vectors on top of the image", - DEFAULT_DEBUG_MV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - caps = klass->in_plugin->capabilities; - if (caps & (CODEC_CAP_FRAME_THREADS | CODEC_CAP_SLICE_THREADS)) { - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_THREADS, - g_param_spec_int ("max-threads", "Maximum decode threads", - "Maximum number of worker threads to spawn. (0 = auto)", - 0, G_MAXINT, DEFAULT_MAX_THREADS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - } - } - - gstelement_class->change_state = gst_ffmpegdec_change_state; + gstelement_class->change_state = gst_ffmpegauddec_change_state; } static void -gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_init (GstFFMpegAudDec * ffmpegdec) { - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); /* setup pads */ ffmpegdec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink"); - gst_pad_set_event_function (ffmpegdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_ffmpegdec_sink_event)); gst_pad_set_query_function (ffmpegdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_ffmpegdec_sink_query)); + GST_DEBUG_FUNCPTR (gst_ffmpegauddec_sink_query)); + gst_pad_set_event_function (ffmpegdec->sinkpad, + GST_DEBUG_FUNCPTR (gst_ffmpegauddec_sink_event)); gst_pad_set_chain_function (ffmpegdec->sinkpad, - GST_DEBUG_FUNCPTR (gst_ffmpegdec_chain)); + GST_DEBUG_FUNCPTR (gst_ffmpegauddec_chain)); gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad); ffmpegdec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src"); gst_pad_use_fixed_caps (ffmpegdec->srcpad); - gst_pad_set_event_function (ffmpegdec->srcpad, - GST_DEBUG_FUNCPTR (gst_ffmpegdec_src_event)); - gst_pad_set_query_function (ffmpegdec->srcpad, - GST_DEBUG_FUNCPTR (gst_ffmpegdec_src_query)); gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad); /* some ffmpeg data */ ffmpegdec->context = avcodec_alloc_context (); - ffmpegdec->picture = avcodec_alloc_frame (); ffmpegdec->pctx = NULL; ffmpegdec->pcache = NULL; ffmpegdec->opened = FALSE; - ffmpegdec->waiting_for_key = TRUE; - ffmpegdec->skip_frame = ffmpegdec->lowres = 0; - ffmpegdec->direct_rendering = DEFAULT_DIRECT_RENDERING; - ffmpegdec->debug_mv = DEFAULT_DEBUG_MV; - ffmpegdec->max_threads = DEFAULT_MAX_THREADS; gst_segment_init (&ffmpegdec->segment, GST_FORMAT_TIME); } static void -gst_ffmpegdec_finalize (GObject * object) +gst_ffmpegauddec_finalize (GObject * object) { - GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object; + GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) object; if (ffmpegdec->context != NULL) av_free (ffmpegdec->context); - if (ffmpegdec->picture != NULL) - av_free (ffmpegdec->picture); - - if (ffmpegdec->pool) - gst_object_unref (ffmpegdec->pool); - G_OBJECT_CLASS (parent_class)->finalize (object); } -static gboolean -gst_ffmpegdec_src_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstFFMpegDec *ffmpegdec; - gboolean res = FALSE; - - ffmpegdec = (GstFFMpegDec *) parent; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - { - GST_DEBUG_OBJECT (ffmpegdec, "latency query %d", - ffmpegdec->context->has_b_frames); - if ((res = gst_pad_peer_query (ffmpegdec->sinkpad, query))) { - if (ffmpegdec->context->has_b_frames) { - gboolean live; - GstClockTime min_lat, max_lat, our_lat; - - gst_query_parse_latency (query, &live, &min_lat, &max_lat); - if (ffmpegdec->out_info.fps_n > 0) - our_lat = - gst_util_uint64_scale_int (ffmpegdec->context->has_b_frames * - GST_SECOND, ffmpegdec->out_info.fps_d, - ffmpegdec->out_info.fps_n); - else - our_lat = - gst_util_uint64_scale_int (ffmpegdec->context->has_b_frames * - GST_SECOND, 1, 25); - if (min_lat != -1) - min_lat += our_lat; - if (max_lat != -1) - max_lat += our_lat; - gst_query_set_latency (query, live, min_lat, max_lat); - } - } - } - break; - default: - res = gst_pad_query_default (pad, parent, query); - break; - } - - return res; -} - static void -gst_ffmpegdec_reset_ts (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_reset_ts (GstFFMpegAudDec * ffmpegdec) { - ffmpegdec->last_dts = GST_CLOCK_TIME_NONE; - ffmpegdec->last_diff = GST_CLOCK_TIME_NONE; - ffmpegdec->last_frames = 0; - ffmpegdec->last_out = GST_CLOCK_TIME_NONE; ffmpegdec->next_out = GST_CLOCK_TIME_NONE; } -static void -gst_ffmpegdec_update_qos (GstFFMpegDec * ffmpegdec, gdouble proportion, - GstClockTime timestamp) -{ - GST_LOG_OBJECT (ffmpegdec, "update QOS: %f, %" GST_TIME_FORMAT, - proportion, GST_TIME_ARGS (timestamp)); - - GST_OBJECT_LOCK (ffmpegdec); - ffmpegdec->proportion = proportion; - ffmpegdec->earliest_time = timestamp; - GST_OBJECT_UNLOCK (ffmpegdec); -} - -static void -gst_ffmpegdec_reset_qos (GstFFMpegDec * ffmpegdec) -{ - gst_ffmpegdec_update_qos (ffmpegdec, 0.5, GST_CLOCK_TIME_NONE); - ffmpegdec->processed = 0; - ffmpegdec->dropped = 0; -} - -static void -gst_ffmpegdec_read_qos (GstFFMpegDec * ffmpegdec, gdouble * proportion, - GstClockTime * timestamp) -{ - GST_OBJECT_LOCK (ffmpegdec); - *proportion = ffmpegdec->proportion; - *timestamp = ffmpegdec->earliest_time; - GST_OBJECT_UNLOCK (ffmpegdec); -} - -static gboolean -gst_ffmpegdec_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstFFMpegDec *ffmpegdec; - gboolean res; - - ffmpegdec = (GstFFMpegDec *) parent; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_QOS: - { - GstQOSType type; - gdouble proportion; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, &type, &proportion, &diff, ×tamp); - - /* update our QoS values */ - gst_ffmpegdec_update_qos (ffmpegdec, proportion, timestamp + diff); - - /* forward upstream */ - res = gst_pad_push_event (ffmpegdec->sinkpad, event); - break; - } - default: - /* forward upstream */ - res = gst_pad_push_event (ffmpegdec->sinkpad, event); - break; - } - - return res; -} - /* with LOCK */ static void -gst_ffmpegdec_close (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_close (GstFFMpegAudDec * ffmpegdec) { if (!ffmpegdec->opened) return; - GST_LOG_OBJECT (ffmpegdec, "closing ffmpeg codec"); + GST_LOG_OBJECT (ffmpegdec, "closing libav codec"); gst_caps_replace (&ffmpegdec->last_caps, NULL); @@ -627,187 +319,82 @@ gst_ffmpegdec_close (GstFFMpegDec * ffmpegdec) /* with LOCK */ static gboolean -gst_ffmpegdec_open (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_open (GstFFMpegAudDec * ffmpegdec) { - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) goto could_not_open; ffmpegdec->opened = TRUE; - ffmpegdec->is_realvideo = FALSE; - GST_LOG_OBJECT (ffmpegdec, "Opened ffmpeg codec %s, id %d", + GST_LOG_OBJECT (ffmpegdec, "Opened libav codec %s, id %d", oclass->in_plugin->name, oclass->in_plugin->id); - /* open a parser if we can */ - switch (oclass->in_plugin->id) { - case CODEC_ID_MPEG4: - case CODEC_ID_MJPEG: - case CODEC_ID_VC1: - GST_LOG_OBJECT (ffmpegdec, "not using parser, blacklisted codec"); - ffmpegdec->pctx = NULL; - break; - case CODEC_ID_H264: - /* For H264, only use a parser if there is no context data, if there is, - * we're talking AVC */ - if (ffmpegdec->context->extradata_size == 0) { - GST_LOG_OBJECT (ffmpegdec, "H264 with no extradata, creating parser"); - ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id); - } else { - GST_LOG_OBJECT (ffmpegdec, - "H264 with extradata implies framed data - not using parser"); - ffmpegdec->pctx = NULL; - } - break; - case CODEC_ID_RV10: - case CODEC_ID_RV30: - case CODEC_ID_RV20: - case CODEC_ID_RV40: - ffmpegdec->is_realvideo = TRUE; - break; - default: - if (!ffmpegdec->turnoff_parser) { - ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id); - if (ffmpegdec->pctx) - GST_LOG_OBJECT (ffmpegdec, "Using parser %p", ffmpegdec->pctx); - else - GST_LOG_OBJECT (ffmpegdec, "No parser for codec"); - } else { - GST_LOG_OBJECT (ffmpegdec, "Parser deactivated for format"); - } - break; + if (!ffmpegdec->turnoff_parser) { + ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id); + if (ffmpegdec->pctx) + GST_LOG_OBJECT (ffmpegdec, "Using parser %p", ffmpegdec->pctx); + else + GST_LOG_OBJECT (ffmpegdec, "No parser for codec"); + } else { + GST_LOG_OBJECT (ffmpegdec, "Parser deactivated for format"); } - switch (oclass->in_plugin->type) { - case AVMEDIA_TYPE_VIDEO: - /* clear values */ - ffmpegdec->ctx_pix_fmt = PIX_FMT_NB; - ffmpegdec->ctx_width = 0; - ffmpegdec->ctx_height = 0; - ffmpegdec->ctx_ticks = 1; - ffmpegdec->ctx_time_n = 0; - ffmpegdec->ctx_time_d = 0; - ffmpegdec->ctx_par_n = 0; - ffmpegdec->ctx_par_d = 0; - break; - case AVMEDIA_TYPE_AUDIO: - ffmpegdec->format.audio.samplerate = 0; - ffmpegdec->format.audio.channels = 0; - ffmpegdec->format.audio.depth = 0; - break; - default: - break; - } + ffmpegdec->samplerate = 0; + ffmpegdec->channels = 0; + ffmpegdec->depth = 0; - gst_ffmpegdec_reset_ts (ffmpegdec); - /* FIXME, reset_qos will take the LOCK and this function is already called - * with the LOCK */ - ffmpegdec->proportion = 0.5; - ffmpegdec->earliest_time = -1; + gst_ffmpegauddec_reset_ts (ffmpegdec); return TRUE; /* ERRORS */ could_not_open: { - gst_ffmpegdec_close (ffmpegdec); - GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open FFMPEG codec", + gst_ffmpegauddec_close (ffmpegdec); + GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open libav codec", oclass->in_plugin->name); return FALSE; } } static gboolean -gst_ffmpegdec_setcaps (GstFFMpegDec * ffmpegdec, GstCaps * caps) +gst_ffmpegauddec_setcaps (GstFFMpegAudDec * ffmpegdec, GstCaps * caps) { - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; GstStructure *structure; - const GValue *par; - const GValue *fps; gboolean ret = TRUE; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); GST_DEBUG_OBJECT (ffmpegdec, "setcaps called"); GST_OBJECT_LOCK (ffmpegdec); - /* stupid check for VC1 */ - if ((oclass->in_plugin->id == CODEC_ID_WMV3) || - (oclass->in_plugin->id == CODEC_ID_VC1)) - oclass->in_plugin->id = gst_ffmpeg_caps_to_codecid (caps, NULL); - /* close old session */ if (ffmpegdec->opened) { GST_OBJECT_UNLOCK (ffmpegdec); - gst_ffmpegdec_drain (ffmpegdec); + gst_ffmpegauddec_drain (ffmpegdec); GST_OBJECT_LOCK (ffmpegdec); - gst_ffmpegdec_close (ffmpegdec); + gst_ffmpegauddec_close (ffmpegdec); /* and reset the defaults that were set when a context is created */ avcodec_get_context_defaults (ffmpegdec->context); } - /* set buffer functions */ - if (oclass->in_plugin->type == AVMEDIA_TYPE_VIDEO) { - ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer; - ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer; - ffmpegdec->context->draw_horiz_band = NULL; - } - /* default is to let format decide if it needs a parser */ ffmpegdec->turnoff_parser = FALSE; - ffmpegdec->has_b_frames = FALSE; - - GST_LOG_OBJECT (ffmpegdec, "size %dx%d", ffmpegdec->context->width, - ffmpegdec->context->height); - /* get size and so */ gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id, oclass->in_plugin->type, caps, ffmpegdec->context); - GST_LOG_OBJECT (ffmpegdec, "size after %dx%d", ffmpegdec->context->width, - ffmpegdec->context->height); - - if (!ffmpegdec->context->time_base.den || !ffmpegdec->context->time_base.num) { - GST_DEBUG_OBJECT (ffmpegdec, "forcing 25/1 framerate"); - ffmpegdec->context->time_base.num = 1; - ffmpegdec->context->time_base.den = 25; - } - /* get pixel aspect ratio if it's set */ structure = gst_caps_get_structure (caps, 0); - par = gst_structure_get_value (structure, "pixel-aspect-ratio"); - if (par != NULL && GST_VALUE_HOLDS_FRACTION (par)) { - ffmpegdec->in_par_n = gst_value_get_fraction_numerator (par); - ffmpegdec->in_par_d = gst_value_get_fraction_denominator (par); - GST_DEBUG_OBJECT (ffmpegdec, "sink caps have pixel-aspect-ratio of %d:%d", - ffmpegdec->in_par_n, ffmpegdec->in_par_d); - } else { - GST_DEBUG_OBJECT (ffmpegdec, "no input pixel-aspect-ratio"); - ffmpegdec->in_par_n = 0; - ffmpegdec->in_par_d = 0; - } - - /* get the framerate from incoming caps. fps_n is set to 0 when - * there is no valid framerate */ - fps = gst_structure_get_value (structure, "framerate"); - if (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)) { - ffmpegdec->in_fps_n = gst_value_get_fraction_numerator (fps); - ffmpegdec->in_fps_d = gst_value_get_fraction_denominator (fps); - GST_DEBUG_OBJECT (ffmpegdec, "sink caps have framerate of %d/%d", - ffmpegdec->in_fps_n, ffmpegdec->in_fps_d); - } else { - GST_DEBUG_OBJECT (ffmpegdec, "no input framerate "); - ffmpegdec->in_fps_n = 0; - ffmpegdec->in_fps_d = 0; - } - /* for AAC we only use av_parse if not on stream-format==raw or ==loas */ if (oclass->in_plugin->id == CODEC_ID_AAC || oclass->in_plugin->id == CODEC_ID_AAC_LATM) { @@ -828,44 +415,12 @@ gst_ffmpegdec_setcaps (GstFFMpegDec * ffmpegdec, GstCaps * caps) ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT; ffmpegdec->context->error_recognition = 1; - /* for slow cpus */ - ffmpegdec->context->lowres = ffmpegdec->lowres; - ffmpegdec->context->skip_frame = ffmpegdec->skip_frame; - - /* ffmpeg can draw motion vectors on top of the image (not every decoder - * supports it) */ - ffmpegdec->context->debug_mv = ffmpegdec->debug_mv; - - if (ffmpegdec->max_threads == 0) { - if (!(oclass->in_plugin->capabilities & CODEC_CAP_AUTO_THREADS)) - ffmpegdec->context->thread_count = gst_ffmpeg_auto_max_threads (); - else - ffmpegdec->context->thread_count = 0; - } else - ffmpegdec->context->thread_count = ffmpegdec->max_threads; - - ffmpegdec->context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME; - /* open codec - we don't select an output pix_fmt yet, * simply because we don't know! We only get it * during playback... */ - if (!gst_ffmpegdec_open (ffmpegdec)) + if (!gst_ffmpegauddec_open (ffmpegdec)) goto open_failed; - /* clipping region. take into account the lowres property */ - if (gst_structure_get_int (structure, "width", &ffmpegdec->in_width)) - ffmpegdec->in_width >>= ffmpegdec->lowres; - else - ffmpegdec->in_width = -1; - - if (gst_structure_get_int (structure, "height", &ffmpegdec->in_height)) - ffmpegdec->in_height >>= ffmpegdec->lowres; - else - ffmpegdec->in_height = -1; - - GST_DEBUG_OBJECT (ffmpegdec, "clipping to %dx%d", - ffmpegdec->in_width, ffmpegdec->in_height); - done: GST_OBJECT_UNLOCK (ffmpegdec); @@ -880,574 +435,49 @@ open_failed: } } -static void -gst_ffmpegdec_fill_picture (GstFFMpegDec * ffmpegdec, GstVideoFrame * frame, - AVFrame * picture) -{ - guint i; - - /* setup data pointers and strides */ - for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (frame); i++) { - picture->data[i] = GST_VIDEO_FRAME_PLANE_DATA (frame, i); - picture->linesize[i] = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i); - - GST_LOG_OBJECT (ffmpegdec, "plane %d: data %p, linesize %d", i, - picture->data[i], picture->linesize[i]); - } -} - -/* called when ffmpeg wants us to allocate a buffer to write the decoded frame - * into. We try to give it memory from our pool */ -static int -gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture) -{ - GstBuffer *buf = NULL; - GstFFMpegDec *ffmpegdec; - GstFlowReturn ret; - GstVideoFrame frame; - - ffmpegdec = (GstFFMpegDec *) context->opaque; - - ffmpegdec->context->pix_fmt = context->pix_fmt; - - GST_DEBUG_OBJECT (ffmpegdec, "getting buffer"); - - /* apply the last info we have seen to this picture, when we get the - * picture back from ffmpeg we can use this to correctly timestamp the output - * buffer */ - picture->reordered_opaque = context->reordered_opaque; - /* make sure we don't free the buffer when it's not ours */ - picture->opaque = NULL; - - /* see if we need renegotiation */ - if (G_UNLIKELY (!gst_ffmpegdec_video_negotiate (ffmpegdec, FALSE))) - goto negotiate_failed; - - if (!ffmpegdec->current_dr) - goto no_dr; - - /* alloc with aligned dimensions for ffmpeg */ - GST_LOG_OBJECT (ffmpegdec, "doing alloc from pool"); - ret = gst_buffer_pool_acquire_buffer (ffmpegdec->pool, &buf, NULL); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto alloc_failed; - - if (!gst_video_frame_map (&frame, &ffmpegdec->out_info, buf, - GST_MAP_READWRITE)) - goto invalid_frame; - - gst_ffmpegdec_fill_picture (ffmpegdec, &frame, picture); - - /* tell ffmpeg we own this buffer, tranfer the ref we have on the buffer to - * the opaque data. */ - picture->type = FF_BUFFER_TYPE_USER; - picture->age = 256 * 256 * 256 * 64; - picture->opaque = g_slice_dup (GstVideoFrame, &frame); - - GST_LOG_OBJECT (ffmpegdec, "returned buffer %p in frame %p", buf, - picture->opaque); - - return 0; - - /* fallbacks */ -negotiate_failed: - { - GST_DEBUG_OBJECT (ffmpegdec, "negotiate failed"); - goto fallback; - } -no_dr: - { - GST_LOG_OBJECT (ffmpegdec, "direct rendering disabled, fallback alloc"); - goto fallback; - } -alloc_failed: - { - /* alloc default buffer when we can't get one from downstream */ - GST_LOG_OBJECT (ffmpegdec, "alloc failed, fallback alloc"); - goto fallback; - } -invalid_frame: - { - /* alloc default buffer when we can't get one from downstream */ - GST_LOG_OBJECT (ffmpegdec, "failed to map frame, fallback alloc"); - gst_buffer_unref (buf); - goto fallback; - } -fallback: - { - return avcodec_default_get_buffer (context, picture); - } -} - -/* called when ffmpeg is done with our buffer */ -static void -gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture) -{ - gint i; - GstBuffer *buf; - GstFFMpegDec *ffmpegdec; - GstVideoFrame *frame; - - ffmpegdec = (GstFFMpegDec *) context->opaque; - - /* check if it was our buffer */ - if (picture->opaque == NULL) { - GST_DEBUG_OBJECT (ffmpegdec, "default release buffer"); - avcodec_default_release_buffer (context, picture); - return; - } - - /* we remove the opaque data now */ - frame = picture->opaque; - picture->opaque = NULL; - - /* unmap buffer data */ - gst_video_frame_unmap (frame); - buf = frame->buffer; - - GST_DEBUG_OBJECT (ffmpegdec, "release buffer %p in frame %p", buf, frame); - - g_slice_free (GstVideoFrame, frame); - gst_buffer_unref (buf); - - /* zero out the reference in ffmpeg */ - for (i = 0; i < 4; i++) { - picture->data[i] = NULL; - picture->linesize[i] = 0; - } -} - -static void -gst_ffmpegdec_update_par (GstFFMpegDec * ffmpegdec, gint * par_n, gint * par_d) -{ - gboolean demuxer_par_set = FALSE; - gboolean decoder_par_set = FALSE; - gint demuxer_num = 1, demuxer_denom = 1; - gint decoder_num = 1, decoder_denom = 1; - - GST_OBJECT_LOCK (ffmpegdec); - - if (ffmpegdec->in_par_n && ffmpegdec->in_par_d) { - demuxer_num = ffmpegdec->in_par_n; - demuxer_denom = ffmpegdec->in_par_d; - demuxer_par_set = TRUE; - GST_DEBUG_OBJECT (ffmpegdec, "Demuxer PAR: %d:%d", demuxer_num, - demuxer_denom); - } - - if (ffmpegdec->ctx_par_n && ffmpegdec->ctx_par_d) { - decoder_num = ffmpegdec->ctx_par_n; - decoder_denom = ffmpegdec->ctx_par_d; - decoder_par_set = TRUE; - GST_DEBUG_OBJECT (ffmpegdec, "Decoder PAR: %d:%d", decoder_num, - decoder_denom); - } - - GST_OBJECT_UNLOCK (ffmpegdec); - - if (!demuxer_par_set && !decoder_par_set) - goto no_par; - - if (demuxer_par_set && !decoder_par_set) - goto use_demuxer_par; - - if (decoder_par_set && !demuxer_par_set) - goto use_decoder_par; - - /* Both the demuxer and the decoder provide a PAR. If one of - * the two PARs is 1:1 and the other one is not, use the one - * that is not 1:1. */ - if (demuxer_num == demuxer_denom && decoder_num != decoder_denom) - goto use_decoder_par; - - if (decoder_num == decoder_denom && demuxer_num != demuxer_denom) - goto use_demuxer_par; - - /* Both PARs are non-1:1, so use the PAR provided by the demuxer */ - goto use_demuxer_par; - -use_decoder_par: - { - GST_DEBUG_OBJECT (ffmpegdec, - "Setting decoder provided pixel-aspect-ratio of %u:%u", decoder_num, - decoder_denom); - *par_n = decoder_num; - *par_d = decoder_denom; - return; - } - -use_demuxer_par: - { - GST_DEBUG_OBJECT (ffmpegdec, - "Setting demuxer provided pixel-aspect-ratio of %u:%u", demuxer_num, - demuxer_denom); - *par_n = demuxer_num; - *par_d = demuxer_denom; - return; - } -no_par: - { - GST_DEBUG_OBJECT (ffmpegdec, - "Neither demuxer nor codec provide a pixel-aspect-ratio"); - *par_n = 1; - *par_d = 1; - return; - } -} - static gboolean -gst_ffmpegdec_bufferpool (GstFFMpegDec * ffmpegdec, GstCaps * caps) +gst_ffmpegauddec_negotiate (GstFFMpegAudDec * ffmpegdec, gboolean force) { - GstQuery *query; - GstBufferPool *pool; - guint size, min, max; - GstStructure *config; - guint edge; - AVCodecContext *context = ffmpegdec->context; - gboolean have_videometa, have_alignment; - GstAllocationParams params = { 0, 0, 0, 15, }; - - GST_DEBUG_OBJECT (ffmpegdec, "setting up bufferpool"); - - /* find a pool for the negotiated caps now */ - query = gst_query_new_allocation (caps, TRUE); - - if (gst_pad_peer_query (ffmpegdec->srcpad, query)) { - have_videometa = - gst_query_has_allocation_meta (query, GST_VIDEO_META_API_TYPE); - } else { - /* use query defaults */ - GST_DEBUG_OBJECT (ffmpegdec, "peer query failed, using defaults"); - have_videometa = FALSE; - } - - if (gst_query_get_n_allocation_pools (query) > 0) { - /* we got configuration from our peer, parse them */ - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - - size = MAX (size, ffmpegdec->out_info.size); - } else { - pool = NULL; - size = ffmpegdec->out_info.size; - min = max = 0; - } - - gst_query_unref (query); - - if (pool == NULL) { - /* we did not get a pool, make one ourselves then */ - pool = gst_video_buffer_pool_new (); - } - - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - /* we are happy with the default allocator but we would like to have 16 bytes - * aligned memory */ - gst_buffer_pool_config_set_allocator (config, NULL, ¶ms); - - have_alignment = - gst_buffer_pool_has_option (pool, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); - - /* we can only enable the alignment if downstream supports the - * videometa api */ - if (have_alignment && have_videometa) { - GstVideoAlignment align; - gint width, height; - gint linesize_align[4]; - gint i; - - width = ffmpegdec->ctx_width; - height = ffmpegdec->ctx_height; - /* let ffmpeg find the alignment and padding */ - avcodec_align_dimensions2 (context, &width, &height, linesize_align); - edge = context->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width (); - /* increase the size for the padding */ - width += edge << 1; - height += edge << 1; - - align.padding_top = edge; - align.padding_left = edge; - align.padding_right = width - ffmpegdec->ctx_width - edge; - align.padding_bottom = height - ffmpegdec->ctx_height - edge; - for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) - align.stride_align[i] = - (linesize_align[i] > 0 ? linesize_align[i] - 1 : 0); - - GST_DEBUG_OBJECT (ffmpegdec, "aligned dimension %dx%d -> %dx%d " - "padding t:%u l:%u r:%u b:%u, stride_align %d:%d:%d:%d", - ffmpegdec->ctx_width, ffmpegdec->ctx_height, width, height, - align.padding_top, align.padding_left, align.padding_right, - align.padding_bottom, align.stride_align[0], align.stride_align[1], - align.stride_align[2], align.stride_align[3]); - - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_META); - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); - gst_buffer_pool_config_set_video_alignment (config, &align); - - if (ffmpegdec->direct_rendering) { - GstFFMpegDecClass *oclass; - - GST_DEBUG_OBJECT (ffmpegdec, "trying to enable direct rendering"); - - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - - if (oclass->in_plugin->capabilities & CODEC_CAP_DR1) { - GST_DEBUG_OBJECT (ffmpegdec, "enabled direct rendering"); - ffmpegdec->current_dr = TRUE; - } else { - GST_DEBUG_OBJECT (ffmpegdec, "direct rendering not supported"); - } - } - } else { - GST_DEBUG_OBJECT (ffmpegdec, - "alignment or videometa not supported, disable direct rendering"); - /* disable direct rendering. This will make us use the fallback ffmpeg - * picture allocation code with padding etc. We will then do the final - * copy (with cropping) into a buffer from our pool */ - ffmpegdec->current_dr = FALSE; - } - - /* and store */ - gst_buffer_pool_set_config (pool, config); - - if (ffmpegdec->pool) { - gst_buffer_pool_set_active (ffmpegdec->pool, FALSE); - gst_object_unref (ffmpegdec->pool); - } - ffmpegdec->pool = pool; - - /* and activate */ - gst_buffer_pool_set_active (pool, TRUE); - - return TRUE; -} - -static gboolean -update_video_context (GstFFMpegDec * ffmpegdec, gboolean force) -{ - AVCodecContext *context = ffmpegdec->context; - - if (!force && ffmpegdec->ctx_width == context->width - && ffmpegdec->ctx_height == context->height - && ffmpegdec->ctx_ticks == context->ticks_per_frame - && ffmpegdec->ctx_time_n == context->time_base.num - && ffmpegdec->ctx_time_d == context->time_base.den - && ffmpegdec->ctx_pix_fmt == context->pix_fmt - && ffmpegdec->ctx_par_n == context->sample_aspect_ratio.num - && ffmpegdec->ctx_par_d == context->sample_aspect_ratio.den) - return FALSE; - - GST_DEBUG_OBJECT (ffmpegdec, - "Renegotiating video from %dx%d@ %d:%d PAR %d/%d fps to %dx%d@ %d:%d PAR %d/%d fps pixfmt %d", - ffmpegdec->ctx_width, ffmpegdec->ctx_height, - ffmpegdec->ctx_par_n, ffmpegdec->ctx_par_d, - ffmpegdec->ctx_time_n, ffmpegdec->ctx_time_d, - context->width, context->height, - context->sample_aspect_ratio.num, - context->sample_aspect_ratio.den, - context->time_base.num, context->time_base.den, context->pix_fmt); - - ffmpegdec->ctx_width = context->width; - ffmpegdec->ctx_height = context->height; - ffmpegdec->ctx_ticks = context->ticks_per_frame; - ffmpegdec->ctx_time_n = context->time_base.num; - ffmpegdec->ctx_time_d = context->time_base.den; - ffmpegdec->ctx_pix_fmt = context->pix_fmt; - ffmpegdec->ctx_par_n = context->sample_aspect_ratio.num; - ffmpegdec->ctx_par_d = context->sample_aspect_ratio.den; - - return TRUE; -} - -static gboolean -gst_ffmpegdec_video_negotiate (GstFFMpegDec * ffmpegdec, gboolean force) -{ - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; GstCaps *caps; - gint width, height; - gint fps_n, fps_d; - GstVideoInfo info; - GstVideoFormat fmt; - - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - - force |= gst_pad_check_reconfigure (ffmpegdec->srcpad); - - /* first check if anything changed */ - if (!update_video_context (ffmpegdec, force)) - return TRUE; - - /* now we're going to construct the video info for the final output - * format */ - gst_video_info_init (&info); - - fmt = gst_ffmpeg_pixfmt_to_video_format (ffmpegdec->ctx_pix_fmt); - if (fmt == GST_VIDEO_FORMAT_UNKNOWN) - goto unknown_format; - - /* determine the width and height, start with the dimension of the - * context */ - width = ffmpegdec->ctx_width; - height = ffmpegdec->ctx_height; - - /* if there is a width/height specified in the input, use that */ - if (ffmpegdec->in_width != -1 && ffmpegdec->in_width < width) - width = ffmpegdec->in_width; - if (ffmpegdec->in_height != -1 && ffmpegdec->in_height < height) - height = ffmpegdec->in_height; - - /* now store the values */ - gst_video_info_set_format (&info, fmt, width, height); - - /* set the interlaced flag */ - if (ffmpegdec->ctx_interlaced) - info.interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED; - else - info.interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; - - /* try to find a good framerate */ - if (ffmpegdec->in_fps_d) { - /* take framerate from input when it was specified (#313970) */ - fps_n = ffmpegdec->in_fps_n; - fps_d = ffmpegdec->in_fps_d; - } else { - fps_n = ffmpegdec->ctx_time_d / ffmpegdec->ctx_ticks; - fps_d = ffmpegdec->ctx_time_n; - - if (!fps_d) { - GST_LOG_OBJECT (ffmpegdec, "invalid framerate: %d/0, -> %d/1", fps_n, - fps_n); - fps_d = 1; - } - if (gst_util_fraction_compare (fps_n, fps_d, 1000, 1) > 0) { - GST_LOG_OBJECT (ffmpegdec, "excessive framerate: %d/%d, -> 0/1", fps_n, - fps_d); - fps_n = 0; - fps_d = 1; - } - } - GST_LOG_OBJECT (ffmpegdec, "setting framerate: %d/%d", fps_n, fps_d); - info.fps_n = fps_n; - info.fps_d = fps_d; - - /* calculate and update par now */ - gst_ffmpegdec_update_par (ffmpegdec, &info.par_n, &info.par_d); - - caps = gst_video_info_to_caps (&info); - - if (!gst_pad_set_caps (ffmpegdec->srcpad, caps)) - goto caps_failed; - - ffmpegdec->out_info = info; - - /* now figure out a bufferpool */ - if (!gst_ffmpegdec_bufferpool (ffmpegdec, caps)) - goto no_bufferpool; - - gst_caps_unref (caps); - - return TRUE; - - /* ERRORS */ -unknown_format: - { -#ifdef HAVE_LIBAV_UNINSTALLED - /* using internal ffmpeg snapshot */ - GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, - ("Could not find GStreamer caps mapping for libav pixfmt %d.", - ffmpegdec->ctx_pix_fmt), (NULL)); -#else - /* using external ffmpeg */ - GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, - ("Could not find GStreamer caps mapping for libav codec '%s', and " - "you are using an external libavcodec. This is most likely due to " - "a packaging problem and/or libavcodec having been upgraded to a " - "version that is not compatible with this version of " - "gstreamer-libav. Make sure your gstreamer-libav and libavcodec " - "packages come from the same source/repository.", - oclass->in_plugin->name), (NULL)); -#endif - return FALSE; - } -caps_failed: - { - GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL), - ("Could not set caps for libav decoder (%s), not fixed?", - oclass->in_plugin->name)); - gst_caps_unref (caps); - - return FALSE; - } -no_bufferpool: - { - GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL), - ("Could not create bufferpool for fmpeg decoder (%s)", - oclass->in_plugin->name)); - gst_caps_unref (caps); - - return FALSE; - } -} - -static gboolean -update_audio_context (GstFFMpegDec * ffmpegdec, gboolean force) -{ - AVCodecContext *context = ffmpegdec->context; gint depth; GstAudioChannelPosition pos[64] = { 0, }; - depth = av_smp_format_depth (context->sample_fmt); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - gst_ffmpeg_channel_layout_to_gst (context, pos); + depth = av_smp_format_depth (ffmpegdec->context->sample_fmt); + gst_ffmpeg_channel_layout_to_gst (ffmpegdec->context, pos); - if (!force && ffmpegdec->format.audio.samplerate == - context->sample_rate && - ffmpegdec->format.audio.channels == context->channels && - ffmpegdec->format.audio.depth == depth && - memcmp (ffmpegdec->format.audio.ffmpeg_layout, pos, - sizeof (GstAudioChannelPosition) * context->channels) == 0) - return FALSE; + if (!force && ffmpegdec->samplerate == + ffmpegdec->context->sample_rate && + ffmpegdec->channels == ffmpegdec->context->channels && + ffmpegdec->depth == depth) + return TRUE; GST_DEBUG_OBJECT (ffmpegdec, "Renegotiating audio from %dHz@%dchannels (%d) to %dHz@%dchannels (%d)", - ffmpegdec->format.audio.samplerate, ffmpegdec->format.audio.channels, - ffmpegdec->format.audio.depth, - context->sample_rate, context->channels, depth); + ffmpegdec->samplerate, ffmpegdec->channels, + ffmpegdec->depth, + ffmpegdec->context->sample_rate, ffmpegdec->context->channels, depth); - ffmpegdec->format.audio.samplerate = context->sample_rate; - ffmpegdec->format.audio.channels = context->channels; - ffmpegdec->format.audio.depth = depth; - memcpy (ffmpegdec->format.audio.ffmpeg_layout, pos, - sizeof (GstAudioChannelPosition) * context->channels); - - return TRUE; -} - -static gboolean -gst_ffmpegdec_audio_negotiate (GstFFMpegDec * ffmpegdec, gboolean force) -{ - GstFFMpegDecClass *oclass; - GstCaps *caps; - - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - - if (!update_audio_context (ffmpegdec, force)) - return TRUE; - - /* convert the raw output format to caps */ - caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type, - ffmpegdec->context, oclass->in_plugin->id, FALSE); - if (caps == NULL) - goto no_caps; + ffmpegdec->samplerate = ffmpegdec->context->sample_rate; + ffmpegdec->channels = ffmpegdec->context->channels; + ffmpegdec->depth = depth; + memcpy (ffmpegdec->ffmpeg_layout, pos, + sizeof (GstAudioChannelPosition) * ffmpegdec->context->channels); /* Get GStreamer channel layout */ - memcpy (ffmpegdec->format.audio.gst_layout, - ffmpegdec->format.audio.ffmpeg_layout, - sizeof (GstAudioChannelPosition) * ffmpegdec->format.audio.channels); - gst_audio_channel_positions_to_valid_order (ffmpegdec->format. - audio.gst_layout, ffmpegdec->format.audio.channels); + memcpy (ffmpegdec->gst_layout, + ffmpegdec->ffmpeg_layout, + sizeof (GstAudioChannelPosition) * ffmpegdec->channels); + gst_audio_channel_positions_to_valid_order (ffmpegdec->gst_layout, + ffmpegdec->channels); + + caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type, + ffmpegdec->context, oclass->in_plugin->id, FALSE); + + if (caps == NULL) + goto no_caps; GST_LOG_OBJECT (ffmpegdec, "output caps %" GST_PTR_FORMAT, caps); @@ -1490,292 +520,8 @@ caps_failed: } } -/* perform qos calculations before decoding the next frame. - * - * Sets the skip_frame flag and if things are really bad, skips to the next - * keyframe. - * - * Returns TRUE if the frame should be decoded, FALSE if the frame can be dropped - * entirely. - */ -static gboolean -gst_ffmpegdec_do_qos (GstFFMpegDec * ffmpegdec, GstClockTime timestamp, - gboolean * mode_switch) -{ - GstClockTimeDiff diff; - gdouble proportion; - GstClockTime qostime, earliest_time; - gboolean res = TRUE; - - *mode_switch = FALSE; - - /* no timestamp, can't do QoS */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) - goto no_qos; - - /* get latest QoS observation values */ - gst_ffmpegdec_read_qos (ffmpegdec, &proportion, &earliest_time); - - /* skip qos if we have no observation (yet) */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) { - /* no skip_frame initialy */ - ffmpegdec->context->skip_frame = AVDISCARD_DEFAULT; - goto no_qos; - } - - /* qos is done on running time of the timestamp */ - qostime = gst_segment_to_running_time (&ffmpegdec->segment, GST_FORMAT_TIME, - timestamp); - - /* timestamp can be out of segment, then we don't do QoS */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime))) - goto no_qos; - - /* see how our next timestamp relates to the latest qos timestamp. negative - * values mean we are early, positive values mean we are too late. */ - diff = GST_CLOCK_DIFF (qostime, earliest_time); - - GST_DEBUG_OBJECT (ffmpegdec, "QOS: qostime %" GST_TIME_FORMAT - ", earliest %" GST_TIME_FORMAT, GST_TIME_ARGS (qostime), - GST_TIME_ARGS (earliest_time)); - - /* if we using less than 40% of the available time, we can try to - * speed up again when we were slow. */ - if (proportion < 0.4 && diff < 0) { - goto normal_mode; - } else { - if (diff >= 0) { - /* we're too slow, try to speed up */ - if (ffmpegdec->waiting_for_key) { - /* we were waiting for a keyframe, that's ok */ - goto skipping; - } - /* switch to skip_frame mode */ - goto skip_frame; - } - } - -no_qos: - ffmpegdec->processed++; - return TRUE; - -skipping: - { - res = FALSE; - goto drop_qos; - } -normal_mode: - { - if (ffmpegdec->context->skip_frame != AVDISCARD_DEFAULT) { - ffmpegdec->context->skip_frame = AVDISCARD_DEFAULT; - *mode_switch = TRUE; - GST_DEBUG_OBJECT (ffmpegdec, "QOS: normal mode %g < 0.4", proportion); - } - ffmpegdec->processed++; - return TRUE; - } -skip_frame: - { - if (ffmpegdec->context->skip_frame != AVDISCARD_NONREF) { - ffmpegdec->context->skip_frame = AVDISCARD_NONREF; - *mode_switch = TRUE; - GST_DEBUG_OBJECT (ffmpegdec, - "QOS: hurry up, diff %" G_GINT64_FORMAT " >= 0", diff); - } - goto drop_qos; - } -drop_qos: - { - GstClockTime stream_time, jitter; - GstMessage *qos_msg; - - ffmpegdec->dropped++; - stream_time = - gst_segment_to_stream_time (&ffmpegdec->segment, GST_FORMAT_TIME, - timestamp); - jitter = GST_CLOCK_DIFF (qostime, earliest_time); - qos_msg = - gst_message_new_qos (GST_OBJECT_CAST (ffmpegdec), FALSE, qostime, - stream_time, timestamp, GST_CLOCK_TIME_NONE); - gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000); - gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, - ffmpegdec->processed, ffmpegdec->dropped); - gst_element_post_message (GST_ELEMENT_CAST (ffmpegdec), qos_msg); - - return res; - } -} - -/* returns TRUE if buffer is within segment, else FALSE. - * if Buffer is on segment border, it's timestamp and duration will be clipped */ -static gboolean -clip_video_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts, - GstClockTime in_dur) -{ - gboolean res = TRUE; - guint64 cstart, cstop; - GstClockTime stop; - - GST_LOG_OBJECT (dec, - "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT, - GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur)); - - /* can't clip without TIME segment */ - if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME)) - goto beach; - - /* we need a start time */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) - goto beach; - - /* generate valid stop, if duration unknown, we have unknown stop */ - stop = - GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE; - - /* now clip */ - res = - gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts, stop, &cstart, - &cstop); - if (G_UNLIKELY (!res)) - goto beach; - - /* we're pretty sure the duration of this buffer is not till the end of this - * segment (which _clip will assume when the stop is -1) */ - if (stop == GST_CLOCK_TIME_NONE) - cstop = GST_CLOCK_TIME_NONE; - - /* update timestamp and possibly duration if the clipped stop time is - * valid */ - GST_BUFFER_TIMESTAMP (buf) = cstart; - if (GST_CLOCK_TIME_IS_VALID (cstop)) - GST_BUFFER_DURATION (buf) = cstop - cstart; - - GST_LOG_OBJECT (dec, - "clipped timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT, - GST_TIME_ARGS (cstart), GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - -beach: - GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : "")); - return res; -} - - -/* figure out if the current picture is a keyframe, return TRUE if that is - * the case. */ -static gboolean -check_keyframe (GstFFMpegDec * ffmpegdec) -{ - GstFFMpegDecClass *oclass; - gboolean is_itype = FALSE; - gboolean is_reference = FALSE; - gboolean iskeyframe; - - /* figure out if we are dealing with a keyframe */ - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - - /* remember that we have B frames, we need this for the DTS -> PTS conversion - * code */ - if (!ffmpegdec->has_b_frames && ffmpegdec->picture->pict_type == FF_B_TYPE) { - GST_DEBUG_OBJECT (ffmpegdec, "we have B frames"); - ffmpegdec->has_b_frames = TRUE; - /* Emit latency message to recalculate it */ - gst_element_post_message (GST_ELEMENT_CAST (ffmpegdec), - gst_message_new_latency (GST_OBJECT_CAST (ffmpegdec))); - } - - is_itype = (ffmpegdec->picture->pict_type == FF_I_TYPE); - is_reference = (ffmpegdec->picture->reference == 1); - - iskeyframe = (is_itype || is_reference || ffmpegdec->picture->key_frame) - || (oclass->in_plugin->id == CODEC_ID_INDEO3) - || (oclass->in_plugin->id == CODEC_ID_MSZH) - || (oclass->in_plugin->id == CODEC_ID_ZLIB) - || (oclass->in_plugin->id == CODEC_ID_VP3) - || (oclass->in_plugin->id == CODEC_ID_HUFFYUV); - - GST_LOG_OBJECT (ffmpegdec, - "current picture: type: %d, is_keyframe:%d, is_itype:%d, is_reference:%d", - ffmpegdec->picture->pict_type, iskeyframe, is_itype, is_reference); - - return iskeyframe; -} - -/* get an outbuf buffer with the current picture */ -static GstFlowReturn -get_output_buffer (GstFFMpegDec * ffmpegdec, GstBuffer ** outbuf) -{ - GstFlowReturn ret; - - if (ffmpegdec->picture->opaque != NULL) { - GstVideoFrame *frame; - - /* we allocated a picture already for ffmpeg to decode into, let's pick it - * up and use it now. */ - frame = ffmpegdec->picture->opaque; - *outbuf = frame->buffer; - GST_LOG_OBJECT (ffmpegdec, "using opaque buffer %p on frame %p", *outbuf, - frame); - gst_buffer_ref (*outbuf); - } else { - GstVideoFrame frame; - AVPicture *src, *dest; - AVFrame pic; - gint width, height; - GstBuffer *buf; - - GST_LOG_OBJECT (ffmpegdec, "allocating an output buffer"); - - if (G_UNLIKELY (!gst_ffmpegdec_video_negotiate (ffmpegdec, FALSE))) - goto negotiate_failed; - - ret = gst_buffer_pool_acquire_buffer (ffmpegdec->pool, &buf, NULL); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto alloc_failed; - - if (!gst_video_frame_map (&frame, &ffmpegdec->out_info, buf, - GST_MAP_READWRITE)) - goto invalid_frame; - - gst_ffmpegdec_fill_picture (ffmpegdec, &frame, &pic); - - width = ffmpegdec->out_info.width; - height = ffmpegdec->out_info.height; - - src = (AVPicture *) ffmpegdec->picture; - dest = (AVPicture *) & pic; - - GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec, - "copy picture to output buffer %dx%d", width, height); - av_picture_copy (dest, src, ffmpegdec->context->pix_fmt, width, height); - - gst_video_frame_unmap (&frame); - - *outbuf = buf; - } - ffmpegdec->picture->reordered_opaque = -1; - - return GST_FLOW_OK; - - /* special cases */ -negotiate_failed: - { - GST_DEBUG_OBJECT (ffmpegdec, "negotiation failed"); - return GST_FLOW_NOT_NEGOTIATED; - } -alloc_failed: - { - GST_DEBUG_OBJECT (ffmpegdec, "buffer alloc failed"); - return ret; - } -invalid_frame: - { - GST_DEBUG_OBJECT (ffmpegdec, "could not map frame"); - return GST_FLOW_ERROR; - } -} - static void -clear_queued (GstFFMpegDec * ffmpegdec) +clear_queued (GstFFMpegAudDec * ffmpegdec) { g_list_foreach (ffmpegdec->queued, (GFunc) gst_mini_object_unref, NULL); g_list_free (ffmpegdec->queued); @@ -1783,7 +529,7 @@ clear_queued (GstFFMpegDec * ffmpegdec) } static GstFlowReturn -flush_queued (GstFFMpegDec * ffmpegdec) +flush_queued (GstFFMpegAudDec * ffmpegdec) { GstFlowReturn res = GST_FLOW_OK; @@ -1814,369 +560,10 @@ gst_avpacket_init (AVPacket * packet, guint8 * data, guint size) packet->size = size; } -/* gst_ffmpegdec_[video|audio]_frame: - * ffmpegdec: - * data: pointer to the data to decode - * size: size of data in bytes - * dec_info: timestamp info - * outbuf: outgoing buffer. Different from NULL ONLY if it contains decoded data. - * ret: Return flow. - * - * Returns: number of bytes used in decoding. The check for successful decode is - * outbuf being non-NULL. - */ -static gint -gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec, - guint8 * data, guint size, - const GstTSInfo * dec_info, GstBuffer ** outbuf, GstFlowReturn * ret) -{ - gint len = -1; - gint have_data; - gboolean iskeyframe; - gboolean mode_switch; - gboolean decode; - gint skip_frame = AVDISCARD_DEFAULT; - GstClockTime out_dts, out_pts, out_duration; - gint64 out_offset; - const GstTSInfo *out_info; - AVPacket packet; - - *ret = GST_FLOW_OK; - *outbuf = NULL; - - ffmpegdec->context->opaque = ffmpegdec; - - /* in case we skip frames */ - ffmpegdec->picture->pict_type = -1; - - /* run QoS code, we don't stop decoding the frame when we are late because - * else we might skip a reference frame */ - decode = gst_ffmpegdec_do_qos (ffmpegdec, dec_info->pts, &mode_switch); - - if (ffmpegdec->is_realvideo && data != NULL) { - gint slice_count; - gint i; - - /* setup the slice table for realvideo */ - if (ffmpegdec->context->slice_offset == NULL) - ffmpegdec->context->slice_offset = g_malloc (sizeof (guint32) * 1000); - - slice_count = (*data++) + 1; - ffmpegdec->context->slice_count = slice_count; - - for (i = 0; i < slice_count; i++) { - data += 4; - ffmpegdec->context->slice_offset[i] = GST_READ_UINT32_LE (data); - data += 4; - } - } - - if (!decode) { - /* no decoding needed, save previous skip_frame value and brutely skip - * decoding everything */ - skip_frame = ffmpegdec->context->skip_frame; - ffmpegdec->context->skip_frame = AVDISCARD_NONREF; - } - - /* save reference to the timing info */ - ffmpegdec->context->reordered_opaque = (gint64) dec_info->idx; - ffmpegdec->picture->reordered_opaque = (gint64) dec_info->idx; - - GST_DEBUG_OBJECT (ffmpegdec, "stored opaque values idx %d", dec_info->idx); - - /* now decode the frame */ - gst_avpacket_init (&packet, data, size); - - if (ffmpegdec->context->palctrl) { - guint8 *pal; - - pal = av_packet_new_side_data (&packet, AV_PKT_DATA_PALETTE, - AVPALETTE_SIZE); - memcpy (pal, ffmpegdec->context->palctrl->palette, AVPALETTE_SIZE); - GST_DEBUG_OBJECT (ffmpegdec, "copy pal %p %p", &packet, pal); - } - - len = avcodec_decode_video2 (ffmpegdec->context, - ffmpegdec->picture, &have_data, &packet); - - /* restore previous state */ - if (!decode) - ffmpegdec->context->skip_frame = skip_frame; - - GST_DEBUG_OBJECT (ffmpegdec, "after decode: len %d, have_data %d", - len, have_data); - - /* when we are in skip_frame mode, don't complain when ffmpeg returned - * no data because we told it to skip stuff. */ - if (len < 0 && (mode_switch || ffmpegdec->context->skip_frame)) - len = 0; - - if (len > 0 && have_data <= 0 && (mode_switch - || ffmpegdec->context->skip_frame)) { - /* we consumed some bytes but nothing decoded and we are skipping frames, - * disable the interpollation of DTS timestamps */ - ffmpegdec->last_out = -1; - } - - /* no data, we're done */ - if (len < 0 || have_data <= 0) - goto beach; - - /* get the output picture timing info again */ - out_info = gst_ts_info_get (ffmpegdec, ffmpegdec->picture->reordered_opaque); - out_dts = out_info->dts; - out_pts = out_info->pts; - out_duration = out_info->duration; - out_offset = out_info->offset; - - GST_DEBUG_OBJECT (ffmpegdec, - "dts %" G_GUINT64_FORMAT " pts %" G_GUINT64_FORMAT " duration %" - G_GUINT64_FORMAT " offset %" G_GINT64_FORMAT, out_dts, out_pts, - out_duration, out_offset); - GST_DEBUG_OBJECT (ffmpegdec, "picture: pts %" G_GUINT64_FORMAT, - (guint64) ffmpegdec->picture->pts); - GST_DEBUG_OBJECT (ffmpegdec, "picture: num %d", - ffmpegdec->picture->coded_picture_number); - GST_DEBUG_OBJECT (ffmpegdec, "picture: ref %d", - ffmpegdec->picture->reference); - GST_DEBUG_OBJECT (ffmpegdec, "picture: display %d", - ffmpegdec->picture->display_picture_number); - GST_DEBUG_OBJECT (ffmpegdec, "picture: opaque %p", - ffmpegdec->picture->opaque); - GST_DEBUG_OBJECT (ffmpegdec, "picture: reordered opaque %" G_GUINT64_FORMAT, - (guint64) ffmpegdec->picture->reordered_opaque); - GST_DEBUG_OBJECT (ffmpegdec, "repeat_pict:%d", - ffmpegdec->picture->repeat_pict); - GST_DEBUG_OBJECT (ffmpegdec, "interlaced_frame:%d", - ffmpegdec->picture->interlaced_frame); - - if (G_UNLIKELY (ffmpegdec->picture->interlaced_frame != - ffmpegdec->ctx_interlaced)) { - GST_WARNING ("Change in interlacing ! picture:%d, recorded:%d", - ffmpegdec->picture->interlaced_frame, ffmpegdec->ctx_interlaced); - ffmpegdec->ctx_interlaced = ffmpegdec->picture->interlaced_frame; - gst_ffmpegdec_video_negotiate (ffmpegdec, TRUE); - } -#if 0 - /* Whether a frame is interlaced or not is unknown at the time of - buffer allocation, so caps on the buffer in opaque will have - the previous frame's interlaced flag set. So if interlacedness - has changed since allocation, we update the buffer (if any) - caps now with the correct interlaced flag. */ - if (ffmpegdec->picture->opaque != NULL) { - GstBuffer *buffer = ffmpegdec->picture->opaque; - if (GST_BUFFER_CAPS (buffer) && GST_PAD_CAPS (ffmpegdec->srcpad)) { - GstStructure *s = gst_caps_get_structure (GST_BUFFER_CAPS (buffer), 0); - gboolean interlaced; - gboolean found = gst_structure_get_boolean (s, "interlaced", &interlaced); - if (!found || (! !interlaced != ! !ffmpegdec->format.video.interlaced)) { - GST_DEBUG_OBJECT (ffmpegdec, - "Buffer interlacing does not match pad, updating"); - buffer = gst_buffer_make_metadata_writable (buffer); - gst_buffer_set_caps (buffer, GST_PAD_CAPS (ffmpegdec->srcpad)); - ffmpegdec->picture->opaque = buffer; - } - } - } -#endif - - /* check if we are dealing with a keyframe here, this will also check if we - * are dealing with B frames. */ - iskeyframe = check_keyframe (ffmpegdec); - - if (out_pts == 0 && out_pts == ffmpegdec->last_out) { - GST_LOG_OBJECT (ffmpegdec, "ffmpeg returns 0 timestamps, ignoring"); - /* some codecs only output 0 timestamps, when that happens, make us select an - * output timestamp based on the input timestamp. We do this by making the - * ffmpeg timestamp and the interpollated next timestamp invalid. */ - out_pts = -1; - ffmpegdec->next_out = -1; - } else - ffmpegdec->last_out = out_pts; - - - if (iskeyframe && out_pts == -1) { - GST_DEBUG_OBJECT (ffmpegdec, "keyframe, use DTS %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_dts)); - out_pts = out_dts; - } - - /* when we're waiting for a keyframe, see if we have one or drop the current - * non-keyframe */ - if (G_UNLIKELY (ffmpegdec->waiting_for_key)) { - if (G_LIKELY (!iskeyframe)) - goto drop_non_keyframe; - - /* we have a keyframe, we can stop waiting for one */ - ffmpegdec->waiting_for_key = FALSE; - } - - /* get a handle to the output buffer */ - *ret = get_output_buffer (ffmpegdec, outbuf); - if (G_UNLIKELY (*ret != GST_FLOW_OK)) - goto no_output; - - /* - * Timestamps: - * - * 1) Copy picture timestamp if valid - * 2) else interpolate from previous output timestamp - * 3) else copy input timestamp - */ - if (out_pts != -1) { - /* Use (interpolated) timestamp from FFMPEG */ - GST_LOG_OBJECT (ffmpegdec, "using timestamp %" GST_TIME_FORMAT - " returned by ffmpeg", GST_TIME_ARGS (out_pts)); - } else if (ffmpegdec->next_out != -1) { - out_pts = ffmpegdec->next_out; - GST_LOG_OBJECT (ffmpegdec, "using next timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_pts)); - } else { - out_pts = dec_info->pts; - GST_LOG_OBJECT (ffmpegdec, "using in timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_pts)); - } - GST_BUFFER_DTS (*outbuf) = out_pts; - GST_BUFFER_PTS (*outbuf) = out_pts; - - /* - * Offset: - * 0) Use stored input offset (from opaque) - * 1) Use value converted from timestamp if valid - * 2) Use input offset if valid - */ - if (out_offset != GST_BUFFER_OFFSET_NONE) { - /* out_offset already contains the offset from ts_info */ - GST_LOG_OBJECT (ffmpegdec, "Using offset returned by ffmpeg"); - } else if (out_pts != GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (ffmpegdec, "Using offset converted from timestamp"); - /* FIXME, we should really remove this as it's not nice at all to do - * upstream queries for each frame to get the frame offset. We also can't - * really remove this because it is the only way of setting frame offsets - * on outgoing buffers. We should have metadata so that the upstream peer - * can set a frame number on the encoded data. */ - gst_pad_peer_query_convert (ffmpegdec->sinkpad, - GST_FORMAT_TIME, out_pts, GST_FORMAT_DEFAULT, &out_offset); - } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) { - /* FIXME, the input offset is input media specific and might not - * be the same for the output media. (byte offset as input, frame number - * as output, for example) */ - GST_LOG_OBJECT (ffmpegdec, "using in_offset %" G_GINT64_FORMAT, - dec_info->offset); - out_offset = dec_info->offset; - } else { - GST_LOG_OBJECT (ffmpegdec, "no valid offset found"); - out_offset = GST_BUFFER_OFFSET_NONE; - } - GST_BUFFER_OFFSET (*outbuf) = out_offset; - - /* - * Duration: - * - * 1) Use reordered input duration if valid - * 2) Else use input duration - * 3) else use input framerate - * 4) else use ffmpeg framerate - */ - if (GST_CLOCK_TIME_IS_VALID (out_duration)) { - /* We have a valid (reordered) duration */ - GST_LOG_OBJECT (ffmpegdec, - "Using duration returned by ffmpeg %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_duration)); - } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) { - out_duration = dec_info->duration; - GST_LOG_OBJECT (ffmpegdec, "using in_duration %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_duration)); - } else if (GST_CLOCK_TIME_IS_VALID (ffmpegdec->last_diff)) { - out_duration = ffmpegdec->last_diff; - GST_LOG_OBJECT (ffmpegdec, "using last-diff %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_duration)); - } else { - /* if we have an input framerate, use that */ - if (ffmpegdec->out_info.fps_n != -1 && - (ffmpegdec->out_info.fps_n != 1000 && ffmpegdec->out_info.fps_d != 1)) { - out_duration = gst_util_uint64_scale_int (GST_SECOND, - ffmpegdec->out_info.fps_d, ffmpegdec->out_info.fps_n); - GST_LOG_OBJECT (ffmpegdec, - "using input framerate for duration %" GST_TIME_FORMAT, - GST_TIME_ARGS (out_duration)); - } else { - /* don't try to use the decoder's framerate when it seems a bit abnormal, - * which we assume when den >= 1000... */ - if (ffmpegdec->context->time_base.num != 0 && - (ffmpegdec->context->time_base.den > 0 && - ffmpegdec->context->time_base.den < 1000)) { - out_duration = gst_util_uint64_scale_int (GST_SECOND, - ffmpegdec->context->time_base.num * - ffmpegdec->context->ticks_per_frame, - ffmpegdec->context->time_base.den); - GST_LOG_OBJECT (ffmpegdec, "using decoder's framerate for duration %" - GST_TIME_FORMAT, GST_TIME_ARGS (out_duration)); - } else { - GST_LOG_OBJECT (ffmpegdec, "no valid duration found"); - } - } - } - - /* Take repeat_pict into account */ - if (GST_CLOCK_TIME_IS_VALID (out_duration)) { - GST_LOG_OBJECT (ffmpegdec, "repeat pict %d", - ffmpegdec->picture->repeat_pict); - out_duration += out_duration * ffmpegdec->picture->repeat_pict / 2; - } - GST_BUFFER_DURATION (*outbuf) = out_duration; - - if (out_dts != -1 && out_duration != -1 && out_duration != 0) - ffmpegdec->next_out = out_pts + out_duration; - else - ffmpegdec->next_out = -1; - - GST_LOG_OBJECT (ffmpegdec, "next out %" GST_TIME_FORMAT, - GST_TIME_ARGS (ffmpegdec->next_out)); - - /* now see if we need to clip the buffer against the segment boundaries. */ - if (G_UNLIKELY (!clip_video_buffer (ffmpegdec, *outbuf, out_pts, - out_duration))) - goto clipped; - - /* mark as keyframe or delta unit */ - if (!iskeyframe) - GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - - if (ffmpegdec->picture->top_field_first) - GST_BUFFER_FLAG_SET (*outbuf, GST_VIDEO_BUFFER_FLAG_TFF); - - -beach: - GST_DEBUG_OBJECT (ffmpegdec, "return flow %d, out %p, len %d", - *ret, *outbuf, len); - return len; - - /* special cases */ -drop_non_keyframe: - { - GST_WARNING_OBJECT (ffmpegdec, "Dropping non-keyframe (seek/init)"); - goto beach; - } -no_output: - { - GST_DEBUG_OBJECT (ffmpegdec, "no output buffer"); - len = -1; - goto beach; - } -clipped: - { - GST_DEBUG_OBJECT (ffmpegdec, "buffer clipped"); - gst_buffer_unref (*outbuf); - *outbuf = NULL; - goto beach; - } -} - /* returns TRUE if buffer is within segment, else FALSE. * if Buffer is on segment border, it's timestamp and duration will be clipped */ static gboolean -clip_audio_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts, +clip_audio_buffer (GstFFMpegAudDec * dec, GstBuffer * buf, GstClockTime in_ts, GstClockTime in_dur) { GstClockTime stop; @@ -2213,8 +600,8 @@ clip_audio_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts, if (G_UNLIKELY ((diff = ctime - in_ts) > 0)) { /* bring clipped time to bytes */ diff = - gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, - GST_SECOND) * (dec->format.audio.depth * dec->format.audio.channels); + gst_util_uint64_scale_int (diff, dec->samplerate, + GST_SECOND) * (dec->depth * dec->channels); GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %" G_GINT64_FORMAT " bytes", GST_TIME_ARGS (ctime), diff); @@ -2225,8 +612,8 @@ clip_audio_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts, if (G_UNLIKELY ((diff = stop - cstop) > 0)) { /* bring clipped time to bytes */ diff = - gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, - GST_SECOND) * (dec->format.audio.depth * dec->format.audio.channels); + gst_util_uint64_scale_int (diff, dec->samplerate, + GST_SECOND) * (dec->depth * dec->channels); GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %" G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff); @@ -2250,7 +637,7 @@ out_of_segment: } static gint -gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec, +gst_ffmpegauddec_audio_frame (GstFFMpegAudDec * ffmpegdec, AVCodec * in_plugin, guint8 * data, guint size, const GstTSInfo * dec_info, GstBuffer ** outbuf, GstFlowReturn * ret) { @@ -2288,7 +675,7 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec, gst_buffer_resize (*outbuf, 0, have_data); GST_DEBUG_OBJECT (ffmpegdec, "Creating output buffer"); - if (!gst_ffmpegdec_audio_negotiate (ffmpegdec, FALSE)) { + if (!gst_ffmpegauddec_negotiate (ffmpegdec, FALSE)) { gst_buffer_unref (*outbuf); *outbuf = NULL; len = -1; @@ -2314,8 +701,7 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec, * 1) calculate based on number of samples */ out_duration = gst_util_uint64_scale (have_data, GST_SECOND, - ffmpegdec->format.audio.depth * ffmpegdec->format.audio.channels * - ffmpegdec->format.audio.samplerate); + ffmpegdec->depth * ffmpegdec->channels * ffmpegdec->samplerate); /* offset: * @@ -2346,12 +732,10 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec, /* Only the width really matters here... and it's stored as depth */ fmt = gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, - ffmpegdec->format.audio.depth * 8, ffmpegdec->format.audio.depth * 8); + ffmpegdec->depth * 8, ffmpegdec->depth * 8); gst_audio_buffer_reorder_channels (*outbuf, fmt, - ffmpegdec->format.audio.channels, - ffmpegdec->format.audio.ffmpeg_layout, - ffmpegdec->format.audio.gst_layout); + ffmpegdec->channels, ffmpegdec->ffmpeg_layout, ffmpegdec->gst_layout); } else { gst_buffer_unmap (*outbuf, &map); gst_buffer_unref (*outbuf); @@ -2382,7 +766,7 @@ clipped: } } -/* gst_ffmpegdec_frame: +/* gst_ffmpegauddec_frame: * ffmpegdec: * data: pointer to the data to decode * size: size of data in bytes @@ -2397,11 +781,11 @@ clipped: */ static gint -gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec, +gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, guint8 * data, guint size, gint * got_data, const GstTSInfo * dec_info, GstFlowReturn * ret) { - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; GstBuffer *outbuf = NULL; gint have_data = 0, len = 0; @@ -2414,31 +798,18 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec, *ret = GST_FLOW_OK; ffmpegdec->context->frame_number++; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - switch (oclass->in_plugin->type) { - case AVMEDIA_TYPE_VIDEO: - len = - gst_ffmpegdec_video_frame (ffmpegdec, data, size, dec_info, &outbuf, - ret); - break; - case AVMEDIA_TYPE_AUDIO: - len = - gst_ffmpegdec_audio_frame (ffmpegdec, oclass->in_plugin, data, size, - dec_info, &outbuf, ret); + len = + gst_ffmpegauddec_audio_frame (ffmpegdec, oclass->in_plugin, data, size, + dec_info, &outbuf, ret); - /* if we did not get an output buffer and we have a pending discont, don't - * clear the input timestamps, we will put them on the next buffer because - * else we might create the first buffer with a very big timestamp gap. */ - if (outbuf == NULL && ffmpegdec->discont) { - GST_DEBUG_OBJECT (ffmpegdec, "no buffer but keeping timestamp"); - ffmpegdec->clear_ts = FALSE; - } - break; - default: - GST_ERROR_OBJECT (ffmpegdec, "Asked to decode non-audio/video frame !"); - g_assert_not_reached (); - break; + /* if we did not get an output buffer and we have a pending discont, don't + * clear the input timestamps, we will put them on the next buffer because + * else we might create the first buffer with a very big timestamp gap. */ + if (outbuf == NULL && ffmpegdec->discont) { + GST_DEBUG_OBJECT (ffmpegdec, "no buffer but keeping timestamp"); + ffmpegdec->clear_ts = FALSE; } if (outbuf) @@ -2496,11 +867,11 @@ no_codec: } static void -gst_ffmpegdec_drain (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec) { - GstFFMpegDecClass *oclass; + GstFFMpegAudDecClass *oclass; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); if (oclass->in_plugin->capabilities & CODEC_CAP_DELAY) { gint have_data, len, try = 0; @@ -2512,7 +883,7 @@ gst_ffmpegdec_drain (GstFFMpegDec * ffmpegdec) GstFlowReturn ret; len = - gst_ffmpegdec_frame (ffmpegdec, NULL, 0, &have_data, &ts_info_none, + gst_ffmpegauddec_frame (ffmpegdec, NULL, 0, &have_data, &ts_info_none, &ret); if (len < 0 || have_data == 0) break; @@ -2525,7 +896,7 @@ gst_ffmpegdec_drain (GstFFMpegDec * ffmpegdec) } static void -gst_ffmpegdec_flush_pcache (GstFFMpegDec * ffmpegdec) +gst_ffmpegauddec_flush_pcache (GstFFMpegAudDec * ffmpegdec) { if (ffmpegdec->pctx) { gint size, bsize; @@ -2550,12 +921,12 @@ gst_ffmpegdec_flush_pcache (GstFFMpegDec * ffmpegdec) } static gboolean -gst_ffmpegdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +gst_ffmpegauddec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { - GstFFMpegDec *ffmpegdec; + GstFFMpegAudDec *ffmpegdec; gboolean ret = FALSE; - ffmpegdec = (GstFFMpegDec *) parent; + ffmpegdec = (GstFFMpegAudDec *) parent; GST_DEBUG_OBJECT (ffmpegdec, "Handling %s event", GST_EVENT_TYPE_NAME (event)); @@ -2563,7 +934,7 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { - gst_ffmpegdec_drain (ffmpegdec); + gst_ffmpegauddec_drain (ffmpegdec); break; } case GST_EVENT_FLUSH_STOP: @@ -2571,10 +942,8 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) if (ffmpegdec->opened) { avcodec_flush_buffers (ffmpegdec->context); } - gst_ffmpegdec_reset_ts (ffmpegdec); - gst_ffmpegdec_reset_qos (ffmpegdec); - gst_ffmpegdec_flush_pcache (ffmpegdec); - ffmpegdec->waiting_for_key = TRUE; + gst_ffmpegauddec_reset_ts (ffmpegdec); + gst_ffmpegauddec_flush_pcache (ffmpegdec); gst_segment_init (&ffmpegdec->segment, GST_FORMAT_TIME); clear_queued (ffmpegdec); break; @@ -2587,7 +956,7 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) if (!ffmpegdec->last_caps || !gst_caps_is_equal (ffmpegdec->last_caps, caps)) { - ret = gst_ffmpegdec_setcaps (ffmpegdec, caps); + ret = gst_ffmpegauddec_setcaps (ffmpegdec, caps); if (ret) { gst_caps_replace (&ffmpegdec->last_caps, caps); } @@ -2681,12 +1050,12 @@ invalid_format: } static gboolean -gst_ffmpegdec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) +gst_ffmpegauddec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) { - GstFFMpegDec *ffmpegdec; + GstFFMpegAudDec *ffmpegdec; gboolean ret = FALSE; - ffmpegdec = (GstFFMpegDec *) parent; + ffmpegdec = (GstFFMpegAudDec *) parent; GST_DEBUG_OBJECT (ffmpegdec, "Handling %s query", GST_QUERY_TYPE_NAME (query)); @@ -2711,19 +1080,6 @@ gst_ffmpegdec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) } break; } - case GST_QUERY_ALLOCATION: - { - GstAllocationParams params; - - gst_allocation_params_init (¶ms); - params.flags = GST_MEMORY_FLAG_ZERO_PADDED; - params.padding = FF_INPUT_BUFFER_PADDING_SIZE; - /* we would like to have some padding so that we don't have to - * memcpy. We don't suggest an allocator. */ - gst_query_add_allocation_param (query, NULL, ¶ms); - ret = TRUE; - break; - } default: ret = gst_pad_query_default (pad, parent, query); break; @@ -2732,21 +1088,21 @@ gst_ffmpegdec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) } static GstFlowReturn -gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) +gst_ffmpegauddec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) { - GstFFMpegDec *ffmpegdec; - GstFFMpegDecClass *oclass; + GstFFMpegAudDec *ffmpegdec; + GstFFMpegAudDecClass *oclass; guint8 *data, *bdata; GstMapInfo map; gint size, bsize, len, have_data; GstFlowReturn ret = GST_FLOW_OK; GstClockTime in_pts, in_dts, in_duration; - gboolean discont, do_padding; + gboolean discont; gint64 in_offset; const GstTSInfo *in_info; const GstTSInfo *dec_info; - ffmpegdec = (GstFFMpegDec *) parent; + ffmpegdec = (GstFFMpegAudDec *) parent; if (G_UNLIKELY (!ffmpegdec->opened)) goto not_negotiated; @@ -2760,27 +1116,18 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) * the pcache, we might be able to remove the drain and flush too. */ if (G_UNLIKELY (discont)) { GST_DEBUG_OBJECT (ffmpegdec, "received DISCONT"); - gst_ffmpegdec_flush_pcache (ffmpegdec); + /* drain what we have queued */ + gst_ffmpegauddec_drain (ffmpegdec); + gst_ffmpegauddec_flush_pcache (ffmpegdec); ffmpegdec->discont = TRUE; - gst_ffmpegdec_reset_ts (ffmpegdec); + gst_ffmpegauddec_reset_ts (ffmpegdec); } /* by default we clear the input timestamp after decoding each frame so that * interpollation can work. */ ffmpegdec->clear_ts = TRUE; - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); - /* do early keyframe check pretty bad to rely on the keyframe flag in the - * source for this as it might not even be parsed (UDP/file/..). */ - if (G_UNLIKELY (ffmpegdec->waiting_for_key)) { - GST_DEBUG_OBJECT (ffmpegdec, "waiting for keyframe"); - if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DELTA_UNIT) && - oclass->in_plugin->type != AVMEDIA_TYPE_AUDIO) - goto skip_keyframe; - - GST_DEBUG_OBJECT (ffmpegdec, "got keyframe"); - ffmpegdec->waiting_for_key = FALSE; - } /* parse cache joining. If there is cached data */ if (ffmpegdec->pcache) { /* join with previous data */ @@ -2799,21 +1146,11 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) in_info = gst_ts_info_store (ffmpegdec, in_dts, in_pts, in_duration, in_offset); - if (in_dts != -1) { - GstClockTime diff; - /* keep track of timestamp diff to estimate duration */ - diff = in_dts - ffmpegdec->last_dts; - /* need to scale with amount of frames in the interval */ - if (ffmpegdec->last_frames) - diff /= ffmpegdec->last_frames; - - GST_LOG_OBJECT (ffmpegdec, "estimated duration %" GST_TIME_FORMAT " %u", - GST_TIME_ARGS (diff), ffmpegdec->last_frames); - - ffmpegdec->last_diff = diff; - ffmpegdec->last_dts = in_dts; - ffmpegdec->last_frames = 0; - } + GST_LOG_OBJECT (ffmpegdec, + "Received new data of size %u, offset:%" G_GUINT64_FORMAT ", ts:%" + GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT ", info %d", + gst_buffer_get_size (inbuf), GST_BUFFER_OFFSET (inbuf), + GST_TIME_ARGS (in_pts), GST_TIME_ARGS (in_duration), in_info->idx); /* workarounds, functions write to buffers: * libavcodec/svq1.c:svq1_decode_frame writes to the given buffer. @@ -2835,31 +1172,11 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) ", info %d", bsize, in_offset, GST_TIME_ARGS (in_dts), GST_TIME_ARGS (in_pts), GST_TIME_ARGS (in_duration), in_info->idx); - if (!GST_MEMORY_IS_ZERO_PADDED (map.memory) - || (map.maxsize - map.size) < FF_INPUT_BUFFER_PADDING_SIZE) { - /* add padding */ - if (ffmpegdec->padded_size < bsize + FF_INPUT_BUFFER_PADDING_SIZE) { - ffmpegdec->padded_size = bsize + FF_INPUT_BUFFER_PADDING_SIZE; - ffmpegdec->padded = g_realloc (ffmpegdec->padded, ffmpegdec->padded_size); - GST_LOG_OBJECT (ffmpegdec, "resized padding buffer to %d", - ffmpegdec->padded_size); - } - GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec, - "Copy input to add padding"); - memcpy (ffmpegdec->padded, bdata, bsize); - memset (ffmpegdec->padded + bsize, 0, FF_INPUT_BUFFER_PADDING_SIZE); - - bdata = ffmpegdec->padded; - } - do { - guint8 tmp_padding[FF_INPUT_BUFFER_PADDING_SIZE]; - /* parse, if at all possible */ if (ffmpegdec->pctx) { gint res; - do_padding = TRUE; GST_LOG_OBJECT (ffmpegdec, "Calling av_parser_parse2 with offset %" G_GINT64_FORMAT ", ts:%" GST_TIME_FORMAT " size %d", in_offset, GST_TIME_ARGS (in_pts), bsize); @@ -2906,28 +1223,16 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) break; } } else { - do_padding = FALSE; data = bdata; size = bsize; dec_info = in_info; } - if (do_padding) { - /* add temporary padding */ - GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec, - "Add temporary input padding"); - memcpy (tmp_padding, data + size, FF_INPUT_BUFFER_PADDING_SIZE); - memset (data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); - } - - /* decode a frame of audio/video now */ + /* decode a frame of audio now */ len = - gst_ffmpegdec_frame (ffmpegdec, data, size, &have_data, dec_info, &ret); - - if (do_padding) { - memcpy (data + size, tmp_padding, FF_INPUT_BUFFER_PADDING_SIZE); - } + gst_ffmpegauddec_frame (ffmpegdec, data, size, &have_data, dec_info, + &ret); if (ret != GST_FLOW_OK) { GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s", @@ -2978,8 +1283,6 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) } else { ffmpegdec->clear_ts = TRUE; } - ffmpegdec->last_frames++; - do_padding = TRUE; GST_LOG_OBJECT (ffmpegdec, "Before (while bsize>0). bsize:%d , bdata:%p", bsize, bdata); @@ -3014,25 +1317,19 @@ gst_ffmpegdec_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) /* ERRORS */ not_negotiated: { - oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL), ("avdec_%s: input format was not set before data start", oclass->in_plugin->name)); gst_buffer_unref (inbuf); return GST_FLOW_NOT_NEGOTIATED; } -skip_keyframe: - { - GST_DEBUG_OBJECT (ffmpegdec, "skipping non keyframe"); - gst_buffer_unref (inbuf); - return GST_FLOW_OK; - } } static GstStateChangeReturn -gst_ffmpegdec_change_state (GstElement * element, GstStateChange transition) +gst_ffmpegauddec_change_state (GstElement * element, GstStateChange transition) { - GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element; + GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) element; GstStateChangeReturn ret; ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); @@ -3040,17 +1337,9 @@ gst_ffmpegdec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: GST_OBJECT_LOCK (ffmpegdec); - gst_ffmpegdec_close (ffmpegdec); + gst_ffmpegauddec_close (ffmpegdec); GST_OBJECT_UNLOCK (ffmpegdec); clear_queued (ffmpegdec); - g_free (ffmpegdec->padded); - ffmpegdec->padded = NULL; - ffmpegdec->padded_size = 0; - if (ffmpegdec->pool) { - gst_buffer_pool_set_active (ffmpegdec->pool, FALSE); - gst_object_unref (ffmpegdec->pool); - } - ffmpegdec->pool = NULL; break; default: break; @@ -3059,77 +1348,19 @@ gst_ffmpegdec_change_state (GstElement * element, GstStateChange transition) return ret; } -static void -gst_ffmpegdec_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object; - - switch (prop_id) { - case PROP_LOWRES: - ffmpegdec->lowres = ffmpegdec->context->lowres = g_value_get_enum (value); - break; - case PROP_SKIPFRAME: - ffmpegdec->skip_frame = ffmpegdec->context->skip_frame = - g_value_get_enum (value); - break; - case PROP_DIRECT_RENDERING: - ffmpegdec->direct_rendering = g_value_get_boolean (value); - break; - case PROP_DEBUG_MV: - ffmpegdec->debug_mv = ffmpegdec->context->debug_mv = - g_value_get_boolean (value); - break; - case PROP_MAX_THREADS: - ffmpegdec->max_threads = g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_ffmpegdec_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object; - - switch (prop_id) { - case PROP_LOWRES: - g_value_set_enum (value, ffmpegdec->context->lowres); - break; - case PROP_SKIPFRAME: - g_value_set_enum (value, ffmpegdec->context->skip_frame); - break; - case PROP_DIRECT_RENDERING: - g_value_set_boolean (value, ffmpegdec->direct_rendering); - break; - case PROP_DEBUG_MV: - g_value_set_boolean (value, ffmpegdec->context->debug_mv); - break; - case PROP_MAX_THREADS: - g_value_set_int (value, ffmpegdec->max_threads); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - gboolean -gst_ffmpegdec_register (GstPlugin * plugin) +gst_ffmpegauddec_register (GstPlugin * plugin) { GTypeInfo typeinfo = { - sizeof (GstFFMpegDecClass), - (GBaseInitFunc) gst_ffmpegdec_base_init, + sizeof (GstFFMpegAudDecClass), + (GBaseInitFunc) gst_ffmpegauddec_base_init, NULL, - (GClassInitFunc) gst_ffmpegdec_class_init, + (GClassInitFunc) gst_ffmpegauddec_class_init, NULL, NULL, - sizeof (GstFFMpegDec), + sizeof (GstFFMpegAudDec), 0, - (GInstanceInitFunc) gst_ffmpegdec_init, + (GInstanceInitFunc) gst_ffmpegauddec_init, }; GType type; AVCodec *in_plugin; @@ -3144,17 +1375,13 @@ gst_ffmpegdec_register (GstPlugin * plugin) gchar *plugin_name; /* only decoders */ - if (!in_plugin->decode) { + if (!in_plugin->decode || in_plugin->type != AVMEDIA_TYPE_AUDIO) { goto next; } /* no quasi-codecs, please */ - if (in_plugin->id == CODEC_ID_RAWVIDEO || - in_plugin->id == CODEC_ID_V210 || - in_plugin->id == CODEC_ID_V210X || - in_plugin->id == CODEC_ID_R210 || - (in_plugin->id >= CODEC_ID_PCM_S16LE && - in_plugin->id <= CODEC_ID_PCM_BLURAY)) { + if (in_plugin->id >= CODEC_ID_PCM_S16LE && + in_plugin->id <= CODEC_ID_PCM_BLURAY) { goto next; } @@ -3168,33 +1395,13 @@ gst_ffmpegdec_register (GstPlugin * plugin) goto next; } - /* No vdpau plugins until we can figure out how to properly use them - * outside of ffmpeg. */ - if (g_str_has_suffix (in_plugin->name, "_vdpau")) { - GST_DEBUG - ("Ignoring VDPAU decoder %s. We can't handle this outside of libav", - in_plugin->name); - goto next; - } - - if (g_str_has_suffix (in_plugin->name, "_xvmc")) { - GST_DEBUG - ("Ignoring XVMC decoder %s. We can't handle this outside of libav", - in_plugin->name); - goto next; - } - GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name); /* no codecs for which we're GUARANTEED to have better alternatives */ - /* MPEG1VIDEO : the mpeg2video decoder is preferred */ /* MP1 : Use MP3 for decoding */ /* MP2 : Use MP3 for decoding */ /* Theora: Use libtheora based theoradec */ - if (!strcmp (in_plugin->name, "gif") || - !strcmp (in_plugin->name, "vorbis") || - !strcmp (in_plugin->name, "theora") || - !strcmp (in_plugin->name, "mpeg1video") || + if (!strcmp (in_plugin->name, "vorbis") || !strcmp (in_plugin->name, "wavpack") || !strcmp (in_plugin->name, "mp1") || !strcmp (in_plugin->name, "mp2") || @@ -3228,25 +1435,13 @@ gst_ffmpegdec_register (GstPlugin * plugin) * msmpeg4v3 same, as it outperforms divxdec for divx3 playback. * VC1/WMV3 are not working and thus unpreferred for now. */ switch (in_plugin->id) { - case CODEC_ID_MPEG4: - case CODEC_ID_MSMPEG4V3: - case CODEC_ID_H264: case CODEC_ID_RA_144: case CODEC_ID_RA_288: - case CODEC_ID_RV10: - case CODEC_ID_RV20: - case CODEC_ID_RV30: - case CODEC_ID_RV40: case CODEC_ID_COOK: rank = GST_RANK_PRIMARY; break; - /* DVVIDEO: we have a good dv decoder, fast on both ppc as well as x86. - * They say libdv's quality is better though. leave as secondary. - * note: if you change this, see the code in gstdv.c in good/ext/dv. - * - * SIPR: decoder should have a higher rank than realaudiodec. + /* SIPR: decoder should have a higher rank than realaudiodec. */ - case CODEC_ID_DVVIDEO: case CODEC_ID_SIPR: rank = GST_RANK_SECONDARY; break; diff --git a/ext/ffmpeg/gstffmpegenc.c b/ext/ffmpeg/gstffmpegenc.c index c2b52c9ca3..70674e9112 100644 --- a/ext/ffmpeg/gstffmpegenc.c +++ b/ext/ffmpeg/gstffmpegenc.c @@ -40,18 +40,9 @@ #include "gstffmpegcodecmap.h" #include "gstffmpegutils.h" #include "gstffmpegenc.h" -#include "gstffmpegcfg.h" -#define DEFAULT_VIDEO_BITRATE 300000 /* in bps */ -#define DEFAULT_VIDEO_GOP_SIZE 15 #define DEFAULT_AUDIO_BITRATE 128000 -#define DEFAULT_WIDTH 352 -#define DEFAULT_HEIGHT 288 - - -#define VIDEO_BUFFER_SIZE (1024*1024) - enum { /* FILL ME */ @@ -62,76 +53,49 @@ enum { ARG_0, ARG_BIT_RATE, - ARG_GOP_SIZE, - ARG_ME_METHOD, ARG_BUFSIZE, ARG_RTP_PAYLOAD_SIZE, - ARG_CFG_BASE }; -#define GST_TYPE_ME_METHOD (gst_ffmpegenc_me_method_get_type()) -static GType -gst_ffmpegenc_me_method_get_type (void) -{ - static GType ffmpegenc_me_method_type = 0; - static GEnumValue ffmpegenc_me_methods[] = { - {ME_ZERO, "None (Very low quality)", "zero"}, - {ME_FULL, "Full (Slow, unmaintained)", "full"}, - {ME_LOG, "Logarithmic (Low quality, unmaintained)", "logarithmic"}, - {ME_PHODS, "phods (Low quality, unmaintained)", "phods"}, - {ME_EPZS, "EPZS (Best quality, Fast)", "epzs"}, - {ME_X1, "X1 (Experimental)", "x1"}, - {0, NULL, NULL}, - }; - if (!ffmpegenc_me_method_type) { - ffmpegenc_me_method_type = - g_enum_register_static ("GstLibAVEncMeMethod", ffmpegenc_me_methods); - } - return ffmpegenc_me_method_type; -} - /* A number of function prototypes are given so we can refer to them later. */ -static void gst_ffmpegenc_class_init (GstFFMpegEncClass * klass); -static void gst_ffmpegenc_base_init (GstFFMpegEncClass * klass); -static void gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc); -static void gst_ffmpegenc_finalize (GObject * object); +static void gst_ffmpegaudenc_class_init (GstFFMpegAudEncClass * klass); +static void gst_ffmpegaudenc_base_init (GstFFMpegAudEncClass * klass); +static void gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc); +static void gst_ffmpegaudenc_finalize (GObject * object); -static gboolean gst_ffmpegenc_setcaps (GstFFMpegEnc * ffmpegenc, +static gboolean gst_ffmpegaudenc_setcaps (GstFFMpegAudEnc * ffmpegenc, GstCaps * caps); -static GstCaps *gst_ffmpegenc_getcaps (GstPad * pad, GstCaps * filter); -static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad, +static GstCaps *gst_ffmpegaudenc_getcaps (GstFFMpegAudEnc * ffmpegenc, + GstCaps * filter); +static GstFlowReturn gst_ffmpegaudenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * buffer); -static GstFlowReturn gst_ffmpegenc_chain_audio (GstPad * pad, - GstObject * parent, GstBuffer * buffer); -static gboolean gst_ffmpegenc_event_sink (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_ffmpegenc_event_src (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_ffmpegenc_query_sink (GstPad * pad, GstObject * parent, +static gboolean gst_ffmpegaudenc_query_sink (GstPad * pad, GstObject * parent, GstQuery * query); +static gboolean gst_ffmpegaudenc_event_sink (GstPad * pad, GstObject * parent, + GstEvent * event); -static void gst_ffmpegenc_set_property (GObject * object, +static void gst_ffmpegaudenc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_ffmpegenc_get_property (GObject * object, +static void gst_ffmpegaudenc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_ffmpegenc_change_state (GstElement * element, +static GstStateChangeReturn gst_ffmpegaudenc_change_state (GstElement * element, GstStateChange transition); #define GST_FFENC_PARAMS_QDATA g_quark_from_static_string("avenc-params") static GstElementClass *parent_class = NULL; -/*static guint gst_ffmpegenc_signals[LAST_SIGNAL] = { 0 }; */ +/*static guint gst_ffmpegaudenc_signals[LAST_SIGNAL] = { 0 }; */ static void -gst_ffmpegenc_base_init (GstFFMpegEncClass * klass) +gst_ffmpegaudenc_base_init (GstFFMpegAudEncClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); AVCodec *in_plugin; GstPadTemplate *srctempl = NULL, *sinktempl = NULL; GstCaps *srccaps = NULL, *sinkcaps = NULL; - gchar *longname, *classification, *description; + gchar *longname, *description; in_plugin = (AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), @@ -140,15 +104,12 @@ gst_ffmpegenc_base_init (GstFFMpegEncClass * klass) /* construct the element details struct */ longname = g_strdup_printf ("libav %s encoder", in_plugin->long_name); - classification = g_strdup_printf ("Codec/Encoder/%s", - (in_plugin->type == AVMEDIA_TYPE_VIDEO) ? "Video" : "Audio"); description = g_strdup_printf ("libav %s encoder", in_plugin->name); gst_element_class_set_metadata (element_class, longname, - classification, description, + "Codec/Encoder/Audio", description, "Wim Taymans , " "Ronald Bultje "); g_free (longname); - g_free (classification); g_free (description); if (!(srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE))) { @@ -156,12 +117,8 @@ gst_ffmpegenc_base_init (GstFFMpegEncClass * klass) srccaps = gst_caps_new_empty_simple ("unknown/unknown"); } - if (in_plugin->type == AVMEDIA_TYPE_VIDEO) { - sinkcaps = gst_caps_from_string ("video/x-raw"); - } else { - sinkcaps = gst_ffmpeg_codectype_to_audio_caps (NULL, - in_plugin->id, TRUE, in_plugin); - } + sinkcaps = gst_ffmpeg_codectype_to_audio_caps (NULL, + in_plugin->id, TRUE, in_plugin); if (!sinkcaps) { GST_DEBUG ("Couldn't get sink caps for encoder '%s'", in_plugin->name); sinkcaps = gst_caps_new_empty_simple ("unknown/unknown"); @@ -184,7 +141,7 @@ gst_ffmpegenc_base_init (GstFFMpegEncClass * klass) } static void -gst_ffmpegenc_class_init (GstFFMpegEncClass * klass) +gst_ffmpegaudenc_class_init (GstFFMpegAudEncClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; @@ -194,534 +151,179 @@ gst_ffmpegenc_class_init (GstFFMpegEncClass * klass) parent_class = g_type_class_peek_parent (klass); - gobject_class->set_property = gst_ffmpegenc_set_property; - gobject_class->get_property = gst_ffmpegenc_get_property; + gobject_class->set_property = gst_ffmpegaudenc_set_property; + gobject_class->get_property = gst_ffmpegaudenc_get_property; - if (klass->in_plugin->type == AVMEDIA_TYPE_VIDEO) { - /* FIXME: could use -1 for a sensible per-codec default based on - * e.g. input resolution and framerate */ - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE, - g_param_spec_int ("bitrate", "Bit Rate", - "Target Video Bitrate", 0, G_MAXINT, DEFAULT_VIDEO_BITRATE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GOP_SIZE, - g_param_spec_int ("gop-size", "GOP Size", - "Number of frames within one GOP", 0, G_MAXINT, - DEFAULT_VIDEO_GOP_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ME_METHOD, - g_param_spec_enum ("me-method", "ME Method", "Motion Estimation Method", - GST_TYPE_ME_METHOD, ME_EPZS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* FIXME: could use -1 for a sensible per-codec defaults */ + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE, + g_param_spec_int ("bitrate", "Bit Rate", + "Target Audio Bitrate", 0, G_MAXINT, DEFAULT_AUDIO_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFSIZE, - g_param_spec_int ("buffer-size", "Buffer Size", - "Size of the video buffers (read-only)", 0, G_MAXINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), - ARG_RTP_PAYLOAD_SIZE, g_param_spec_int ("rtp-payload-size", - "RTP Payload Size", "Target GOB length", 0, G_MAXINT, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = gst_ffmpegaudenc_change_state; - /* register additional properties, possibly dependent on the exact CODEC */ - gst_ffmpeg_cfg_install_property (klass, ARG_CFG_BASE); - } else if (klass->in_plugin->type == AVMEDIA_TYPE_AUDIO) { - /* FIXME: could use -1 for a sensible per-codec defaults */ - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE, - g_param_spec_int ("bitrate", "Bit Rate", - "Target Audio Bitrate", 0, G_MAXINT, DEFAULT_AUDIO_BITRATE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - } - - gstelement_class->change_state = gst_ffmpegenc_change_state; - - gobject_class->finalize = gst_ffmpegenc_finalize; + gobject_class->finalize = gst_ffmpegaudenc_finalize; } static void -gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc) +gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc) { - GstFFMpegEncClass *oclass = - (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); + GstFFMpegAudEncClass *oclass = + (GstFFMpegAudEncClass *) (G_OBJECT_GET_CLASS (ffmpegaudenc)); /* setup pads */ - ffmpegenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink"); - gst_pad_set_query_function (ffmpegenc->sinkpad, gst_ffmpegenc_query_sink); - ffmpegenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src"); - gst_pad_use_fixed_caps (ffmpegenc->srcpad); + ffmpegaudenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink"); + gst_pad_set_event_function (ffmpegaudenc->sinkpad, + gst_ffmpegaudenc_event_sink); + gst_pad_set_query_function (ffmpegaudenc->sinkpad, + gst_ffmpegaudenc_query_sink); + gst_pad_set_chain_function (ffmpegaudenc->sinkpad, + gst_ffmpegaudenc_chain_audio); + + ffmpegaudenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src"); + gst_pad_use_fixed_caps (ffmpegaudenc->srcpad); /* ffmpeg objects */ - ffmpegenc->context = avcodec_alloc_context (); - ffmpegenc->picture = avcodec_alloc_frame (); - ffmpegenc->opened = FALSE; + ffmpegaudenc->context = avcodec_alloc_context (); + ffmpegaudenc->opened = FALSE; - ffmpegenc->file = NULL; - ffmpegenc->delay = g_queue_new (); + gst_element_add_pad (GST_ELEMENT (ffmpegaudenc), ffmpegaudenc->sinkpad); + gst_element_add_pad (GST_ELEMENT (ffmpegaudenc), ffmpegaudenc->srcpad); - gst_pad_set_event_function (ffmpegenc->sinkpad, gst_ffmpegenc_event_sink); - - if (oclass->in_plugin->type == AVMEDIA_TYPE_VIDEO) { - gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_video); - /* so we know when to flush the buffers on EOS */ - gst_pad_set_event_function (ffmpegenc->srcpad, gst_ffmpegenc_event_src); - - ffmpegenc->bitrate = DEFAULT_VIDEO_BITRATE; - ffmpegenc->me_method = ME_EPZS; - ffmpegenc->buffer_size = 512 * 1024; - ffmpegenc->gop_size = DEFAULT_VIDEO_GOP_SIZE; - ffmpegenc->rtp_payload_size = 0; - - ffmpegenc->lmin = 2; - ffmpegenc->lmax = 31; - ffmpegenc->max_key_interval = 0; - - gst_ffmpeg_cfg_set_defaults (ffmpegenc); - } else if (oclass->in_plugin->type == AVMEDIA_TYPE_AUDIO) { - gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_audio); - - ffmpegenc->bitrate = DEFAULT_AUDIO_BITRATE; - } - - gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->sinkpad); - gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->srcpad); - - ffmpegenc->adapter = gst_adapter_new (); + ffmpegaudenc->adapter = gst_adapter_new (); } static void -gst_ffmpegenc_finalize (GObject * object) +gst_ffmpegaudenc_finalize (GObject * object) { - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) object; + GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) object; - gst_ffmpeg_cfg_finalize (ffmpegenc); /* close old session */ - if (ffmpegenc->opened) { - gst_ffmpeg_avcodec_close (ffmpegenc->context); - ffmpegenc->opened = FALSE; + if (ffmpegaudenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); + ffmpegaudenc->opened = FALSE; } /* clean up remaining allocated data */ - av_free (ffmpegenc->context); - av_free (ffmpegenc->picture); + av_free (ffmpegaudenc->context); - g_queue_free (ffmpegenc->delay); - g_free (ffmpegenc->filename); - - g_object_unref (ffmpegenc->adapter); + g_object_unref (ffmpegaudenc->adapter); G_OBJECT_CLASS (parent_class)->finalize (object); } static GstCaps * -gst_ffmpegenc_get_possible_sizes (GstFFMpegEnc * ffmpegenc, GstPad * pad, - GstCaps * caps) +gst_ffmpegaudenc_getcaps (GstFFMpegAudEnc * ffmpegaudenc, GstCaps * filter) { - GstCaps *templ, *othercaps = NULL; - GstCaps *tmpcaps = NULL; - GstCaps *intersect = NULL; - guint i; + GstCaps *caps = NULL; - othercaps = gst_pad_peer_query_caps (ffmpegenc->srcpad, NULL); - - if (!othercaps) - return gst_caps_ref (caps); - - templ = gst_pad_get_pad_template_caps (ffmpegenc->srcpad); - intersect = gst_caps_intersect (othercaps, templ); - gst_caps_unref (othercaps); - gst_caps_unref (templ); - - if (gst_caps_is_empty (intersect)) - return intersect; - - if (gst_caps_is_any (intersect)) - return gst_caps_ref (caps); - - tmpcaps = gst_caps_new_empty (); - - for (i = 0; i < gst_caps_get_size (intersect); i++) { - GstStructure *s = gst_caps_get_structure (intersect, i); - const GValue *height = NULL; - const GValue *width = NULL; - const GValue *framerate = NULL; - GstStructure *tmps; - - height = gst_structure_get_value (s, "height"); - width = gst_structure_get_value (s, "width"); - framerate = gst_structure_get_value (s, "framerate"); - - tmps = gst_structure_new_empty ("video/x-raw"); - if (width) - gst_structure_set_value (tmps, "width", width); - if (height) - gst_structure_set_value (tmps, "height", height); - if (framerate) - gst_structure_set_value (tmps, "framerate", framerate); - - tmpcaps = gst_caps_merge_structure (tmpcaps, tmps); - } - gst_caps_unref (intersect); - - intersect = gst_caps_intersect (caps, tmpcaps); - gst_caps_unref (tmpcaps); - - return intersect; -} - - -static GstCaps * -gst_ffmpegenc_getcaps (GstPad * pad, GstCaps * filter) -{ - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) GST_PAD_PARENT (pad); - GstFFMpegEncClass *oclass = - (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); - AVCodecContext *ctx = NULL; - enum PixelFormat pixfmt; - GstCaps *templ, *caps = NULL; - GstCaps *finalcaps = NULL; - gint i; - - GST_DEBUG_OBJECT (ffmpegenc, "getting caps, filter %" GST_PTR_FORMAT, filter); + GST_DEBUG_OBJECT (ffmpegaudenc, "getting caps"); /* audio needs no special care */ - if (oclass->in_plugin->type == AVMEDIA_TYPE_AUDIO) { - templ = gst_pad_get_pad_template_caps (pad); - if (filter) { - caps = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (templ); - } else - caps = templ; - - GST_DEBUG_OBJECT (ffmpegenc, "audio caps, return intersected template %" - GST_PTR_FORMAT, caps); - - return caps; - } - - /* cached */ - if (oclass->sinkcaps) { - caps = gst_ffmpegenc_get_possible_sizes (ffmpegenc, pad, oclass->sinkcaps); - if (filter) { - finalcaps = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - } else { - finalcaps = caps; - } - GST_DEBUG_OBJECT (ffmpegenc, - "return intersected cached caps %" GST_PTR_FORMAT, finalcaps); - return finalcaps; - } - - /* create cache etc. */ - - /* shut up the logging while we autoprobe; we don't want warnings and - * errors about unsupported formats */ - /* FIXME: if someone cares about this disabling the logging for other - * instances/threads/..., one could investigate if there is a way to - * set this as a struct member on the av context, and check it from the - * log handler */ -#ifndef GST_DISABLE_GST_DEBUG - _shut_up_I_am_probing = TRUE; -#endif - GST_DEBUG_OBJECT (ffmpegenc, "probing caps"); - i = pixfmt = 0; - /* check pixfmt until deemed finished */ - for (pixfmt = 0;; pixfmt++) { - GstCaps *tmpcaps; - - /* override looping all pixfmt if codec declares pixfmts; - * these may not properly check and report supported pixfmt during _init */ - if (oclass->in_plugin->pix_fmts) { - if ((pixfmt = oclass->in_plugin->pix_fmts[i++]) == PIX_FMT_NONE) { - GST_DEBUG_OBJECT (ffmpegenc, - "At the end of official pixfmt for this codec, breaking out"); - break; - } - GST_DEBUG_OBJECT (ffmpegenc, - "Got an official pixfmt [%d], attempting to get caps", pixfmt); - tmpcaps = gst_ffmpeg_pixfmt_to_caps (pixfmt, NULL, oclass->in_plugin->id); - if (tmpcaps) { - GST_DEBUG_OBJECT (ffmpegenc, "Got caps, breaking out"); - if (!caps) - caps = gst_caps_new_empty (); - gst_caps_append (caps, tmpcaps); - continue; - } - GST_DEBUG_OBJECT (ffmpegenc, - "Couldn't figure out caps without context, trying again with a context"); - } - - GST_DEBUG_OBJECT (ffmpegenc, "pixfmt :%d", pixfmt); - if (pixfmt >= PIX_FMT_NB) { - GST_WARNING ("Invalid pixfmt, breaking out"); - break; - } - - /* need to start with a fresh codec_context each time around, since - * codec_close may have released stuff causing the next pass to segfault */ - ctx = avcodec_alloc_context (); - if (!ctx) { - GST_DEBUG_OBJECT (ffmpegenc, "no context"); - break; - } - - /* set some default properties */ - ctx->width = DEFAULT_WIDTH; - ctx->height = DEFAULT_HEIGHT; - ctx->time_base.num = 1; - ctx->time_base.den = 25; - ctx->ticks_per_frame = 1; - ctx->bit_rate = DEFAULT_VIDEO_BITRATE; - /* makes it silent */ - ctx->strict_std_compliance = -1; - - ctx->pix_fmt = pixfmt; - - GST_DEBUG ("Attempting to open codec"); - if (gst_ffmpeg_avcodec_open (ctx, oclass->in_plugin) >= 0 && - ctx->pix_fmt == pixfmt) { - ctx->width = -1; - if (!caps) - caps = gst_caps_new_empty (); - tmpcaps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type, ctx, - oclass->in_plugin->id, TRUE); - if (tmpcaps) - gst_caps_append (caps, tmpcaps); - else - GST_LOG_OBJECT (ffmpegenc, - "Couldn't get caps for oclass->in_plugin->name:%s", - oclass->in_plugin->name); - gst_ffmpeg_avcodec_close (ctx); - } else { - GST_DEBUG_OBJECT (ffmpegenc, "Opening codec failed with pixfmt : %d", - pixfmt); - } - if (ctx->priv_data) - gst_ffmpeg_avcodec_close (ctx); - av_free (ctx); - } -#ifndef GST_DISABLE_GST_DEBUG - _shut_up_I_am_probing = FALSE; -#endif - - /* make sure we have something */ - if (!caps) { - templ = gst_pad_get_pad_template_caps (pad); - caps = gst_ffmpegenc_get_possible_sizes (ffmpegenc, pad, templ); - gst_caps_unref (templ); - if (filter) { - finalcaps = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - } else { - finalcaps = caps; - } - GST_DEBUG_OBJECT (ffmpegenc, "probing gave nothing, " - "return intersected template %" GST_PTR_FORMAT, finalcaps); - return finalcaps; - } - - GST_DEBUG_OBJECT (ffmpegenc, "probed caps gave %" GST_PTR_FORMAT, caps); - oclass->sinkcaps = caps; - - finalcaps = - gst_ffmpegenc_get_possible_sizes (ffmpegenc, pad, oclass->sinkcaps); + caps = gst_pad_get_pad_template_caps (ffmpegaudenc->sinkpad); if (filter) { - caps = finalcaps; - finalcaps = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + GstCaps *tmp; + tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (caps); + caps = tmp; } - return finalcaps; + GST_DEBUG_OBJECT (ffmpegaudenc, + "audio caps, return template %" GST_PTR_FORMAT, caps); + + return caps; } static gboolean -gst_ffmpegenc_setcaps (GstFFMpegEnc * ffmpegenc, GstCaps * caps) +gst_ffmpegaudenc_setcaps (GstFFMpegAudEnc * ffmpegaudenc, GstCaps * caps) { GstCaps *other_caps; GstCaps *allowed_caps; GstCaps *icaps; - enum PixelFormat pix_fmt; - GstFFMpegEncClass *oclass = - (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); + GstFFMpegAudEncClass *oclass = + (GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc); /* close old session */ - if (ffmpegenc->opened) { - gst_ffmpeg_avcodec_close (ffmpegenc->context); - ffmpegenc->opened = FALSE; + if (ffmpegaudenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); + ffmpegaudenc->opened = FALSE; + /* fixed src caps; + * so clear src caps for proper (re-)negotiation */ + gst_pad_set_caps (ffmpegaudenc->srcpad, NULL); } /* set defaults */ - avcodec_get_context_defaults (ffmpegenc->context); + avcodec_get_context_defaults (ffmpegaudenc->context); /* if we set it in _getcaps we should set it also in _link */ - ffmpegenc->context->strict_std_compliance = -1; + ffmpegaudenc->context->strict_std_compliance = -1; /* user defined properties */ - ffmpegenc->context->bit_rate = ffmpegenc->bitrate; - ffmpegenc->context->bit_rate_tolerance = ffmpegenc->bitrate; - ffmpegenc->context->gop_size = ffmpegenc->gop_size; - ffmpegenc->context->me_method = ffmpegenc->me_method; - GST_DEBUG_OBJECT (ffmpegenc, "Setting avcontext to bitrate %lu, gop_size %d", - ffmpegenc->bitrate, ffmpegenc->gop_size); + ffmpegaudenc->context->bit_rate = ffmpegaudenc->bitrate; + ffmpegaudenc->context->bit_rate_tolerance = ffmpegaudenc->bitrate; + GST_DEBUG_OBJECT (ffmpegaudenc, "Setting avcontext to bitrate %lu", + ffmpegaudenc->bitrate); /* RTP payload used for GOB production (for Asterisk) */ - if (ffmpegenc->rtp_payload_size) { - ffmpegenc->context->rtp_payload_size = ffmpegenc->rtp_payload_size; - } - - /* additional avcodec settings */ - /* first fill in the majority by copying over */ - gst_ffmpeg_cfg_fill_context (ffmpegenc, ffmpegenc->context); - - /* then handle some special cases */ - ffmpegenc->context->lmin = (ffmpegenc->lmin * FF_QP2LAMBDA + 0.5); - ffmpegenc->context->lmax = (ffmpegenc->lmax * FF_QP2LAMBDA + 0.5); - - if (ffmpegenc->interlaced) { - ffmpegenc->context->flags |= - CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME; - ffmpegenc->picture->interlaced_frame = TRUE; - /* if this is not the case, a filter element should be used to swap fields */ - ffmpegenc->picture->top_field_first = TRUE; + if (ffmpegaudenc->rtp_payload_size) { + ffmpegaudenc->context->rtp_payload_size = ffmpegaudenc->rtp_payload_size; } /* some other defaults */ - ffmpegenc->context->rc_strategy = 2; - ffmpegenc->context->b_frame_strategy = 0; - ffmpegenc->context->coder_type = 0; - ffmpegenc->context->context_model = 0; - ffmpegenc->context->scenechange_threshold = 0; - ffmpegenc->context->inter_threshold = 0; + ffmpegaudenc->context->rc_strategy = 2; + ffmpegaudenc->context->b_frame_strategy = 0; + ffmpegaudenc->context->coder_type = 0; + ffmpegaudenc->context->context_model = 0; + ffmpegaudenc->context->scenechange_threshold = 0; + ffmpegaudenc->context->inter_threshold = 0; - /* and last but not least the pass; CBR, 2-pass, etc */ - ffmpegenc->context->flags |= ffmpegenc->pass; - switch (ffmpegenc->pass) { - /* some additional action depends on type of pass */ - case CODEC_FLAG_QSCALE: - ffmpegenc->context->global_quality - = ffmpegenc->picture->quality = FF_QP2LAMBDA * ffmpegenc->quantizer; - break; - case CODEC_FLAG_PASS1: /* need to prepare a stats file */ - /* we don't close when changing caps, fingers crossed */ - if (!ffmpegenc->file) - ffmpegenc->file = g_fopen (ffmpegenc->filename, "w"); - if (!ffmpegenc->file) { - GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, OPEN_WRITE, - (("Could not open file \"%s\" for writing."), ffmpegenc->filename), - GST_ERROR_SYSTEM); - return FALSE; - } - break; - case CODEC_FLAG_PASS2: - { /* need to read the whole stats file ! */ - gsize size; - - if (!g_file_get_contents (ffmpegenc->filename, - &ffmpegenc->context->stats_in, &size, NULL)) { - GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, READ, - (("Could not get contents of file \"%s\"."), ffmpegenc->filename), - GST_ERROR_SYSTEM); - return FALSE; - } - - break; - } - default: - break; - } /* fetch pix_fmt and so on */ gst_ffmpeg_caps_with_codectype (oclass->in_plugin->type, - caps, ffmpegenc->context); - if (!ffmpegenc->context->time_base.den) { - ffmpegenc->context->time_base.den = 25; - ffmpegenc->context->time_base.num = 1; - ffmpegenc->context->ticks_per_frame = 1; - } else if ((oclass->in_plugin->id == CODEC_ID_MPEG4) - && (ffmpegenc->context->time_base.den > 65535)) { - /* MPEG4 Standards do not support time_base denominator greater than - * (1<<16) - 1 . We therefore scale them down. - * Agreed, it will not be the exact framerate... but the difference - * shouldn't be that noticeable */ - ffmpegenc->context->time_base.num = - (gint) gst_util_uint64_scale_int (ffmpegenc->context->time_base.num, - 65535, ffmpegenc->context->time_base.den); - ffmpegenc->context->time_base.den = 65535; - GST_LOG_OBJECT (ffmpegenc, "MPEG4 : scaled down framerate to %d / %d", - ffmpegenc->context->time_base.den, ffmpegenc->context->time_base.num); - } - - pix_fmt = ffmpegenc->context->pix_fmt; - - /* max-key-interval may need the framerate set above */ - if (ffmpegenc->max_key_interval) { - AVCodecContext *ctx; - - /* override gop-size */ - ctx = ffmpegenc->context; - ctx->gop_size = (ffmpegenc->max_key_interval < 0) ? - (-ffmpegenc->max_key_interval - * (ctx->time_base.den * ctx->ticks_per_frame / ctx->time_base.num)) - : ffmpegenc->max_key_interval; + caps, ffmpegaudenc->context); + if (!ffmpegaudenc->context->time_base.den) { + ffmpegaudenc->context->time_base.den = 25; + ffmpegaudenc->context->time_base.num = 1; + ffmpegaudenc->context->ticks_per_frame = 1; } /* open codec */ - if (gst_ffmpeg_avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) { - if (ffmpegenc->context->priv_data) - gst_ffmpeg_avcodec_close (ffmpegenc->context); - if (ffmpegenc->context->stats_in) - g_free (ffmpegenc->context->stats_in); - GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to open libav codec", + if (gst_ffmpeg_avcodec_open (ffmpegaudenc->context, oclass->in_plugin) < 0) { + if (ffmpegaudenc->context->priv_data) + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); + if (ffmpegaudenc->context->stats_in) + g_free (ffmpegaudenc->context->stats_in); + GST_DEBUG_OBJECT (ffmpegaudenc, "avenc_%s: Failed to open FFMPEG codec", oclass->in_plugin->name); return FALSE; } /* second pass stats buffer no longer needed */ - if (ffmpegenc->context->stats_in) - g_free (ffmpegenc->context->stats_in); - - /* is the colourspace correct? */ - if (pix_fmt != ffmpegenc->context->pix_fmt) { - gst_ffmpeg_avcodec_close (ffmpegenc->context); - GST_DEBUG_OBJECT (ffmpegenc, - "avenc_%s: AV wants different colourspace (%d given, %d wanted)", - oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt); - return FALSE; - } - /* we may have failed mapping caps to a pixfmt, - * and quite some codecs do not make up their own mind about that - * in any case, _NONE can never work out later on */ - if (oclass->in_plugin->type == AVMEDIA_TYPE_VIDEO && pix_fmt == PIX_FMT_NONE) { - GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to determine input format", - oclass->in_plugin->name); - return FALSE; - } + if (ffmpegaudenc->context->stats_in) + g_free (ffmpegaudenc->context->stats_in); /* some codecs support more than one format, first auto-choose one */ - GST_DEBUG_OBJECT (ffmpegenc, "picking an output format ..."); - allowed_caps = gst_pad_get_allowed_caps (ffmpegenc->srcpad); + GST_DEBUG_OBJECT (ffmpegaudenc, "picking an output format ..."); + allowed_caps = gst_pad_get_allowed_caps (ffmpegaudenc->srcpad); if (!allowed_caps) { - GST_DEBUG_OBJECT (ffmpegenc, "... but no peer, using template caps"); + GST_DEBUG_OBJECT (ffmpegaudenc, "... but no peer, using template caps"); /* we need to copy because get_allowed_caps returns a ref, and * get_pad_template_caps doesn't */ - allowed_caps = gst_pad_get_pad_template_caps (ffmpegenc->srcpad); + allowed_caps = gst_pad_get_pad_template_caps (ffmpegaudenc->srcpad); } - GST_DEBUG_OBJECT (ffmpegenc, "chose caps %" GST_PTR_FORMAT, allowed_caps); + GST_DEBUG_OBJECT (ffmpegaudenc, "chose caps %" GST_PTR_FORMAT, allowed_caps); gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id, - oclass->in_plugin->type, allowed_caps, ffmpegenc->context); + oclass->in_plugin->type, allowed_caps, ffmpegaudenc->context); /* try to set this caps on the other side */ other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id, - ffmpegenc->context, TRUE); + ffmpegaudenc->context, TRUE); if (!other_caps) { gst_caps_unref (allowed_caps); - gst_ffmpeg_avcodec_close (ffmpegenc->context); + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); GST_DEBUG ("Unsupported codec - no caps found"); return FALSE; } @@ -744,146 +346,23 @@ gst_ffmpegenc_setcaps (GstFFMpegEnc * ffmpegenc, GstCaps * caps) icaps = newcaps; } - if (!gst_pad_set_caps (ffmpegenc->srcpad, icaps)) { - gst_ffmpeg_avcodec_close (ffmpegenc->context); + if (!gst_pad_set_caps (ffmpegaudenc->srcpad, icaps)) { + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); gst_caps_unref (icaps); return FALSE; } gst_caps_unref (icaps); /* success! */ - ffmpegenc->opened = TRUE; + ffmpegaudenc->opened = TRUE; return TRUE; } -static void -ffmpegenc_setup_working_buf (GstFFMpegEnc * ffmpegenc) -{ - guint wanted_size = - ffmpegenc->context->width * ffmpegenc->context->height * 6 + - FF_MIN_BUFFER_SIZE; - - /* Above is the buffer size used by ffmpeg/ffmpeg.c */ - - if (ffmpegenc->working_buf == NULL || - ffmpegenc->working_buf_size != wanted_size) { - if (ffmpegenc->working_buf) - g_free (ffmpegenc->working_buf); - ffmpegenc->working_buf_size = wanted_size; - ffmpegenc->working_buf = g_malloc (ffmpegenc->working_buf_size); - } - ffmpegenc->buffer_size = wanted_size; -} static GstFlowReturn -gst_ffmpegenc_chain_video (GstPad * pad, GstObject * parent, GstBuffer * inbuf) -{ - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) parent; - GstBuffer *outbuf; - GstMapInfo map; - gint ret_size = 0, frame_size; - gboolean force_keyframe; - - if (G_UNLIKELY (!ffmpegenc->opened)) - goto not_negotiated; - - GST_DEBUG_OBJECT (ffmpegenc, - "Received buffer of time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf))); - - GST_OBJECT_LOCK (ffmpegenc); - force_keyframe = ffmpegenc->force_keyframe; - ffmpegenc->force_keyframe = FALSE; - GST_OBJECT_UNLOCK (ffmpegenc); - - if (force_keyframe) - ffmpegenc->picture->pict_type = FF_I_TYPE; - - gst_buffer_map (inbuf, &map, GST_MAP_READ); - frame_size = gst_ffmpeg_avpicture_fill ((AVPicture *) ffmpegenc->picture, - map.data, - ffmpegenc->context->pix_fmt, - ffmpegenc->context->width, ffmpegenc->context->height); - g_return_val_if_fail (frame_size == map.size, GST_FLOW_ERROR); - ffmpegenc->picture->pts = - gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (inbuf) / - ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); - - ffmpegenc_setup_working_buf (ffmpegenc); - - ret_size = avcodec_encode_video (ffmpegenc->context, - ffmpegenc->working_buf, ffmpegenc->working_buf_size, ffmpegenc->picture); - - gst_buffer_unmap (inbuf, &map); - - if (ret_size < 0) { -#ifndef GST_DISABLE_GST_DEBUG - GstFFMpegEncClass *oclass = - (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); - GST_ERROR_OBJECT (ffmpegenc, - "avenc_%s: failed to encode buffer", oclass->in_plugin->name); -#endif /* GST_DISABLE_GST_DEBUG */ - gst_buffer_unref (inbuf); - return GST_FLOW_OK; - } - - /* handle b-frame delay when no output, so we don't output empty frames; - * timestamps and so can permute a bit between coding and display order - * but keyframes should still end up with the proper metadata */ - g_queue_push_tail (ffmpegenc->delay, inbuf); - if (ret_size) - inbuf = g_queue_pop_head (ffmpegenc->delay); - else - return GST_FLOW_OK; - - /* save stats info if there is some as well as a stats file */ - if (ffmpegenc->file && ffmpegenc->context->stats_out) - if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) - GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, - (("Could not write to file \"%s\"."), ffmpegenc->filename), - GST_ERROR_SYSTEM); - - outbuf = gst_buffer_new_and_alloc (ret_size); - gst_buffer_fill (outbuf, 0, ffmpegenc->working_buf, ret_size); - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); - GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); - /* buggy codec may not set coded_frame */ - if (ffmpegenc->context->coded_frame) { - if (!ffmpegenc->context->coded_frame->key_frame) - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - } else - GST_WARNING_OBJECT (ffmpegenc, "codec did not provide keyframe info"); - - gst_buffer_unref (inbuf); - - /* Reset frame type */ - if (ffmpegenc->picture->pict_type) - ffmpegenc->picture->pict_type = 0; - - if (force_keyframe) { - gst_pad_push_event (ffmpegenc->srcpad, - gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, - gst_structure_new ("GstForceKeyUnit", - "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), - NULL))); - } - - return gst_pad_push (ffmpegenc->srcpad, outbuf); - - /* ERRORS */ -not_negotiated: - { - GST_ELEMENT_ERROR (ffmpegenc, CORE, NEGOTIATION, (NULL), - ("not configured to input format before data start")); - gst_buffer_unref (inbuf); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -static GstFlowReturn -gst_ffmpegenc_encode_audio (GstFFMpegEnc * ffmpegenc, guint8 * audio_in, - guint in_size, guint max_size, GstClockTime timestamp, +gst_ffmpegaudenc_encode_audio (GstFFMpegAudEnc * ffmpegaudenc, + guint8 * audio_in, guint in_size, guint max_size, GstClockTime timestamp, GstClockTime duration, gboolean discont) { GstBuffer *outbuf; @@ -892,25 +371,25 @@ gst_ffmpegenc_encode_audio (GstFFMpegEnc * ffmpegenc, guint8 * audio_in, gint res; GstFlowReturn ret; - ctx = ffmpegenc->context; + ctx = ffmpegaudenc->context; /* We need to provide at least ffmpegs minimal buffer size */ outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); - GST_LOG_OBJECT (ffmpegenc, "encoding buffer of max size %d", max_size); - if (ffmpegenc->buffer_size != max_size) - ffmpegenc->buffer_size = max_size; + GST_LOG_OBJECT (ffmpegaudenc, "encoding buffer of max size %d", max_size); + if (ffmpegaudenc->buffer_size != max_size) + ffmpegaudenc->buffer_size = max_size; res = avcodec_encode_audio (ctx, map.data, max_size, (short *) audio_in); if (res < 0) { gst_buffer_unmap (outbuf, &map); - GST_ERROR_OBJECT (ffmpegenc, "Failed to encode buffer: %d", res); + GST_ERROR_OBJECT (ffmpegaudenc, "Failed to encode buffer: %d", res); gst_buffer_unref (outbuf); return GST_FLOW_OK; } - GST_LOG_OBJECT (ffmpegenc, "got output size %d", res); + GST_LOG_OBJECT (ffmpegaudenc, "got output size %d", res); gst_buffer_unmap (outbuf, &map); gst_buffer_resize (outbuf, 0, res); @@ -919,19 +398,20 @@ gst_ffmpegenc_encode_audio (GstFFMpegEnc * ffmpegenc, guint8 * audio_in, if (discont) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - GST_LOG_OBJECT (ffmpegenc, "pushing size %d, timestamp %" GST_TIME_FORMAT, + GST_LOG_OBJECT (ffmpegaudenc, "pushing size %d, timestamp %" GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp)); - ret = gst_pad_push (ffmpegenc->srcpad, outbuf); + ret = gst_pad_push (ffmpegaudenc->srcpad, outbuf); return ret; } static GstFlowReturn -gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) +gst_ffmpegaudenc_chain_audio (GstPad * pad, GstObject * parent, + GstBuffer * inbuf) { - GstFFMpegEnc *ffmpegenc; - GstFFMpegEncClass *oclass; + GstFFMpegAudEnc *ffmpegaudenc; + GstFFMpegAudEncClass *oclass; AVCodecContext *ctx; GstClockTime timestamp, duration; gsize size, frame_size; @@ -941,20 +421,20 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) gboolean discont; guint8 *in_data; - ffmpegenc = (GstFFMpegEnc *) parent; - oclass = (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); + ffmpegaudenc = (GstFFMpegAudEnc *) parent; + oclass = (GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc); - if (G_UNLIKELY (!ffmpegenc->opened)) + if (G_UNLIKELY (!ffmpegaudenc->opened)) goto not_negotiated; - ctx = ffmpegenc->context; + ctx = ffmpegaudenc->context; size = gst_buffer_get_size (inbuf); timestamp = GST_BUFFER_TIMESTAMP (inbuf); duration = GST_BUFFER_DURATION (inbuf); discont = GST_BUFFER_IS_DISCONT (inbuf); - GST_DEBUG_OBJECT (ffmpegenc, + GST_DEBUG_OBJECT (ffmpegaudenc, "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", size %" G_GSIZE_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), size); @@ -967,17 +447,17 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) guint avail, frame_bytes; if (discont) { - GST_LOG_OBJECT (ffmpegenc, "DISCONT, clear adapter"); - gst_adapter_clear (ffmpegenc->adapter); - ffmpegenc->discont = TRUE; + GST_LOG_OBJECT (ffmpegaudenc, "DISCONT, clear adapter"); + gst_adapter_clear (ffmpegaudenc->adapter); + ffmpegaudenc->discont = TRUE; } - if (gst_adapter_available (ffmpegenc->adapter) == 0) { + if (gst_adapter_available (ffmpegaudenc->adapter) == 0) { /* lock on to new timestamp */ - GST_LOG_OBJECT (ffmpegenc, "taking buffer timestamp %" GST_TIME_FORMAT, + GST_LOG_OBJECT (ffmpegaudenc, "taking buffer timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); - ffmpegenc->adapter_ts = timestamp; - ffmpegenc->adapter_consumed = 0; + ffmpegaudenc->adapter_ts = timestamp; + ffmpegaudenc->adapter_consumed = 0; } else { GstClockTime upstream_time; GstClockTime consumed_time; @@ -985,18 +465,20 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) /* use timestamp at head of the adapter */ consumed_time = - gst_util_uint64_scale (ffmpegenc->adapter_consumed, GST_SECOND, + gst_util_uint64_scale (ffmpegaudenc->adapter_consumed, GST_SECOND, ctx->sample_rate); - timestamp = ffmpegenc->adapter_ts + consumed_time; - GST_LOG_OBJECT (ffmpegenc, "taking adapter timestamp %" GST_TIME_FORMAT + timestamp = ffmpegaudenc->adapter_ts + consumed_time; + GST_LOG_OBJECT (ffmpegaudenc, "taking adapter timestamp %" GST_TIME_FORMAT " and adding consumed time %" GST_TIME_FORMAT, - GST_TIME_ARGS (ffmpegenc->adapter_ts), GST_TIME_ARGS (consumed_time)); + GST_TIME_ARGS (ffmpegaudenc->adapter_ts), + GST_TIME_ARGS (consumed_time)); /* check with upstream timestamps, if too much deviation, * forego some timestamp perfection in favour of upstream syncing * (particularly in case these do not happen to come in multiple * of frame size) */ - upstream_time = gst_adapter_prev_timestamp (ffmpegenc->adapter, &bytes); + upstream_time = + gst_adapter_prev_timestamp (ffmpegaudenc->adapter, &bytes); if (GST_CLOCK_TIME_IS_VALID (upstream_time)) { GstClockTimeDiff diff; @@ -1006,32 +488,33 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) diff = upstream_time - timestamp; /* relaxed difference, rather than half a sample or so ... */ if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) { - GST_DEBUG_OBJECT (ffmpegenc, "adapter timestamp drifting, " + GST_DEBUG_OBJECT (ffmpegaudenc, "adapter timestamp drifting, " "taking upstream timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (upstream_time)); timestamp = upstream_time; /* samples corresponding to bytes */ - ffmpegenc->adapter_consumed = bytes / (osize * ctx->channels); - ffmpegenc->adapter_ts = upstream_time - - gst_util_uint64_scale (ffmpegenc->adapter_consumed, GST_SECOND, + ffmpegaudenc->adapter_consumed = bytes / (osize * ctx->channels); + ffmpegaudenc->adapter_ts = upstream_time - + gst_util_uint64_scale (ffmpegaudenc->adapter_consumed, GST_SECOND, ctx->sample_rate); - ffmpegenc->discont = TRUE; + ffmpegaudenc->discont = TRUE; } } } - GST_LOG_OBJECT (ffmpegenc, "pushing buffer in adapter"); - gst_adapter_push (ffmpegenc->adapter, inbuf); + GST_LOG_OBJECT (ffmpegaudenc, "pushing buffer in adapter"); + gst_adapter_push (ffmpegaudenc->adapter, inbuf); /* first see how many bytes we need to feed to the decoder. */ frame_bytes = frame_size * osize * ctx->channels; - avail = gst_adapter_available (ffmpegenc->adapter); + avail = gst_adapter_available (ffmpegaudenc->adapter); - GST_LOG_OBJECT (ffmpegenc, "frame_bytes %u, avail %u", frame_bytes, avail); + GST_LOG_OBJECT (ffmpegaudenc, "frame_bytes %u, avail %u", frame_bytes, + avail); /* while there is more than a frame size in the adapter, consume it */ while (avail >= frame_bytes) { - GST_LOG_OBJECT (ffmpegenc, "taking %u bytes from the adapter", + GST_LOG_OBJECT (ffmpegaudenc, "taking %u bytes from the adapter", frame_bytes); /* Note that we take frame_bytes and add frame_size. @@ -1039,25 +522,25 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) * or samplesize to divide by the samplerate */ /* take an audio buffer out of the adapter */ - in_data = (guint8 *) gst_adapter_map (ffmpegenc->adapter, frame_bytes); - ffmpegenc->adapter_consumed += frame_size; + in_data = (guint8 *) gst_adapter_map (ffmpegaudenc->adapter, frame_bytes); + ffmpegaudenc->adapter_consumed += frame_size; /* calculate timestamp and duration relative to start of adapter and to * the amount of samples we consumed */ duration = - gst_util_uint64_scale (ffmpegenc->adapter_consumed, GST_SECOND, + gst_util_uint64_scale (ffmpegaudenc->adapter_consumed, GST_SECOND, ctx->sample_rate); - duration -= (timestamp - ffmpegenc->adapter_ts); + duration -= (timestamp - ffmpegaudenc->adapter_ts); /* 4 times the input size should be big enough... */ out_size = frame_bytes * 4; ret = - gst_ffmpegenc_encode_audio (ffmpegenc, in_data, frame_bytes, out_size, - timestamp, duration, ffmpegenc->discont); + gst_ffmpegaudenc_encode_audio (ffmpegaudenc, in_data, frame_bytes, + out_size, timestamp, duration, ffmpegaudenc->discont); - gst_adapter_unmap (ffmpegenc->adapter); - gst_adapter_flush (ffmpegenc->adapter, frame_bytes); + gst_adapter_unmap (ffmpegaudenc->adapter); + gst_adapter_flush (ffmpegaudenc->adapter, frame_bytes); if (ret != GST_FLOW_OK) goto push_failed; @@ -1065,17 +548,17 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) /* advance the adapter timestamp with the duration */ timestamp += duration; - ffmpegenc->discont = FALSE; - avail = gst_adapter_available (ffmpegenc->adapter); + ffmpegaudenc->discont = FALSE; + avail = gst_adapter_available (ffmpegaudenc->adapter); } - GST_LOG_OBJECT (ffmpegenc, "%u bytes left in the adapter", avail); + GST_LOG_OBJECT (ffmpegaudenc, "%u bytes left in the adapter", avail); } else { GstMapInfo map; /* we have no frame_size, feed the encoder all the data and expect a fixed * output size */ int coded_bps = av_get_bits_per_sample (oclass->in_plugin->id); - GST_LOG_OBJECT (ffmpegenc, "coded bps %d, osize %d", coded_bps, osize); + GST_LOG_OBJECT (ffmpegaudenc, "coded bps %d, osize %d", coded_bps, osize); out_size = size / osize; if (coded_bps) @@ -1084,7 +567,7 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) gst_buffer_map (inbuf, &map, GST_MAP_READ); in_data = map.data; size = map.size; - ret = gst_ffmpegenc_encode_audio (ffmpegenc, in_data, size, out_size, + ret = gst_ffmpegaudenc_encode_audio (ffmpegaudenc, in_data, size, out_size, timestamp, duration, discont); gst_buffer_unmap (inbuf, &map); gst_buffer_unref (inbuf); @@ -1098,110 +581,32 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstObject * parent, GstBuffer * inbuf) /* ERRORS */ not_negotiated: { - GST_ELEMENT_ERROR (ffmpegenc, CORE, NEGOTIATION, (NULL), + GST_ELEMENT_ERROR (ffmpegaudenc, CORE, NEGOTIATION, (NULL), ("not configured to input format before data start")); gst_buffer_unref (inbuf); return GST_FLOW_NOT_NEGOTIATED; } push_failed: { - GST_DEBUG_OBJECT (ffmpegenc, "Failed to push buffer %d (%s)", ret, + GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to push buffer %d (%s)", ret, gst_flow_get_name (ret)); return ret; } } -static void -gst_ffmpegenc_flush_buffers (GstFFMpegEnc * ffmpegenc, gboolean send) -{ - GstBuffer *outbuf, *inbuf; - gint ret_size; - - GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send); - - /* no need to empty codec if there is none */ - if (!ffmpegenc->opened) - goto flush; - - while (!g_queue_is_empty (ffmpegenc->delay)) { - - ffmpegenc_setup_working_buf (ffmpegenc); - - ret_size = avcodec_encode_video (ffmpegenc->context, - ffmpegenc->working_buf, ffmpegenc->working_buf_size, NULL); - - if (ret_size < 0) { /* there should be something, notify and give up */ -#ifndef GST_DISABLE_GST_DEBUG - GstFFMpegEncClass *oclass = - (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); - GST_WARNING_OBJECT (ffmpegenc, - "avenc_%s: failed to flush buffer", oclass->in_plugin->name); -#endif /* GST_DISABLE_GST_DEBUG */ - break; - } - - /* save stats info if there is some as well as a stats file */ - if (ffmpegenc->file && ffmpegenc->context->stats_out) - if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) - GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, - (("Could not write to file \"%s\"."), ffmpegenc->filename), - GST_ERROR_SYSTEM); - - /* handle b-frame delay when no output, so we don't output empty frames */ - inbuf = g_queue_pop_head (ffmpegenc->delay); - - outbuf = gst_buffer_new_and_alloc (ret_size); - gst_buffer_fill (outbuf, 0, ffmpegenc->working_buf, ret_size); - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); - GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); - - if (!ffmpegenc->context->coded_frame->key_frame) - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - - gst_buffer_unref (inbuf); - - if (send) - gst_pad_push (ffmpegenc->srcpad, outbuf); - else - gst_buffer_unref (outbuf); - } - -flush: - { - /* make sure that we empty the queue, is still needed if we had to break */ - while (!g_queue_is_empty (ffmpegenc->delay)) - gst_buffer_unref (g_queue_pop_head (ffmpegenc->delay)); - } -} - static gboolean -gst_ffmpegenc_event_sink (GstPad * pad, GstObject * parent, GstEvent * event) +gst_ffmpegaudenc_event_sink (GstPad * pad, GstObject * parent, GstEvent * event) { - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) parent; + GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) parent; switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_ffmpegenc_flush_buffers (ffmpegenc, TRUE); - break; - /* no flushing if flush received, - * buffers in encoder are considered (in the) past */ - case GST_EVENT_CUSTOM_DOWNSTREAM: - { - const GstStructure *s; - - s = gst_event_get_structure (event); - if (gst_structure_has_name (s, "GstForceKeyUnit")) { - ffmpegenc->picture->pict_type = FF_I_TYPE; - } - break; - } case GST_EVENT_CAPS: { GstCaps *caps; gboolean ret; gst_event_parse_caps (event, &caps); - ret = gst_ffmpegenc_setcaps (ffmpegenc, caps); + ret = gst_ffmpegaudenc_setcaps (ffmpegaudenc, caps); gst_event_unref (event); return ret; } @@ -1209,42 +614,13 @@ gst_ffmpegenc_event_sink (GstPad * pad, GstObject * parent, GstEvent * event) break; } - return gst_pad_push_event (ffmpegenc->srcpad, event); + return gst_pad_event_default (pad, parent, event); } static gboolean -gst_ffmpegenc_event_src (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) parent; - gboolean forward = TRUE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CUSTOM_UPSTREAM:{ - const GstStructure *s; - s = gst_event_get_structure (event); - if (gst_structure_has_name (s, "GstForceKeyUnit")) { - GST_OBJECT_LOCK (ffmpegenc); - ffmpegenc->force_keyframe = TRUE; - GST_OBJECT_UNLOCK (ffmpegenc); - forward = FALSE; - gst_event_unref (event); - } - break; - } - - default: - break; - } - - if (forward) - return gst_pad_push_event (ffmpegenc->sinkpad, event); - else - return TRUE; -} - -static gboolean -gst_ffmpegenc_query_sink (GstPad * pad, GstObject * parent, GstQuery * query) +gst_ffmpegaudenc_query_sink (GstPad * pad, GstObject * parent, GstQuery * query) { + GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) parent; gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { @@ -1253,7 +629,7 @@ gst_ffmpegenc_query_sink (GstPad * pad, GstObject * parent, GstQuery * query) GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); - caps = gst_ffmpegenc_getcaps (pad, filter); + caps = gst_ffmpegaudenc_getcaps (ffmpegaudenc, filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; @@ -1268,16 +644,16 @@ gst_ffmpegenc_query_sink (GstPad * pad, GstObject * parent, GstQuery * query) } static void -gst_ffmpegenc_set_property (GObject * object, +gst_ffmpegaudenc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstFFMpegEnc *ffmpegenc; + GstFFMpegAudEnc *ffmpegaudenc; /* Get a pointer of the right type. */ - ffmpegenc = (GstFFMpegEnc *) (object); + ffmpegaudenc = (GstFFMpegAudEnc *) (object); - if (ffmpegenc->opened) { - GST_WARNING_OBJECT (ffmpegenc, + if (ffmpegaudenc->opened) { + GST_WARNING_OBJECT (ffmpegaudenc, "Can't change properties once decoder is setup !"); return; } @@ -1285,63 +661,50 @@ gst_ffmpegenc_set_property (GObject * object, /* Check the argument id to see which argument we're setting. */ switch (prop_id) { case ARG_BIT_RATE: - ffmpegenc->bitrate = g_value_get_int (value); - break; - case ARG_GOP_SIZE: - ffmpegenc->gop_size = g_value_get_int (value); - break; - case ARG_ME_METHOD: - ffmpegenc->me_method = g_value_get_enum (value); + ffmpegaudenc->bitrate = g_value_get_int (value); break; case ARG_BUFSIZE: break; case ARG_RTP_PAYLOAD_SIZE: - ffmpegenc->rtp_payload_size = g_value_get_int (value); + ffmpegaudenc->rtp_payload_size = g_value_get_int (value); break; default: - if (!gst_ffmpeg_cfg_set_property (object, value, pspec)) - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /* The set function is simply the inverse of the get fuction. */ static void -gst_ffmpegenc_get_property (GObject * object, +gst_ffmpegaudenc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstFFMpegEnc *ffmpegenc; + GstFFMpegAudEnc *ffmpegaudenc; /* It's not null if we got it, but it might not be ours */ - ffmpegenc = (GstFFMpegEnc *) (object); + ffmpegaudenc = (GstFFMpegAudEnc *) (object); switch (prop_id) { case ARG_BIT_RATE: - g_value_set_int (value, ffmpegenc->bitrate); + g_value_set_int (value, ffmpegaudenc->bitrate); break; - case ARG_GOP_SIZE: - g_value_set_int (value, ffmpegenc->gop_size); - break; - case ARG_ME_METHOD: - g_value_set_enum (value, ffmpegenc->me_method); break; case ARG_BUFSIZE: - g_value_set_int (value, ffmpegenc->buffer_size); + g_value_set_int (value, ffmpegaudenc->buffer_size); break; case ARG_RTP_PAYLOAD_SIZE: - g_value_set_int (value, ffmpegenc->rtp_payload_size); + g_value_set_int (value, ffmpegaudenc->rtp_payload_size); break; default: - if (!gst_ffmpeg_cfg_get_property (object, value, pspec)) - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstStateChangeReturn -gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition) +gst_ffmpegaudenc_change_state (GstElement * element, GstStateChange transition) { - GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) element; + GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) element; GstStateChangeReturn result; switch (transition) { @@ -1353,21 +716,11 @@ gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_ffmpegenc_flush_buffers (ffmpegenc, FALSE); - if (ffmpegenc->opened) { - gst_ffmpeg_avcodec_close (ffmpegenc->context); - ffmpegenc->opened = FALSE; - } - gst_adapter_clear (ffmpegenc->adapter); - - if (ffmpegenc->file) { - fclose (ffmpegenc->file); - ffmpegenc->file = NULL; - } - if (ffmpegenc->working_buf) { - g_free (ffmpegenc->working_buf); - ffmpegenc->working_buf = NULL; + if (ffmpegaudenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegaudenc->context); + ffmpegaudenc->opened = FALSE; } + gst_adapter_clear (ffmpegaudenc->adapter); break; default: break; @@ -1376,18 +729,18 @@ gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition) } gboolean -gst_ffmpegenc_register (GstPlugin * plugin) +gst_ffmpegaudenc_register (GstPlugin * plugin) { GTypeInfo typeinfo = { - sizeof (GstFFMpegEncClass), - (GBaseInitFunc) gst_ffmpegenc_base_init, + sizeof (GstFFMpegAudEncClass), + (GBaseInitFunc) gst_ffmpegaudenc_base_init, NULL, - (GClassInitFunc) gst_ffmpegenc_class_init, + (GClassInitFunc) gst_ffmpegaudenc_class_init, NULL, NULL, - sizeof (GstFFMpegEnc), + sizeof (GstFFMpegAudEnc), 0, - (GInstanceInitFunc) gst_ffmpegenc_init, + (GInstanceInitFunc) gst_ffmpegaudenc_init, }; GType type; AVCodec *in_plugin; @@ -1395,25 +748,16 @@ gst_ffmpegenc_register (GstPlugin * plugin) GST_LOG ("Registering encoders"); - /* build global ffmpeg param/property info */ - gst_ffmpeg_cfg_init (); - in_plugin = av_codec_next (NULL); while (in_plugin) { gchar *type_name; /* Skip non-AV codecs */ - if (in_plugin->type != AVMEDIA_TYPE_AUDIO && - in_plugin->type != AVMEDIA_TYPE_VIDEO) + if (in_plugin->type != AVMEDIA_TYPE_AUDIO) goto next; /* no quasi codecs, please */ - if (in_plugin->id == CODEC_ID_RAWVIDEO || - in_plugin->id == CODEC_ID_V210 || - in_plugin->id == CODEC_ID_V210X || - in_plugin->id == CODEC_ID_R210 || - in_plugin->id == CODEC_ID_ZLIB || - (in_plugin->id >= CODEC_ID_PCM_S16LE && + if ((in_plugin->id >= CODEC_ID_PCM_S16LE && in_plugin->id <= CODEC_ID_PCM_BLURAY)) { goto next; } @@ -1439,8 +783,8 @@ gst_ffmpegenc_register (GstPlugin * plugin) GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name); /* no codecs for which we're GUARANTEED to have better alternatives */ - if (!strcmp (in_plugin->name, "vorbis") || - !strcmp (in_plugin->name, "gif") || !strcmp (in_plugin->name, "flac")) { + if (!strcmp (in_plugin->name, "vorbis") + || !strcmp (in_plugin->name, "flac")) { GST_LOG ("Ignoring encoder %s", in_plugin->name); goto next; } diff --git a/ext/ffmpeg/gstffmpegenc.h b/ext/ffmpeg/gstffmpegenc.h index 4b455e7e55..019b1687fe 100644 --- a/ext/ffmpeg/gstffmpegenc.h +++ b/ext/ffmpeg/gstffmpegenc.h @@ -21,16 +21,16 @@ * object definition and other useful things. */ -#ifndef __GST_FFMPEGENC_H__ -#define __GST_FFMPEGENC_H__ +#ifndef __GST_FFMPEGAUDENC_H__ +#define __GST_FFMPEGAUDENC_H__ G_BEGIN_DECLS #include -typedef struct _GstFFMpegEnc GstFFMpegEnc; +typedef struct _GstFFMpegAudEnc GstFFMpegAudEnc; -struct _GstFFMpegEnc +struct _GstFFMpegAudEnc { GstElement element; @@ -39,7 +39,6 @@ struct _GstFFMpegEnc GstPad *sinkpad; AVCodecContext *context; - AVFrame *picture; gboolean opened; GstClockTime adapter_ts; guint64 adapter_consumed; @@ -48,39 +47,17 @@ struct _GstFFMpegEnc /* cache */ gint bitrate; - gint me_method; - gint gop_size; gint buffer_size; gint rtp_payload_size; - guint8 *working_buf; - gulong working_buf_size; - - /* settings with some special handling */ - guint pass; - gfloat quantizer; - gchar *filename; - guint lmin; - guint lmax; - gint max_key_interval; - gboolean interlaced; - - /* statistics file */ - FILE *file; - - /* for b-frame delay handling */ - GQueue *delay; - /* other settings are copied over straight, * include a context here, rather than copy-and-past it from avcodec.h */ AVCodecContext config; - - gboolean force_keyframe; }; -typedef struct _GstFFMpegEncClass GstFFMpegEncClass; +typedef struct _GstFFMpegAudEncClass GstFFMpegAudEncClass; -struct _GstFFMpegEncClass +struct _GstFFMpegAudEncClass { GstElementClass parent_class; @@ -89,17 +66,17 @@ struct _GstFFMpegEncClass GstCaps *sinkcaps; }; -#define GST_TYPE_FFMPEGENC \ - (gst_ffmpegenc_get_type()) -#define GST_FFMPEGENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGENC,GstFFMpegEnc)) -#define GST_FFMPEGENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGENC,GstFFMpegEncClass)) -#define GST_IS_FFMPEGENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGENC)) -#define GST_IS_FFMPEGENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGENC)) +#define GST_TYPE_FFMPEGAUDENC \ + (gst_ffmpegaudenc_get_type()) +#define GST_FFMPEGAUDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGAUDENC,GstFFMpegAudEnc)) +#define GST_FFMPEGAUDENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGAUDENC,GstFFMpegAudEncClass)) +#define GST_IS_FFMPEGAUDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGAUDENC)) +#define GST_IS_FFMPEGAUDENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGAUDENC)) G_END_DECLS -#endif /* __GST_FFMPEGENC_H__ */ +#endif /* __GST_FFMPEGAUDENC_H__ */ diff --git a/ext/ffmpeg/gstffmpegviddec.c b/ext/ffmpeg/gstffmpegviddec.c new file mode 100644 index 0000000000..3554ead117 --- /dev/null +++ b/ext/ffmpeg/gstffmpegviddec.c @@ -0,0 +1,1620 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef HAVE_FFMPEG_UNINSTALLED +#include +#else +#include +#endif + +#include +#include +#include + +#include "gstffmpeg.h" +#include "gstffmpegcodecmap.h" +#include "gstffmpegutils.h" + +typedef struct _GstFFMpegVidDec GstFFMpegVidDec; + +#define MAX_TS_MASK 0xff + +struct _GstFFMpegVidDec +{ + GstVideoDecoder parent; + + GstVideoCodecState *input_state; + GstVideoCodecState *output_state; + + /* decoding */ + AVCodecContext *context; + AVFrame *picture; + gboolean opened; + + enum PixelFormat pix_fmt; + gboolean waiting_for_key; + + /* for tracking DTS/PTS */ + gboolean has_b_frames; + + guint8 *padded; + guint padded_size; + + GValue *par; /* pixel aspect ratio of incoming data */ + gboolean current_dr; /* if direct rendering is enabled */ + gboolean extra_ref; /* keep extra ref around in get/release */ + + /* some properties */ + enum AVDiscard skip_frame; + gint lowres; + gboolean direct_rendering; + gboolean do_padding; + gboolean debug_mv; + gboolean crop; + int max_threads; + + gboolean is_realvideo; + + /* Can downstream allocate 16bytes aligned data. */ + gboolean can_allocate_aligned; +}; + +typedef struct _GstFFMpegVidDecClass GstFFMpegVidDecClass; + +struct _GstFFMpegVidDecClass +{ + GstVideoDecoderClass parent_class; + + AVCodec *in_plugin; +}; + + +#define GST_TYPE_FFMPEGDEC \ + (gst_ffmpegviddec_get_type()) +#define GST_FFMPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegVidDec)) +#define GST_FFMPEGVIDDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegVidDecClass)) +#define GST_IS_FFMPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC)) +#define GST_IS_FFMPEGVIDDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC)) + +#define DEFAULT_LOWRES 0 +#define DEFAULT_SKIPFRAME 0 +#define DEFAULT_DIRECT_RENDERING TRUE +#define DEFAULT_DO_PADDING TRUE +#define DEFAULT_DEBUG_MV FALSE +#define DEFAULT_CROP TRUE +#define DEFAULT_MAX_THREADS 0 + +enum +{ + PROP_0, + PROP_LOWRES, + PROP_SKIPFRAME, + PROP_DIRECT_RENDERING, + PROP_DO_PADDING, + PROP_DEBUG_MV, + PROP_CROP, + PROP_MAX_THREADS, + PROP_LAST +}; + +/* A number of function prototypes are given so we can refer to them later. */ +static void gst_ffmpegviddec_base_init (GstFFMpegVidDecClass * klass); +static void gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass); +static void gst_ffmpegviddec_init (GstFFMpegVidDec * ffmpegdec); +static void gst_ffmpegviddec_finalize (GObject * object); + +static gboolean gst_ffmpegviddec_set_format (GstVideoDecoder * decoder, + GstVideoCodecState * state); +static GstFlowReturn gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder, + GstVideoCodecFrame * frame); +static gboolean gst_ffmpegviddec_stop (GstVideoDecoder * decoder); +static gboolean gst_ffmpegviddec_reset (GstVideoDecoder * decoder, + gboolean hard); + +static void gst_ffmpegviddec_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_ffmpegviddec_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static gboolean gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec, + gboolean force); + +/* some sort of bufferpool handling, but different */ +static int gst_ffmpegviddec_get_buffer (AVCodecContext * context, + AVFrame * picture); +static void gst_ffmpegviddec_release_buffer (AVCodecContext * context, + AVFrame * picture); + +static GstFlowReturn gst_ffmpegviddec_finish (GstVideoDecoder * decoder); +static void gst_ffmpegviddec_drain (GstFFMpegVidDec * ffmpegdec); + +#define GST_FFDEC_PARAMS_QDATA g_quark_from_static_string("ffdec-params") + +static GstElementClass *parent_class = NULL; + +#define GST_FFMPEGVIDDEC_TYPE_LOWRES (gst_ffmpegviddec_lowres_get_type()) +static GType +gst_ffmpegviddec_lowres_get_type (void) +{ + static GType ffmpegdec_lowres_type = 0; + + if (!ffmpegdec_lowres_type) { + static const GEnumValue ffmpegdec_lowres[] = { + {0, "0", "full"}, + {1, "1", "1/2-size"}, + {2, "2", "1/4-size"}, + {0, NULL, NULL}, + }; + + ffmpegdec_lowres_type = + g_enum_register_static ("GstFFMpegVidDecLowres", ffmpegdec_lowres); + } + + return ffmpegdec_lowres_type; +} + +#define GST_FFMPEGVIDDEC_TYPE_SKIPFRAME (gst_ffmpegviddec_skipframe_get_type()) +static GType +gst_ffmpegviddec_skipframe_get_type (void) +{ + static GType ffmpegdec_skipframe_type = 0; + + if (!ffmpegdec_skipframe_type) { + static const GEnumValue ffmpegdec_skipframe[] = { + {0, "0", "Skip nothing"}, + {1, "1", "Skip B-frames"}, + {2, "2", "Skip IDCT/Dequantization"}, + {5, "5", "Skip everything"}, + {0, NULL, NULL}, + }; + + ffmpegdec_skipframe_type = + g_enum_register_static ("GstFFMpegVidDecSkipFrame", + ffmpegdec_skipframe); + } + + return ffmpegdec_skipframe_type; +} + +static void +gst_ffmpegviddec_base_init (GstFFMpegVidDecClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstPadTemplate *sinktempl, *srctempl; + GstCaps *sinkcaps, *srccaps; + AVCodec *in_plugin; + gchar *longname, *description; + + in_plugin = + (AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), + GST_FFDEC_PARAMS_QDATA); + g_assert (in_plugin != NULL); + + /* construct the element details struct */ + longname = g_strdup_printf ("FFmpeg %s decoder", in_plugin->long_name); + description = g_strdup_printf ("FFmpeg %s decoder", in_plugin->name); + gst_element_class_set_details_simple (element_class, longname, + "Codec/Decoder/Video", description, + "Wim Taymans , " + "Ronald Bultje , " + "Edward Hervey "); + g_free (longname); + g_free (description); + + /* get the caps */ + sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE); + if (!sinkcaps) { + GST_DEBUG ("Couldn't get sink caps for decoder '%s'", in_plugin->name); + sinkcaps = gst_caps_from_string ("unknown/unknown"); + } + srccaps = + gst_caps_from_string + ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray"); + + /* pad templates */ + sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, sinkcaps); + srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps); + + gst_element_class_add_pad_template (element_class, srctempl); + gst_element_class_add_pad_template (element_class, sinktempl); + + klass->in_plugin = in_plugin; +} + +static void +gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstVideoDecoderClass *viddec_class = GST_VIDEO_DECODER_CLASS (klass); + int caps; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gst_ffmpegviddec_finalize; + + gobject_class->set_property = gst_ffmpegviddec_set_property; + gobject_class->get_property = gst_ffmpegviddec_get_property; + + g_object_class_install_property (gobject_class, PROP_SKIPFRAME, + g_param_spec_enum ("skip-frame", "Skip frames", + "Which types of frames to skip during decoding", + GST_FFMPEGVIDDEC_TYPE_SKIPFRAME, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_LOWRES, + g_param_spec_enum ("lowres", "Low resolution", + "At which resolution to decode images", + GST_FFMPEGVIDDEC_TYPE_LOWRES, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DIRECT_RENDERING, + g_param_spec_boolean ("direct-rendering", "Direct Rendering", + "Enable direct rendering", DEFAULT_DIRECT_RENDERING, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DO_PADDING, + g_param_spec_boolean ("do-padding", "Do Padding", + "Add 0 padding before decoding data", DEFAULT_DO_PADDING, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DEBUG_MV, + g_param_spec_boolean ("debug-mv", "Debug motion vectors", + "Whether ffmpeg should print motion vectors on top of the image", + DEFAULT_DEBUG_MV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + caps = klass->in_plugin->capabilities; + if (caps & (CODEC_CAP_FRAME_THREADS | CODEC_CAP_SLICE_THREADS)) { + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_THREADS, + g_param_spec_int ("max-threads", "Maximum decode threads", + "Maximum number of worker threads to spawn. (0 = auto)", + 0, G_MAXINT, DEFAULT_MAX_THREADS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + } + + viddec_class->set_format = gst_ffmpegviddec_set_format; + viddec_class->handle_frame = gst_ffmpegviddec_handle_frame; + viddec_class->stop = gst_ffmpegviddec_stop; + viddec_class->reset = gst_ffmpegviddec_reset; + viddec_class->finish = gst_ffmpegviddec_finish; +} + +static void +gst_ffmpegviddec_init (GstFFMpegVidDec * ffmpegdec) +{ + /* some ffmpeg data */ + ffmpegdec->context = avcodec_alloc_context (); + ffmpegdec->picture = avcodec_alloc_frame (); + ffmpegdec->opened = FALSE; + ffmpegdec->waiting_for_key = TRUE; + ffmpegdec->skip_frame = ffmpegdec->lowres = 0; + ffmpegdec->direct_rendering = DEFAULT_DIRECT_RENDERING; + ffmpegdec->do_padding = DEFAULT_DO_PADDING; + ffmpegdec->debug_mv = DEFAULT_DEBUG_MV; + ffmpegdec->crop = DEFAULT_CROP; + ffmpegdec->max_threads = DEFAULT_MAX_THREADS; + + /* We initially assume downstream can allocate 16 bytes aligned buffers */ + ffmpegdec->can_allocate_aligned = TRUE; +} + +static void +gst_ffmpegviddec_finalize (GObject * object) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) object; + + if (ffmpegdec->context != NULL) { + av_free (ffmpegdec->context); + ffmpegdec->context = NULL; + } + + if (ffmpegdec->picture != NULL) { + av_free (ffmpegdec->picture); + ffmpegdec->picture = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +/* with LOCK */ +static void +gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec) +{ + if (!ffmpegdec->opened) + return; + + GST_LOG_OBJECT (ffmpegdec, "closing ffmpeg codec"); + + if (ffmpegdec->context->priv_data) + gst_ffmpeg_avcodec_close (ffmpegdec->context); + ffmpegdec->opened = FALSE; + + if (ffmpegdec->context->palctrl) { + av_free (ffmpegdec->context->palctrl); + ffmpegdec->context->palctrl = NULL; + } + + if (ffmpegdec->context->extradata) { + av_free (ffmpegdec->context->extradata); + ffmpegdec->context->extradata = NULL; + } +} + +/* with LOCK */ +static gboolean +gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec) +{ + GstFFMpegVidDecClass *oclass; + + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) + goto could_not_open; + + ffmpegdec->opened = TRUE; + ffmpegdec->is_realvideo = FALSE; + + GST_LOG_OBJECT (ffmpegdec, "Opened ffmpeg codec %s, id %d", + oclass->in_plugin->name, oclass->in_plugin->id); + + switch (oclass->in_plugin->id) { + case CODEC_ID_RV10: + case CODEC_ID_RV30: + case CODEC_ID_RV20: + case CODEC_ID_RV40: + ffmpegdec->is_realvideo = TRUE; + break; + default: + GST_LOG_OBJECT (ffmpegdec, "Parser deactivated for format"); + break; + } + + ffmpegdec->pix_fmt = PIX_FMT_NB; + + return TRUE; + + /* ERRORS */ +could_not_open: + { + gst_ffmpegviddec_close (ffmpegdec); + GST_DEBUG_OBJECT (ffmpegdec, "ffdec_%s: Failed to open FFMPEG codec", + oclass->in_plugin->name); + return FALSE; + } +} + +static gboolean +gst_ffmpegviddec_set_format (GstVideoDecoder * decoder, + GstVideoCodecState * state) +{ + GstFFMpegVidDec *ffmpegdec; + GstFFMpegVidDecClass *oclass; + gboolean ret = TRUE; + + ffmpegdec = (GstFFMpegVidDec *) decoder; + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + GST_DEBUG_OBJECT (ffmpegdec, "setcaps called"); + + GST_OBJECT_LOCK (ffmpegdec); + /* stupid check for VC1 */ + if ((oclass->in_plugin->id == CODEC_ID_WMV3) || + (oclass->in_plugin->id == CODEC_ID_VC1)) + oclass->in_plugin->id = gst_ffmpeg_caps_to_codecid (state->caps, NULL); + + /* close old session */ + if (ffmpegdec->opened) { + GST_OBJECT_UNLOCK (ffmpegdec); + gst_ffmpegviddec_drain (ffmpegdec); + GST_OBJECT_LOCK (ffmpegdec); + gst_ffmpegviddec_close (ffmpegdec); + + /* and reset the defaults that were set when a context is created */ + avcodec_get_context_defaults (ffmpegdec->context); + } + + /* set buffer functions */ + ffmpegdec->context->get_buffer = gst_ffmpegviddec_get_buffer; + ffmpegdec->context->release_buffer = gst_ffmpegviddec_release_buffer; + ffmpegdec->context->draw_horiz_band = NULL; + + ffmpegdec->has_b_frames = FALSE; + + GST_LOG_OBJECT (ffmpegdec, "size %dx%d", ffmpegdec->context->width, + ffmpegdec->context->height); + + /* FIXME : Create a method that takes GstVideoCodecState instead */ + /* get size and so */ + gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id, + oclass->in_plugin->type, state->caps, ffmpegdec->context); + + GST_LOG_OBJECT (ffmpegdec, "size after %dx%d", ffmpegdec->context->width, + ffmpegdec->context->height); + + if (!ffmpegdec->context->time_base.den || !ffmpegdec->context->time_base.num) { + GST_DEBUG_OBJECT (ffmpegdec, "forcing 25/1 framerate"); + ffmpegdec->context->time_base.num = 1; + ffmpegdec->context->time_base.den = 25; + } + + /* figure out if we can use direct rendering */ + ffmpegdec->current_dr = FALSE; + ffmpegdec->extra_ref = FALSE; + if (ffmpegdec->direct_rendering) { + GST_DEBUG_OBJECT (ffmpegdec, "trying to enable direct rendering"); + if (oclass->in_plugin->capabilities & CODEC_CAP_DR1) { + if (oclass->in_plugin->id == CODEC_ID_H264) { + GST_DEBUG_OBJECT (ffmpegdec, "disable direct rendering setup for H264"); + /* does not work, many stuff reads outside of the planes */ + ffmpegdec->current_dr = FALSE; + ffmpegdec->extra_ref = TRUE; + } else if ((oclass->in_plugin->id == CODEC_ID_SVQ1) || + (oclass->in_plugin->id == CODEC_ID_VP5) || + (oclass->in_plugin->id == CODEC_ID_VP6) || + (oclass->in_plugin->id == CODEC_ID_VP6F) || + (oclass->in_plugin->id == CODEC_ID_VP6A)) { + GST_DEBUG_OBJECT (ffmpegdec, + "disable direct rendering setup for broken stride support"); + /* does not work, uses a incompatible stride. See #610613 */ + ffmpegdec->current_dr = FALSE; + ffmpegdec->extra_ref = TRUE; + } else { + GST_DEBUG_OBJECT (ffmpegdec, "enabled direct rendering"); + ffmpegdec->current_dr = TRUE; + } + } else { + GST_DEBUG_OBJECT (ffmpegdec, "direct rendering not supported"); + } + } + if (ffmpegdec->current_dr) { + /* do *not* draw edges when in direct rendering, for some reason it draws + * outside of the memory. */ + ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE; + } + + /* workaround encoder bugs */ + ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT; + ffmpegdec->context->error_recognition = 1; + + /* for slow cpus */ + ffmpegdec->context->lowres = ffmpegdec->lowres; + ffmpegdec->context->skip_frame = ffmpegdec->skip_frame; + + /* ffmpeg can draw motion vectors on top of the image (not every decoder + * supports it) */ + ffmpegdec->context->debug_mv = ffmpegdec->debug_mv; + + if (ffmpegdec->max_threads == 0) { + if (!(oclass->in_plugin->capabilities & CODEC_CAP_AUTO_THREADS)) + ffmpegdec->context->thread_count = gst_ffmpeg_auto_max_threads (); + else + ffmpegdec->context->thread_count = 0; + } else + ffmpegdec->context->thread_count = ffmpegdec->max_threads; + + ffmpegdec->context->thread_type = FF_THREAD_SLICE; + + /* open codec - we don't select an output pix_fmt yet, + * simply because we don't know! We only get it + * during playback... */ + if (!gst_ffmpegviddec_open (ffmpegdec)) + goto open_failed; + + if (ffmpegdec->input_state) + gst_video_codec_state_unref (ffmpegdec->input_state); + ffmpegdec->input_state = gst_video_codec_state_ref (state); + +done: + GST_OBJECT_UNLOCK (ffmpegdec); + + return ret; + + /* ERRORS */ +open_failed: + { + GST_DEBUG_OBJECT (ffmpegdec, "Failed to open"); + goto done; + } +} + +static GstFlowReturn +alloc_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame) +{ + GstFlowReturn ret; + gint fsize; + + ret = GST_FLOW_ERROR; + + GST_LOG_OBJECT (ffmpegdec, "alloc output buffer"); + + /* see if we need renegotiation */ + if (G_UNLIKELY (!gst_ffmpegviddec_negotiate (ffmpegdec, FALSE))) + goto negotiate_failed; + + /* get the size of the gstreamer output buffer given a + * width/height/format */ + fsize = GST_VIDEO_INFO_SIZE (&ffmpegdec->output_state->info); + + if (!ffmpegdec->context->palctrl && ffmpegdec->can_allocate_aligned) { + GST_LOG_OBJECT (ffmpegdec, "calling pad_alloc"); + /* no pallete, we can use the buffer size to alloc */ + ret = + gst_video_decoder_alloc_output_frame (GST_VIDEO_DECODER (ffmpegdec), + frame); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto alloc_failed; + + /* If buffer isn't 128-bit aligned, create a memaligned one ourselves */ + if (((uintptr_t) GST_BUFFER_DATA (frame->output_buffer)) % 16) { + GST_DEBUG_OBJECT (ffmpegdec, + "Downstream can't allocate aligned buffers."); + ffmpegdec->can_allocate_aligned = FALSE; + gst_buffer_unref (frame->output_buffer); + frame->output_buffer = new_aligned_buffer (fsize, NULL); + } + } else { + GST_LOG_OBJECT (ffmpegdec, + "not calling pad_alloc, we have a pallete or downstream can't give 16 byte aligned buffers."); + /* for paletted data we can't use pad_alloc_buffer(), because + * fsize contains the size of the palette, so the overall size + * is bigger than ffmpegcolorspace's unit size, which will + * prompt GstBaseTransform to complain endlessly ... */ + frame->output_buffer = new_aligned_buffer (fsize, NULL); + ret = GST_FLOW_OK; + } + + return ret; + + /* special cases */ +negotiate_failed: + { + GST_DEBUG_OBJECT (ffmpegdec, "negotiate failed"); + return GST_FLOW_NOT_NEGOTIATED; + } +alloc_failed: + { + GST_DEBUG_OBJECT (ffmpegdec, "pad_alloc failed %d (%s)", ret, + gst_flow_get_name (ret)); + return ret; + } +} + +static int +gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture) +{ + GstVideoCodecFrame *frame; + GstFFMpegVidDec *ffmpegdec; + gint width, height; + gint coded_width, coded_height; + gint res; + gint c; + GstVideoInfo *info; + GstFlowReturn ret; + + + ffmpegdec = (GstFFMpegVidDec *) context->opaque; + + GST_DEBUG_OBJECT (ffmpegdec, "getting buffer"); + + /* apply the last info we have seen to this picture, when we get the + * picture back from ffmpeg we can use this to correctly timestamp the output + * buffer */ + picture->reordered_opaque = context->reordered_opaque; + + frame = + gst_video_decoder_get_frame (GST_VIDEO_DECODER (ffmpegdec), + picture->reordered_opaque); + if (G_UNLIKELY (frame == NULL)) + goto no_frame; + + picture->opaque = frame; + + if (!ffmpegdec->current_dr) { + GST_LOG_OBJECT (ffmpegdec, "direct rendering disabled, fallback alloc"); + res = avcodec_default_get_buffer (context, picture); + + GST_LOG_OBJECT (ffmpegdec, "linsize %d %d %d", picture->linesize[0], + picture->linesize[1], picture->linesize[2]); + GST_LOG_OBJECT (ffmpegdec, "data %u %u %u", 0, + (guint) (picture->data[1] - picture->data[0]), + (guint) (picture->data[2] - picture->data[0])); + return res; + } + + /* take width and height before clipping */ + width = context->width; + height = context->height; + coded_width = context->coded_width; + coded_height = context->coded_height; + + GST_LOG_OBJECT (ffmpegdec, "dimension %dx%d, coded %dx%d", width, height, + coded_width, coded_height); + + /* this is the size ffmpeg needs for the buffer */ + avcodec_align_dimensions (context, &width, &height); + GST_LOG_OBJECT (ffmpegdec, "Aligned dimensions %dx%d", width, height); + + if (width != context->width || height != context->height) { + /* We can't alloc if we need to clip the output buffer later */ + GST_LOG_OBJECT (ffmpegdec, "we need clipping, fallback alloc"); + return avcodec_default_get_buffer (context, picture); + } + + /* alloc with aligned dimensions for ffmpeg */ + ret = alloc_output_buffer (ffmpegdec, frame); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + /* alloc default buffer when we can't get one from downstream */ + GST_LOG_OBJECT (ffmpegdec, "alloc failed, fallback alloc"); + return avcodec_default_get_buffer (context, picture); + } + + /* Fill avpicture */ + info = &ffmpegdec->output_state->info; + for (c = 0; c < AV_NUM_DATA_POINTERS; c++) { + if (c < GST_VIDEO_INFO_N_PLANES (info)) { + picture->data[c] = + GST_BUFFER_DATA (frame->output_buffer) + + GST_VIDEO_INFO_PLANE_OFFSET (info, c); + picture->linesize[c] = GST_VIDEO_INFO_PLANE_STRIDE (info, c); + } else { + picture->data[c] = NULL; + picture->linesize[c] = 0; + } + } + GST_DEBUG_OBJECT (ffmpegdec, "from GstVideoInfo data %p %p %p", + picture->data[0], picture->data[1], picture->data[2]); + GST_DEBUG_OBJECT (ffmpegdec, "from GstVideoInfo linesize %d %d %d", + picture->linesize[0], picture->linesize[1], picture->linesize[2]); + + /* tell ffmpeg we own this buffer, tranfer the ref we have on the buffer to + * the opaque data. */ + picture->type = FF_BUFFER_TYPE_USER; + picture->age = 256 * 256 * 256 * 64; + + GST_LOG_OBJECT (ffmpegdec, "returned frame %p", frame->output_buffer); + + return 0; + +no_frame: + GST_WARNING_OBJECT (ffmpegdec, "Couldn't get codec frame !"); + return -1; +} + +static void +gst_ffmpegviddec_release_buffer (AVCodecContext * context, AVFrame * picture) +{ + gint i; + GstVideoCodecFrame *frame; + GstFFMpegVidDec *ffmpegdec; + + ffmpegdec = (GstFFMpegVidDec *) context->opaque; + frame = (GstVideoCodecFrame *) picture->opaque; + GST_DEBUG_OBJECT (ffmpegdec, "release frame %d", frame->system_frame_number); + + /* check if it was our buffer */ + if (picture->type != FF_BUFFER_TYPE_USER) { + GST_DEBUG_OBJECT (ffmpegdec, "default release buffer"); + avcodec_default_release_buffer (context, picture); + } + + /* we remove the opaque data now */ + picture->opaque = NULL; + + gst_video_codec_frame_unref (frame); + + /* zero out the reference in ffmpeg */ + for (i = 0; i < 4; i++) { + picture->data[i] = NULL; + picture->linesize[i] = 0; + } +} + +static gboolean +gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec, gboolean force) +{ + GstVideoInfo *info; + AVCodecContext *context = ffmpegdec->context; + GstVideoFormat fmt; + GstVideoCodecState *output_format; + + info = &ffmpegdec->input_state->info; + if (!force && GST_VIDEO_INFO_WIDTH (info) == context->width + && GST_VIDEO_INFO_HEIGHT (info) == context->height + && GST_VIDEO_INFO_PAR_N (info) == context->sample_aspect_ratio.num + && GST_VIDEO_INFO_PAR_D (info) == context->sample_aspect_ratio.den + && ffmpegdec->pix_fmt == context->pix_fmt) + return TRUE; + + GST_DEBUG_OBJECT (ffmpegdec, + "Renegotiating video from %dx%d@ (PAR %d:%d, %d/%d fps) to %dx%d@ (PAR %d:%d, %d/%d fps)", + GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), + GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info), + GST_VIDEO_INFO_FPS_N (info), GST_VIDEO_INFO_FPS_D (info), + context->width, context->height, + context->sample_aspect_ratio.num, + context->sample_aspect_ratio.den, -1, -1); + + /* Remember current pix_fmt */ + ffmpegdec->pix_fmt = context->pix_fmt; + fmt = gst_ffmpeg_pixfmt_to_videoformat (context->pix_fmt); + + if (G_UNLIKELY (fmt == GST_VIDEO_FORMAT_UNKNOWN)) + goto unknown_format; + + output_format = + gst_video_decoder_set_output_state (GST_VIDEO_DECODER (ffmpegdec), fmt, + ffmpegdec->context->width, ffmpegdec->context->height, + ffmpegdec->input_state); + if (context->sample_aspect_ratio.num) { + GST_VIDEO_INFO_PAR_N (&output_format->info) = + context->sample_aspect_ratio.num; + GST_VIDEO_INFO_PAR_D (&output_format->info) = + context->sample_aspect_ratio.den; + } + if (ffmpegdec->output_state) + gst_video_codec_state_unref (ffmpegdec->output_state); + ffmpegdec->output_state = output_format; + + return TRUE; + + /* ERRORS */ +unknown_format: + { + GST_ERROR_OBJECT (ffmpegdec, + "decoder requires a video format unsupported by GStreamer"); + return FALSE; + } +} + +/* perform qos calculations before decoding the next frame. + * + * Sets the skip_frame flag and if things are really bad, skips to the next + * keyframe. + * + * Returns TRUE if the frame should be decoded, FALSE if the frame can be dropped + * entirely. + */ +static gboolean +gst_ffmpegviddec_do_qos (GstFFMpegVidDec * ffmpegdec, + GstVideoCodecFrame * frame, gboolean * mode_switch) +{ + GstClockTimeDiff diff; + + *mode_switch = FALSE; + + if (frame == NULL) + goto no_qos; + + diff = + gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (ffmpegdec), + frame); + + /* if we don't have timing info, then we don't do QoS */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (diff))) + goto no_qos; + + GST_DEBUG_OBJECT (ffmpegdec, "decoding time %" GST_TIME_FORMAT, + GST_TIME_ARGS (diff)); + + if (diff > 0) + goto normal_mode; + + if (diff <= 0) { + if (ffmpegdec->waiting_for_key) + goto skipping; + goto skip_frame; + } + +no_qos: + return TRUE; + +skipping: + { + return FALSE; + } +normal_mode: + { + if (ffmpegdec->context->skip_frame != AVDISCARD_DEFAULT) { + ffmpegdec->context->skip_frame = AVDISCARD_DEFAULT; + *mode_switch = TRUE; + GST_DEBUG_OBJECT (ffmpegdec, "QOS: normal mode"); + } + return TRUE; + } +skip_frame: + { + if (ffmpegdec->context->skip_frame != AVDISCARD_NONREF) { + ffmpegdec->context->skip_frame = AVDISCARD_NONREF; + *mode_switch = TRUE; + GST_DEBUG_OBJECT (ffmpegdec, + "QOS: hurry up, diff %" G_GINT64_FORMAT " >= 0", diff); + } + return FALSE; + } +} + +/* figure out if the current picture is a keyframe, return TRUE if that is + * the case. */ +static gboolean +check_keyframe (GstFFMpegVidDec * ffmpegdec) +{ + GstFFMpegVidDecClass *oclass; + gboolean is_itype = FALSE; + gboolean is_reference = FALSE; + gboolean iskeyframe; + + /* figure out if we are dealing with a keyframe */ + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + /* remember that we have B frames, we need this for the DTS -> PTS conversion + * code */ + if (!ffmpegdec->has_b_frames && ffmpegdec->picture->pict_type == FF_B_TYPE) { + GST_DEBUG_OBJECT (ffmpegdec, "we have B frames"); + ffmpegdec->has_b_frames = TRUE; + /* FIXME : set latency on base video class */ + /* Emit latency message to recalculate it */ + gst_element_post_message (GST_ELEMENT_CAST (ffmpegdec), + gst_message_new_latency (GST_OBJECT_CAST (ffmpegdec))); + } + + is_itype = (ffmpegdec->picture->pict_type == FF_I_TYPE); + is_reference = (ffmpegdec->picture->reference == 1); + + iskeyframe = (is_itype || is_reference || ffmpegdec->picture->key_frame) + || (oclass->in_plugin->id == CODEC_ID_INDEO3) + || (oclass->in_plugin->id == CODEC_ID_MSZH) + || (oclass->in_plugin->id == CODEC_ID_ZLIB) + || (oclass->in_plugin->id == CODEC_ID_VP3) + || (oclass->in_plugin->id == CODEC_ID_HUFFYUV); + + GST_LOG_OBJECT (ffmpegdec, + "current picture: type: %d, is_keyframe:%d, is_itype:%d, is_reference:%d", + ffmpegdec->picture->pict_type, iskeyframe, is_itype, is_reference); + + return iskeyframe; +} + +/* get an outbuf buffer with the current picture */ +static GstFlowReturn +get_output_buffer (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame) +{ + GstFlowReturn ret = GST_FLOW_OK; + AVPicture pic, *outpic; + GstVideoInfo *info; + gint c; + + GST_LOG_OBJECT (ffmpegdec, "get output buffer"); + + ret = alloc_output_buffer (ffmpegdec, frame); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto alloc_failed; + + /* original ffmpeg code does not handle odd sizes correctly. + * This patched up version does */ + /* Fill avpicture */ + info = &ffmpegdec->output_state->info; + for (c = 0; c < AV_NUM_DATA_POINTERS; c++) { + if (c < GST_VIDEO_INFO_N_COMPONENTS (info)) { + pic.data[c] = + GST_BUFFER_DATA (frame->output_buffer) + + GST_VIDEO_INFO_COMP_OFFSET (info, c); + pic.linesize[c] = GST_VIDEO_INFO_COMP_STRIDE (info, c); + } else { + pic.data[c] = NULL; + pic.linesize[c] = 0; + } + } + + outpic = (AVPicture *) ffmpegdec->picture; + + GST_LOG_OBJECT (ffmpegdec, "linsize %d %d %d", outpic->linesize[0], + outpic->linesize[1], outpic->linesize[2]); + GST_LOG_OBJECT (ffmpegdec, "data %u %u %u", 0, + (guint) (outpic->data[1] - outpic->data[0]), + (guint) (outpic->data[2] - outpic->data[0])); + + av_picture_copy (&pic, outpic, ffmpegdec->context->pix_fmt, + GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info)); + + ffmpegdec->picture->reordered_opaque = -1; + + return ret; + + /* special cases */ +alloc_failed: + { + GST_DEBUG_OBJECT (ffmpegdec, "pad_alloc failed"); + return ret; + } +} + +static void +gst_avpacket_init (AVPacket * packet, guint8 * data, guint size) +{ + memset (packet, 0, sizeof (AVPacket)); + packet->data = data; + packet->size = size; +} + +/* gst_ffmpegviddec_[video|audio]_frame: + * ffmpegdec: + * data: pointer to the data to decode + * size: size of data in bytes + * in_timestamp: incoming timestamp. + * in_duration: incoming duration. + * in_offset: incoming offset (frame number). + * ret: Return flow. + * + * Returns: number of bytes used in decoding. The check for successful decode is + * outbuf being non-NULL. + */ +static gint +gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec, + guint8 * data, guint size, GstVideoCodecFrame * frame, GstFlowReturn * ret) +{ + gint len = -1; + gint have_data; + gboolean iskeyframe; + gboolean mode_switch; + gboolean decode; + gint skip_frame = AVDISCARD_DEFAULT; + GstVideoCodecFrame *out_frame; + AVPacket packet; + + *ret = GST_FLOW_OK; + + ffmpegdec->context->opaque = ffmpegdec; + + /* in case we skip frames */ + ffmpegdec->picture->pict_type = -1; + + /* run QoS code, we don't stop decoding the frame when we are late because + * else we might skip a reference frame */ + decode = gst_ffmpegviddec_do_qos (ffmpegdec, frame, &mode_switch); + + if (ffmpegdec->is_realvideo && data != NULL) { + gint slice_count; + gint i; + + /* setup the slice table for realvideo */ + if (ffmpegdec->context->slice_offset == NULL) + ffmpegdec->context->slice_offset = g_malloc (sizeof (guint32) * 1000); + + slice_count = (*data++) + 1; + ffmpegdec->context->slice_count = slice_count; + + for (i = 0; i < slice_count; i++) { + data += 4; + ffmpegdec->context->slice_offset[i] = GST_READ_UINT32_LE (data); + data += 4; + } + } + + if (!decode) { + /* no decoding needed, save previous skip_frame value and brutely skip + * decoding everything */ + skip_frame = ffmpegdec->context->skip_frame; + ffmpegdec->context->skip_frame = AVDISCARD_NONREF; + } + + if (frame) { + /* save reference to the timing info */ + ffmpegdec->context->reordered_opaque = (gint64) frame->system_frame_number; + ffmpegdec->picture->reordered_opaque = (gint64) frame->system_frame_number; + + GST_DEBUG_OBJECT (ffmpegdec, "stored opaque values idx %d", + frame->system_frame_number); + } + + /* now decode the frame */ + gst_avpacket_init (&packet, data, size); + len = avcodec_decode_video2 (ffmpegdec->context, + ffmpegdec->picture, &have_data, &packet); + + /* restore previous state */ + if (!decode) + ffmpegdec->context->skip_frame = skip_frame; + + GST_DEBUG_OBJECT (ffmpegdec, "after decode: len %d, have_data %d", + len, have_data); + + /* when we are in skip_frame mode, don't complain when ffmpeg returned + * no data because we told it to skip stuff. */ + if (len < 0 && (mode_switch || ffmpegdec->context->skip_frame)) + len = 0; + + /* no data, we're done */ + if (len < 0 || have_data <= 0) + goto beach; + + /* get the output picture timing info again */ + out_frame = ffmpegdec->picture->opaque; + + GST_DEBUG_OBJECT (ffmpegdec, + "pts %" G_GUINT64_FORMAT " duration %" G_GUINT64_FORMAT, + out_frame->pts, out_frame->duration); + GST_DEBUG_OBJECT (ffmpegdec, "picture: pts %" G_GUINT64_FORMAT, + (guint64) ffmpegdec->picture->pts); + GST_DEBUG_OBJECT (ffmpegdec, "picture: num %d", + ffmpegdec->picture->coded_picture_number); + GST_DEBUG_OBJECT (ffmpegdec, "picture: ref %d", + ffmpegdec->picture->reference); + GST_DEBUG_OBJECT (ffmpegdec, "picture: display %d", + ffmpegdec->picture->display_picture_number); + GST_DEBUG_OBJECT (ffmpegdec, "picture: opaque %p", + ffmpegdec->picture->opaque); + GST_DEBUG_OBJECT (ffmpegdec, "picture: reordered opaque %" G_GUINT64_FORMAT, + (guint64) ffmpegdec->picture->reordered_opaque); + GST_DEBUG_OBJECT (ffmpegdec, "repeat_pict:%d", + ffmpegdec->picture->repeat_pict); + GST_DEBUG_OBJECT (ffmpegdec, "interlaced_frame:%d", + ffmpegdec->picture->interlaced_frame); + + if (G_UNLIKELY (ffmpegdec->output_state + && ffmpegdec->picture->interlaced_frame != + GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->output_state->info))) { + GST_WARNING ("Change in interlacing ! picture:%d, recorded:%d", + ffmpegdec->picture->interlaced_frame, + GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->output_state->info)); + if (ffmpegdec->picture->interlaced_frame) + GST_VIDEO_INFO_INTERLACE_MODE (&ffmpegdec->output_state->info) = + GST_VIDEO_INTERLACE_MODE_INTERLEAVED; + else + GST_VIDEO_INFO_INTERLACE_MODE (&ffmpegdec->output_state->info) = + GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + gst_ffmpegviddec_negotiate (ffmpegdec, TRUE); + } + + if (G_UNLIKELY (out_frame->output_buffer == NULL)) + *ret = get_output_buffer (ffmpegdec, out_frame); + + if (G_UNLIKELY (*ret != GST_FLOW_OK)) + goto no_output; + + if (G_UNLIKELY (ffmpegdec->output_state + && ffmpegdec->picture->interlaced_frame != + GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->output_state->info))) { + GST_WARNING ("Change in interlacing ! picture:%d, recorded:%d", + ffmpegdec->picture->interlaced_frame, + GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->output_state->info)); + if (ffmpegdec->picture->interlaced_frame) + GST_VIDEO_INFO_INTERLACE_MODE (&ffmpegdec->output_state->info) = + GST_VIDEO_INTERLACE_MODE_INTERLEAVED; + else + GST_VIDEO_INFO_INTERLACE_MODE (&ffmpegdec->output_state->info) = + GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + gst_ffmpegviddec_negotiate (ffmpegdec, TRUE); + } + + /* check if we are dealing with a keyframe here, this will also check if we + * are dealing with B frames. */ + iskeyframe = check_keyframe (ffmpegdec); + + /* when we're waiting for a keyframe, see if we have one or drop the current + * non-keyframe */ + if (G_UNLIKELY (ffmpegdec->waiting_for_key)) { + if (G_LIKELY (!iskeyframe)) + goto drop_non_keyframe; + + /* we have a keyframe, we can stop waiting for one */ + ffmpegdec->waiting_for_key = FALSE; + } +#if 0 + /* FIXME : How should we properly handle this with base classes */ + frame->n_fields = ffmpegdec->picture->repeat_pict; +#endif + + /* palette is not part of raw video frame in gst and the size + * of the outgoing buffer needs to be adjusted accordingly */ + if (ffmpegdec->context->palctrl != NULL) + GST_BUFFER_SIZE (out_frame->output_buffer) -= AVPALETTE_SIZE; + + /* mark as keyframe or delta unit */ + if (ffmpegdec->picture->top_field_first) + GST_VIDEO_CODEC_FRAME_FLAG_SET (out_frame, GST_VIDEO_CODEC_FRAME_FLAG_TFF); + + + *ret = + gst_video_decoder_finish_frame (GST_VIDEO_DECODER (ffmpegdec), out_frame); + +beach: + GST_DEBUG_OBJECT (ffmpegdec, "return flow %d, len %d", *ret, len); + return len; + + /* special cases */ +drop_non_keyframe: + { + GST_WARNING_OBJECT (ffmpegdec, "Dropping non-keyframe (seek/init)"); + gst_video_decoder_drop_frame (GST_VIDEO_DECODER (ffmpegdec), out_frame); + goto beach; + } + +no_output: + { + GST_DEBUG_OBJECT (ffmpegdec, "no output buffer"); + gst_video_decoder_drop_frame (GST_VIDEO_DECODER (ffmpegdec), out_frame); + len = -1; + goto beach; + } +} + + +/* gst_ffmpegviddec_frame: + * ffmpegdec: + * data: pointer to the data to decode + * size: size of data in bytes + * got_data: 0 if no data was decoded, != 0 otherwise. + * in_time: timestamp of data + * in_duration: duration of data + * ret: GstFlowReturn to return in the chain function + * + * Decode the given frame and pushes it downstream. + * + * Returns: Number of bytes used in decoding, -1 on error/failure. + */ + +static gint +gst_ffmpegviddec_frame (GstFFMpegVidDec * ffmpegdec, + guint8 * data, guint size, gint * got_data, GstVideoCodecFrame * frame, + GstFlowReturn * ret) +{ + GstFFMpegVidDecClass *oclass; + gint have_data = 0, len = 0; + + if (G_UNLIKELY (ffmpegdec->context->codec == NULL)) + goto no_codec; + + GST_LOG_OBJECT (ffmpegdec, "data:%p, size:%d", data, size); + + *ret = GST_FLOW_OK; + ffmpegdec->context->frame_number++; + + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + len = gst_ffmpegviddec_video_frame (ffmpegdec, data, size, frame, ret); + + if (frame && frame->output_buffer) + have_data = 1; + + if (len < 0 || have_data < 0) { + GST_WARNING_OBJECT (ffmpegdec, + "ffdec_%s: decoding error (len: %d, have_data: %d)", + oclass->in_plugin->name, len, have_data); + *got_data = 0; + goto beach; + } + if (len == 0 && have_data == 0) { + *got_data = 0; + goto beach; + } + + /* this is where I lost my last clue on ffmpeg... */ + *got_data = 1; + +beach: + return len; + + /* ERRORS */ +no_codec: + { + GST_ERROR_OBJECT (ffmpegdec, "no codec context"); + return -1; + } +} + +static void +gst_ffmpegviddec_drain (GstFFMpegVidDec * ffmpegdec) +{ + GstFFMpegVidDecClass *oclass; + + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + if (oclass->in_plugin->capabilities & CODEC_CAP_DELAY) { + gint have_data, len, try = 0; + + GST_LOG_OBJECT (ffmpegdec, + "codec has delay capabilities, calling until ffmpeg has drained everything"); + + do { + GstFlowReturn ret; + + len = gst_ffmpegviddec_frame (ffmpegdec, NULL, 0, &have_data, NULL, &ret); + if (len < 0 || have_data == 0) + break; + } while (try++ < 10); + } +} + +static GstFlowReturn +gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder, + GstVideoCodecFrame * frame) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; + guint8 *data, *bdata; + gint size, bsize, len, have_data; + GstFlowReturn ret = GST_FLOW_OK; + + /* do early keyframe check pretty bad to rely on the keyframe flag in the + * source for this as it might not even be parsed (UDP/file/..). */ + if (G_UNLIKELY (ffmpegdec->waiting_for_key)) { + GST_DEBUG_OBJECT (ffmpegdec, "waiting for keyframe"); + if (!GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) + goto skip_keyframe; + + GST_DEBUG_OBJECT (ffmpegdec, "got keyframe"); + ffmpegdec->waiting_for_key = FALSE; + } + + GST_LOG_OBJECT (ffmpegdec, + "Received new data of size %u, pts:%" + GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT, + GST_BUFFER_SIZE (frame->input_buffer), + GST_TIME_ARGS (frame->pts), GST_TIME_ARGS (frame->duration)); + + bdata = GST_BUFFER_DATA (frame->input_buffer); + bsize = GST_BUFFER_SIZE (frame->input_buffer); + + if (ffmpegdec->do_padding) { + /* add padding */ + if (ffmpegdec->padded_size < bsize + FF_INPUT_BUFFER_PADDING_SIZE) { + ffmpegdec->padded_size = bsize + FF_INPUT_BUFFER_PADDING_SIZE; + ffmpegdec->padded = g_realloc (ffmpegdec->padded, ffmpegdec->padded_size); + GST_LOG_OBJECT (ffmpegdec, "resized padding buffer to %d", + ffmpegdec->padded_size); + } + memcpy (ffmpegdec->padded, bdata, bsize); + memset (ffmpegdec->padded + bsize, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + bdata = ffmpegdec->padded; + } + + do { + guint8 tmp_padding[FF_INPUT_BUFFER_PADDING_SIZE]; + + /* parse, if at all possible */ + data = bdata; + size = bsize; + + if (ffmpegdec->do_padding) { + /* add temporary padding */ + memcpy (tmp_padding, data + size, FF_INPUT_BUFFER_PADDING_SIZE); + memset (data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + } + + /* decode a frame of audio/video now */ + len = + gst_ffmpegviddec_frame (ffmpegdec, data, size, &have_data, frame, &ret); + + if (ffmpegdec->do_padding) { + memcpy (data + size, tmp_padding, FF_INPUT_BUFFER_PADDING_SIZE); + } + + if (ret != GST_FLOW_OK) { + GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s", + gst_flow_get_name (ret)); + /* bad flow retun, make sure we discard all data and exit */ + bsize = 0; + break; + } + + if (len == 0 && !have_data) { + /* nothing was decoded, this could be because no data was available or + * because we were skipping frames. + * If we have no context we must exit and wait for more data, we keep the + * data we tried. */ + GST_LOG_OBJECT (ffmpegdec, "Decoding didn't return any data, breaking"); + break; + } + + if (len < 0) { + /* a decoding error happened, we must break and try again with next data. */ + GST_LOG_OBJECT (ffmpegdec, "Decoding error, breaking"); + bsize = 0; + break; + } + + /* prepare for the next round, for codecs with a context we did this + * already when using the parser. */ + bsize -= len; + bdata += len; + + GST_LOG_OBJECT (ffmpegdec, "Before (while bsize>0). bsize:%d , bdata:%p", + bsize, bdata); + } while (bsize > 0); + + if (bsize > 0) + GST_DEBUG_OBJECT (ffmpegdec, "Dropping %d bytes of data", bsize); + + return ret; + + /* ERRORS */ +skip_keyframe: + { + GST_DEBUG_OBJECT (ffmpegdec, "skipping non keyframe"); + return gst_video_decoder_drop_frame (decoder, frame); + } +} + +static gboolean +gst_ffmpegviddec_stop (GstVideoDecoder * decoder) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; + + GST_OBJECT_LOCK (ffmpegdec); + gst_ffmpegviddec_close (ffmpegdec); + GST_OBJECT_UNLOCK (ffmpegdec); + g_free (ffmpegdec->padded); + ffmpegdec->padded = NULL; + ffmpegdec->padded_size = 0; + ffmpegdec->can_allocate_aligned = TRUE; + if (ffmpegdec->input_state) + gst_video_codec_state_unref (ffmpegdec->input_state); + ffmpegdec->input_state = NULL; + if (ffmpegdec->output_state) + gst_video_codec_state_unref (ffmpegdec->output_state); + ffmpegdec->output_state = NULL; + + return TRUE; +} + +static GstFlowReturn +gst_ffmpegviddec_finish (GstVideoDecoder * decoder) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; + + gst_ffmpegviddec_drain (ffmpegdec); + + return GST_FLOW_OK; +} + +static gboolean +gst_ffmpegviddec_reset (GstVideoDecoder * decoder, gboolean hard) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; + + if (ffmpegdec->opened) { + if (!hard) + gst_ffmpegviddec_drain (ffmpegdec); + avcodec_flush_buffers (ffmpegdec->context); + } + + return TRUE; +} + +static void +gst_ffmpegviddec_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) object; + + switch (prop_id) { + case PROP_LOWRES: + ffmpegdec->lowres = ffmpegdec->context->lowres = g_value_get_enum (value); + break; + case PROP_SKIPFRAME: + ffmpegdec->skip_frame = ffmpegdec->context->skip_frame = + g_value_get_enum (value); + break; + case PROP_DIRECT_RENDERING: + ffmpegdec->direct_rendering = g_value_get_boolean (value); + break; + case PROP_DO_PADDING: + ffmpegdec->do_padding = g_value_get_boolean (value); + break; + case PROP_DEBUG_MV: + ffmpegdec->debug_mv = ffmpegdec->context->debug_mv = + g_value_get_boolean (value); + break; + case PROP_CROP: + ffmpegdec->crop = g_value_get_boolean (value); + break; + case PROP_MAX_THREADS: + ffmpegdec->max_threads = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_ffmpegviddec_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) object; + + switch (prop_id) { + case PROP_LOWRES: + g_value_set_enum (value, ffmpegdec->context->lowres); + break; + case PROP_SKIPFRAME: + g_value_set_enum (value, ffmpegdec->context->skip_frame); + break; + case PROP_DIRECT_RENDERING: + g_value_set_boolean (value, ffmpegdec->direct_rendering); + break; + case PROP_DO_PADDING: + g_value_set_boolean (value, ffmpegdec->do_padding); + break; + case PROP_DEBUG_MV: + g_value_set_boolean (value, ffmpegdec->context->debug_mv); + break; + case PROP_CROP: + g_value_set_boolean (value, ffmpegdec->crop); + break; + case PROP_MAX_THREADS: + g_value_set_int (value, ffmpegdec->max_threads); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_ffmpegviddec_register (GstPlugin * plugin) +{ + GTypeInfo typeinfo = { + sizeof (GstFFMpegVidDecClass), + (GBaseInitFunc) gst_ffmpegviddec_base_init, + NULL, + (GClassInitFunc) gst_ffmpegviddec_class_init, + NULL, + NULL, + sizeof (GstFFMpegVidDec), + 0, + (GInstanceInitFunc) gst_ffmpegviddec_init, + }; + GType type; + AVCodec *in_plugin; + gint rank; + + in_plugin = av_codec_next (NULL); + + GST_LOG ("Registering decoders"); + + while (in_plugin) { + gchar *type_name; + gchar *plugin_name; + + /* only decoders */ + if (!in_plugin->decode) { + goto next; + } + + /* no quasi-codecs, please */ + if (in_plugin->id == CODEC_ID_RAWVIDEO || + in_plugin->id == CODEC_ID_V210 || + in_plugin->id == CODEC_ID_V210X || + in_plugin->id == CODEC_ID_R210 || + (in_plugin->id >= CODEC_ID_PCM_S16LE && + in_plugin->id <= CODEC_ID_PCM_BLURAY)) { + goto next; + } + + /* No decoders depending on external libraries (we don't build them, but + * people who build against an external ffmpeg might have them. + * We have native gstreamer plugins for all of those libraries anyway. */ + if (!strncmp (in_plugin->name, "lib", 3)) { + GST_DEBUG + ("Not using external library decoder %s. Use the gstreamer-native ones instead.", + in_plugin->name); + goto next; + } + + /* No vdpau plugins until we can figure out how to properly use them + * outside of ffmpeg. */ + if (g_str_has_suffix (in_plugin->name, "_vdpau")) { + GST_DEBUG + ("Ignoring VDPAU decoder %s. We can't handle this outside of ffmpeg", + in_plugin->name); + goto next; + } + + if (g_str_has_suffix (in_plugin->name, "_xvmc")) { + GST_DEBUG + ("Ignoring XVMC decoder %s. We can't handle this outside of ffmpeg", + in_plugin->name); + goto next; + } + + GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name); + + /* no codecs for which we're GUARANTEED to have better alternatives */ + /* MPEG1VIDEO : the mpeg2video decoder is preferred */ + /* MP1 : Use MP3 for decoding */ + /* MP2 : Use MP3 for decoding */ + /* Theora: Use libtheora based theoradec */ + if (!strcmp (in_plugin->name, "gif") || + !strcmp (in_plugin->name, "theora") || + !strcmp (in_plugin->name, "mpeg1video") || + !strcmp (in_plugin->name, "ass") || + !strcmp (in_plugin->name, "srt") || + !strcmp (in_plugin->name, "pgssub") || + !strcmp (in_plugin->name, "dvdsub") || + !strcmp (in_plugin->name, "dvbsub")) { + GST_LOG ("Ignoring decoder %s", in_plugin->name); + goto next; + } + + /* construct the type */ + plugin_name = g_strdup ((gchar *) in_plugin->name); + g_strdelimit (plugin_name, NULL, '_'); + type_name = g_strdup_printf ("ffdec_%s", plugin_name); + g_free (plugin_name); + + type = g_type_from_name (type_name); + + if (!type) { + /* create the gtype now */ + type = + g_type_register_static (GST_TYPE_VIDEO_DECODER, type_name, &typeinfo, + 0); + g_type_set_qdata (type, GST_FFDEC_PARAMS_QDATA, (gpointer) in_plugin); + } + + /* (Ronald) MPEG-4 gets a higher priority because it has been well- + * tested and by far outperforms divxdec/xviddec - so we prefer it. + * msmpeg4v3 same, as it outperforms divxdec for divx3 playback. + * VC1/WMV3 are not working and thus unpreferred for now. */ + switch (in_plugin->id) { + case CODEC_ID_MPEG4: + case CODEC_ID_MSMPEG4V3: + case CODEC_ID_H264: + case CODEC_ID_RV10: + case CODEC_ID_RV20: + case CODEC_ID_RV30: + case CODEC_ID_RV40: + rank = GST_RANK_PRIMARY; + break; + /* DVVIDEO: we have a good dv decoder, fast on both ppc as well as x86. + * They say libdv's quality is better though. leave as secondary. + * note: if you change this, see the code in gstdv.c in good/ext/dv. + */ + case CODEC_ID_DVVIDEO: + rank = GST_RANK_SECONDARY; + break; + default: + rank = GST_RANK_MARGINAL; + break; + } + if (!gst_element_register (plugin, type_name, rank, type)) { + g_warning ("Failed to register %s", type_name); + g_free (type_name); + return FALSE; + } + + g_free (type_name); + + next: + in_plugin = av_codec_next (in_plugin); + } + + GST_LOG ("Finished Registering decoders"); + + return TRUE; +} diff --git a/ext/ffmpeg/gstffmpegvidenc.c b/ext/ffmpeg/gstffmpegvidenc.c new file mode 100644 index 0000000000..bbb1bec766 --- /dev/null +++ b/ext/ffmpeg/gstffmpegvidenc.c @@ -0,0 +1,987 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +/* for stats file handling */ +#include +#include +#include + +#ifdef HAVE_FFMPEG_UNINSTALLED +#include +#else +#include +#endif + +#include + +#include "gstffmpeg.h" +#include "gstffmpegcodecmap.h" +#include "gstffmpegutils.h" +#include "gstffmpegvidenc.h" +#include "gstffmpegcfg.h" + +#define DEFAULT_VIDEO_BITRATE 300000 /* in bps */ +#define DEFAULT_VIDEO_GOP_SIZE 15 + +#define DEFAULT_WIDTH 352 +#define DEFAULT_HEIGHT 288 + + +#define VIDEO_BUFFER_SIZE (1024*1024) + +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, + ARG_BIT_RATE, + ARG_GOP_SIZE, + ARG_ME_METHOD, + ARG_BUFSIZE, + ARG_RTP_PAYLOAD_SIZE, + ARG_CFG_BASE +}; + +#define GST_TYPE_ME_METHOD (gst_ffmpegvidenc_me_method_get_type()) +static GType +gst_ffmpegvidenc_me_method_get_type (void) +{ + static GType ffmpegenc_me_method_type = 0; + static GEnumValue ffmpegenc_me_methods[] = { + {ME_ZERO, "None (Very low quality)", "zero"}, + {ME_FULL, "Full (Slow, unmaintained)", "full"}, + {ME_LOG, "Logarithmic (Low quality, unmaintained)", "logarithmic"}, + {ME_PHODS, "phods (Low quality, unmaintained)", "phods"}, + {ME_EPZS, "EPZS (Best quality, Fast)", "epzs"}, + {ME_X1, "X1 (Experimental)", "x1"}, + {0, NULL, NULL}, + }; + if (!ffmpegenc_me_method_type) { + ffmpegenc_me_method_type = + g_enum_register_static ("GstFFMpegVidEncMeMethod", + ffmpegenc_me_methods); + } + return ffmpegenc_me_method_type; +} + +/* A number of function prototypes are given so we can refer to them later. */ +static void gst_ffmpegvidenc_class_init (GstFFMpegVidEncClass * klass); +static void gst_ffmpegvidenc_base_init (GstFFMpegVidEncClass * klass); +static void gst_ffmpegvidenc_init (GstFFMpegVidEnc * ffmpegenc); +static void gst_ffmpegvidenc_finalize (GObject * object); + +static gboolean gst_ffmpegvidenc_stop (GstVideoEncoder * encoder); +static GstFlowReturn gst_ffmpegvidenc_finish (GstVideoEncoder * encoder); +static gboolean gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state); + +static GstCaps *gst_ffmpegvidenc_getcaps (GstVideoEncoder * encoder); +static GstFlowReturn gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame); + +static void gst_ffmpegvidenc_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_ffmpegvidenc_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +#define GST_FFENC_PARAMS_QDATA g_quark_from_static_string("ffenc-params") + +static GstElementClass *parent_class = NULL; + +/*static guint gst_ffmpegvidenc_signals[LAST_SIGNAL] = { 0 }; */ + +static void +gst_ffmpegvidenc_base_init (GstFFMpegVidEncClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + AVCodec *in_plugin; + GstPadTemplate *srctempl = NULL, *sinktempl = NULL; + GstCaps *srccaps = NULL, *sinkcaps = NULL; + gchar *longname, *description; + + in_plugin = + (AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), + GST_FFENC_PARAMS_QDATA); + g_assert (in_plugin != NULL); + + /* construct the element details struct */ + longname = g_strdup_printf ("FFmpeg %s encoder", in_plugin->long_name); + description = g_strdup_printf ("FFmpeg %s encoder", in_plugin->name); + gst_element_class_set_details_simple (element_class, longname, + "Codec/Encoder/Video", description, + "Wim Taymans , " + "Ronald Bultje "); + g_free (longname); + g_free (description); + + if (!(srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE))) { + GST_DEBUG ("Couldn't get source caps for encoder '%s'", in_plugin->name); + srccaps = gst_caps_new_simple ("unknown/unknown", NULL); + } + + sinkcaps = gst_caps_from_string + ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray"); + + /* pad templates */ + sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, sinkcaps); + srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps); + + gst_element_class_add_pad_template (element_class, srctempl); + gst_element_class_add_pad_template (element_class, sinktempl); + + klass->in_plugin = in_plugin; + klass->srctempl = srctempl; + klass->sinktempl = sinktempl; + klass->sinkcaps = NULL; + + return; +} + +static void +gst_ffmpegvidenc_class_init (GstFFMpegVidEncClass * klass) +{ + GObjectClass *gobject_class; + GstVideoEncoderClass *venc_class; + + gobject_class = (GObjectClass *) klass; + venc_class = (GstVideoEncoderClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_ffmpegvidenc_set_property; + gobject_class->get_property = gst_ffmpegvidenc_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE, + g_param_spec_ulong ("bitrate", "Bit Rate", + "Target Video Bitrate", 0, G_MAXULONG, DEFAULT_VIDEO_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GOP_SIZE, + g_param_spec_int ("gop-size", "GOP Size", + "Number of frames within one GOP", 0, G_MAXINT, + DEFAULT_VIDEO_GOP_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ME_METHOD, + g_param_spec_enum ("me-method", "ME Method", "Motion Estimation Method", + GST_TYPE_ME_METHOD, ME_EPZS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /* FIXME 0.11: Make this property read-only */ + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFSIZE, + g_param_spec_ulong ("buffer-size", "Buffer Size", + "Size of the video buffers. " + "Note: Setting this property has no effect " + "and is deprecated!", 0, G_MAXULONG, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + ARG_RTP_PAYLOAD_SIZE, g_param_spec_ulong ("rtp-payload-size", + "RTP Payload Size", "Target GOB length", 0, G_MAXULONG, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /* register additional properties, possibly dependent on the exact CODEC */ + gst_ffmpeg_cfg_install_property (klass, ARG_CFG_BASE); + + venc_class->stop = gst_ffmpegvidenc_stop; + venc_class->finish = gst_ffmpegvidenc_finish; + venc_class->handle_frame = gst_ffmpegvidenc_handle_frame; + venc_class->getcaps = gst_ffmpegvidenc_getcaps; + venc_class->set_format = gst_ffmpegvidenc_set_format; + + gobject_class->finalize = gst_ffmpegvidenc_finalize; +} + +static void +gst_ffmpegvidenc_init (GstFFMpegVidEnc * ffmpegenc) +{ + /* ffmpeg objects */ + ffmpegenc->context = avcodec_alloc_context (); + ffmpegenc->picture = avcodec_alloc_frame (); + ffmpegenc->opened = FALSE; + + ffmpegenc->file = NULL; + + ffmpegenc->bitrate = DEFAULT_VIDEO_BITRATE; + ffmpegenc->me_method = ME_EPZS; + ffmpegenc->buffer_size = 512 * 1024; + ffmpegenc->gop_size = DEFAULT_VIDEO_GOP_SIZE; + ffmpegenc->rtp_payload_size = 0; + + ffmpegenc->lmin = 2; + ffmpegenc->lmax = 31; + ffmpegenc->max_key_interval = 0; + + gst_ffmpeg_cfg_set_defaults (ffmpegenc); +} + +static void +gst_ffmpegvidenc_finalize (GObject * object) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) object; + + gst_ffmpeg_cfg_finalize (ffmpegenc); + + /* close old session */ + if (ffmpegenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegenc->context); + ffmpegenc->opened = FALSE; + } + + /* clean up remaining allocated data */ + av_free (ffmpegenc->context); + av_free (ffmpegenc->picture); + + g_free (ffmpegenc->filename); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstCaps * +gst_ffmpegvidenc_getcaps (GstVideoEncoder * encoder) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + GstFFMpegVidEncClass *oclass = + (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); + AVCodecContext *ctx = NULL; + enum PixelFormat pixfmt; + GstCaps *caps = NULL; + gint i; + + GST_DEBUG_OBJECT (ffmpegenc, "getting caps"); + + /* cached */ + if (oclass->sinkcaps) { + caps = gst_video_encoder_proxy_getcaps (encoder, oclass->sinkcaps); + GST_DEBUG_OBJECT (ffmpegenc, "return cached caps %" GST_PTR_FORMAT, caps); + return caps; + } + + /* create cache etc. */ + + /* shut up the logging while we autoprobe; we don't want warnings and + * errors about unsupported formats */ + /* FIXME: if someone cares about this disabling the logging for other + * instances/threads/..., one could investigate if there is a way to + * set this as a struct member on the av context, and check it from the + * log handler */ +#ifndef GST_DISABLE_GST_DEBUG + _shut_up_I_am_probing = TRUE; +#endif + GST_DEBUG_OBJECT (ffmpegenc, "probing caps"); + i = pixfmt = 0; + /* check pixfmt until deemed finished */ + for (pixfmt = 0;; pixfmt++) { + GstCaps *tmpcaps; + + /* override looping all pixfmt if codec declares pixfmts; + * these may not properly check and report supported pixfmt during _init */ + if (oclass->in_plugin->pix_fmts) { + if ((pixfmt = oclass->in_plugin->pix_fmts[i++]) == PIX_FMT_NONE) { + GST_DEBUG_OBJECT (ffmpegenc, + "At the end of official pixfmt for this codec, breaking out"); + break; + } + GST_DEBUG_OBJECT (ffmpegenc, + "Got an official pixfmt [%d], attempting to get caps", pixfmt); + tmpcaps = gst_ffmpeg_pixfmt_to_caps (pixfmt, NULL, oclass->in_plugin->id); + if (tmpcaps) { + GST_DEBUG_OBJECT (ffmpegenc, "Got caps, breaking out"); + if (!caps) + caps = gst_caps_new_empty (); + gst_caps_append (caps, tmpcaps); + continue; + } + GST_DEBUG_OBJECT (ffmpegenc, + "Couldn't figure out caps without context, trying again with a context"); + } + + GST_DEBUG_OBJECT (ffmpegenc, "pixfmt :%d", pixfmt); + if (pixfmt >= PIX_FMT_NB) { + GST_WARNING ("Invalid pixfmt, breaking out"); + break; + } + + /* need to start with a fresh codec_context each time around, since + * codec_close may have released stuff causing the next pass to segfault */ + ctx = avcodec_alloc_context (); + if (!ctx) { + GST_DEBUG_OBJECT (ffmpegenc, "no context"); + break; + } + + /* set some default properties */ + ctx->width = DEFAULT_WIDTH; + ctx->height = DEFAULT_HEIGHT; + ctx->time_base.num = 1; + ctx->time_base.den = 25; + ctx->ticks_per_frame = 1; + ctx->bit_rate = DEFAULT_VIDEO_BITRATE; + /* makes it silent */ + ctx->strict_std_compliance = -1; + + ctx->pix_fmt = pixfmt; + + GST_DEBUG ("Attempting to open codec"); + if (gst_ffmpeg_avcodec_open (ctx, oclass->in_plugin) >= 0 && + ctx->pix_fmt == pixfmt) { + ctx->width = -1; + if (!caps) + caps = gst_caps_new_empty (); + tmpcaps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type, ctx, + oclass->in_plugin->id, TRUE); + if (tmpcaps) + gst_caps_append (caps, tmpcaps); + else + GST_LOG_OBJECT (ffmpegenc, + "Couldn't get caps for oclass->in_plugin->name:%s", + oclass->in_plugin->name); + gst_ffmpeg_avcodec_close (ctx); + } else { + GST_DEBUG_OBJECT (ffmpegenc, "Opening codec failed with pixfmt : %d", + pixfmt); + } + if (ctx->priv_data) + gst_ffmpeg_avcodec_close (ctx); + av_free (ctx); + } +#ifndef GST_DISABLE_GST_DEBUG + _shut_up_I_am_probing = FALSE; +#endif + + oclass->sinkcaps = gst_video_encoder_proxy_getcaps (encoder, caps); + + return gst_caps_ref (oclass->sinkcaps); +} + +static gboolean +gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state) +{ + GstCaps *other_caps; + GstCaps *allowed_caps; + GstCaps *icaps; + GstVideoCodecState *output_format; + enum PixelFormat pix_fmt; + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + GstFFMpegVidEncClass *oclass = + (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); + + /* close old session */ + if (ffmpegenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegenc->context); + ffmpegenc->opened = FALSE; +#if 0 + /* FIXME : Is this still needed with GstVideoEncoder ?? */ + /* fixed src caps; + * so clear src caps for proper (re-)negotiation */ + gst_pad_set_caps (ffmpegenc->srcpad, NULL); +#endif + } + + /* set defaults */ + avcodec_get_context_defaults (ffmpegenc->context); + + /* if we set it in _getcaps we should set it also in _link */ + ffmpegenc->context->strict_std_compliance = -1; + + /* user defined properties */ + ffmpegenc->context->bit_rate = ffmpegenc->bitrate; + ffmpegenc->context->bit_rate_tolerance = ffmpegenc->bitrate; + ffmpegenc->context->gop_size = ffmpegenc->gop_size; + ffmpegenc->context->me_method = ffmpegenc->me_method; + GST_DEBUG_OBJECT (ffmpegenc, "Setting avcontext to bitrate %lu, gop_size %d", + ffmpegenc->bitrate, ffmpegenc->gop_size); + + /* RTP payload used for GOB production (for Asterisk) */ + if (ffmpegenc->rtp_payload_size) { + ffmpegenc->context->rtp_payload_size = ffmpegenc->rtp_payload_size; + } + + /* additional avcodec settings */ + /* first fill in the majority by copying over */ + gst_ffmpeg_cfg_fill_context (ffmpegenc, ffmpegenc->context); + + /* then handle some special cases */ + ffmpegenc->context->lmin = (ffmpegenc->lmin * FF_QP2LAMBDA + 0.5); + ffmpegenc->context->lmax = (ffmpegenc->lmax * FF_QP2LAMBDA + 0.5); + + if (ffmpegenc->interlaced) { + ffmpegenc->context->flags |= + CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME; + ffmpegenc->picture->interlaced_frame = TRUE; + /* if this is not the case, a filter element should be used to swap fields */ + ffmpegenc->picture->top_field_first = TRUE; + } + + /* some other defaults */ + ffmpegenc->context->rc_strategy = 2; + ffmpegenc->context->b_frame_strategy = 0; + ffmpegenc->context->coder_type = 0; + ffmpegenc->context->context_model = 0; + ffmpegenc->context->scenechange_threshold = 0; + ffmpegenc->context->inter_threshold = 0; + + /* and last but not least the pass; CBR, 2-pass, etc */ + ffmpegenc->context->flags |= ffmpegenc->pass; + switch (ffmpegenc->pass) { + /* some additional action depends on type of pass */ + case CODEC_FLAG_QSCALE: + ffmpegenc->context->global_quality + = ffmpegenc->picture->quality = FF_QP2LAMBDA * ffmpegenc->quantizer; + break; + case CODEC_FLAG_PASS1: /* need to prepare a stats file */ + /* we don't close when changing caps, fingers crossed */ + if (!ffmpegenc->file) + ffmpegenc->file = g_fopen (ffmpegenc->filename, "w"); + if (!ffmpegenc->file) + goto open_file_err; + break; + case CODEC_FLAG_PASS2: + { /* need to read the whole stats file ! */ + gsize size; + + if (!g_file_get_contents (ffmpegenc->filename, + &ffmpegenc->context->stats_in, &size, NULL)) + goto file_read_err; + + break; + } + default: + break; + } + + GST_DEBUG_OBJECT (ffmpegenc, "Extracting common video information"); + /* fetch pix_fmt, fps, par, width, height... */ + gst_ffmpeg_videoinfo_to_context (&state->info, ffmpegenc->context); + + if ((oclass->in_plugin->id == CODEC_ID_MPEG4) + && (ffmpegenc->context->time_base.den > 65535)) { + /* MPEG4 Standards do not support time_base denominator greater than + * (1<<16) - 1 . We therefore scale them down. + * Agreed, it will not be the exact framerate... but the difference + * shouldn't be that noticeable */ + ffmpegenc->context->time_base.num = + (gint) gst_util_uint64_scale_int (ffmpegenc->context->time_base.num, + 65535, ffmpegenc->context->time_base.den); + ffmpegenc->context->time_base.den = 65535; + GST_LOG_OBJECT (ffmpegenc, "MPEG4 : scaled down framerate to %d / %d", + ffmpegenc->context->time_base.den, ffmpegenc->context->time_base.num); + } + + pix_fmt = ffmpegenc->context->pix_fmt; + + /* max-key-interval may need the framerate set above */ + if (ffmpegenc->max_key_interval) { + AVCodecContext *ctx; + + /* override gop-size */ + ctx = ffmpegenc->context; + ctx->gop_size = (ffmpegenc->max_key_interval < 0) ? + (-ffmpegenc->max_key_interval + * (ctx->time_base.den * ctx->ticks_per_frame / ctx->time_base.num)) + : ffmpegenc->max_key_interval; + } + + /* open codec */ + if (gst_ffmpeg_avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) + goto open_codec_fail; + + /* second pass stats buffer no longer needed */ + if (ffmpegenc->context->stats_in) + g_free (ffmpegenc->context->stats_in); + + /* is the colourspace correct? */ + if (pix_fmt != ffmpegenc->context->pix_fmt) + goto pix_fmt_err; + + /* we may have failed mapping caps to a pixfmt, + * and quite some codecs do not make up their own mind about that + * in any case, _NONE can never work out later on */ + if (pix_fmt == PIX_FMT_NONE) + goto bad_input_fmt; + + /* some codecs support more than one format, first auto-choose one */ + GST_DEBUG_OBJECT (ffmpegenc, "picking an output format ..."); + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + if (!allowed_caps) { + GST_DEBUG_OBJECT (ffmpegenc, "... but no peer, using template caps"); + /* we need to copy because get_allowed_caps returns a ref, and + * get_pad_template_caps doesn't */ + allowed_caps = + gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD + (encoder))); + } + GST_DEBUG_OBJECT (ffmpegenc, "chose caps %" GST_PTR_FORMAT, allowed_caps); + gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id, + oclass->in_plugin->type, allowed_caps, ffmpegenc->context); + + /* try to set this caps on the other side */ + other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id, + ffmpegenc->context, TRUE); + + if (!other_caps) + goto unsupported_codec; + + icaps = gst_caps_intersect (allowed_caps, other_caps); + gst_caps_unref (allowed_caps); + gst_caps_unref (other_caps); + if (gst_caps_is_empty (icaps)) { + gst_caps_unref (icaps); + return FALSE; + } + + if (gst_caps_get_size (icaps) > 1) { + GstCaps *newcaps; + + newcaps = + gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps, + 0)), NULL); + gst_caps_unref (icaps); + icaps = newcaps; + } + + /* Store input state and set output state */ + if (ffmpegenc->input_state) + gst_video_codec_state_unref (ffmpegenc->input_state); + ffmpegenc->input_state = gst_video_codec_state_ref (state); + + output_format = gst_video_encoder_set_output_state (encoder, icaps, state); + gst_video_codec_state_unref (output_format); + + /* success! */ + ffmpegenc->opened = TRUE; + + return TRUE; + + /* ERRORS */ +open_file_err: + { + GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, OPEN_WRITE, + (("Could not open file \"%s\" for writing."), ffmpegenc->filename), + GST_ERROR_SYSTEM); + return FALSE; + } +file_read_err: + { + GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, READ, + (("Could not get contents of file \"%s\"."), ffmpegenc->filename), + GST_ERROR_SYSTEM); + return FALSE; + } + +open_codec_fail: + { + if (ffmpegenc->context->priv_data) + gst_ffmpeg_avcodec_close (ffmpegenc->context); + if (ffmpegenc->context->stats_in) + g_free (ffmpegenc->context->stats_in); + GST_DEBUG_OBJECT (ffmpegenc, "ffenc_%s: Failed to open FFMPEG codec", + oclass->in_plugin->name); + return FALSE; + } + +pix_fmt_err: + { + gst_ffmpeg_avcodec_close (ffmpegenc->context); + GST_DEBUG_OBJECT (ffmpegenc, + "ffenc_%s: AV wants different colourspace (%d given, %d wanted)", + oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt); + return FALSE; + } + +bad_input_fmt: + { + GST_DEBUG_OBJECT (ffmpegenc, "ffenc_%s: Failed to determine input format", + oclass->in_plugin->name); + return FALSE; + } + +unsupported_codec: + { + gst_ffmpeg_avcodec_close (ffmpegenc->context); + GST_DEBUG ("Unsupported codec - no caps found"); + return FALSE; + } +} + +static void +ffmpegenc_setup_working_buf (GstFFMpegVidEnc * ffmpegenc) +{ + guint wanted_size = + ffmpegenc->context->width * ffmpegenc->context->height * 6 + + FF_MIN_BUFFER_SIZE; + + /* Above is the buffer size used by ffmpeg/ffmpeg.c */ + + if (ffmpegenc->working_buf == NULL || + ffmpegenc->working_buf_size != wanted_size) { + if (ffmpegenc->working_buf) + g_free (ffmpegenc->working_buf); + ffmpegenc->working_buf_size = wanted_size; + ffmpegenc->working_buf = g_malloc (ffmpegenc->working_buf_size); + } + ffmpegenc->buffer_size = wanted_size; +} + +static GstFlowReturn +gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + GstBuffer *outbuf; + gint ret_size = 0, c; + GstVideoInfo *info = &ffmpegenc->input_state->info; + guint8 *data = GST_BUFFER_DATA (frame->input_buffer); + + if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) + ffmpegenc->picture->pict_type = FF_I_TYPE; + + /* Fill avpicture */ + for (c = 0; c < AV_NUM_DATA_POINTERS; c++) { + if (c < GST_VIDEO_INFO_N_COMPONENTS (info)) { + ffmpegenc->picture->data[c] = data + GST_VIDEO_INFO_COMP_OFFSET (info, c); + ffmpegenc->picture->linesize[c] = GST_VIDEO_INFO_COMP_STRIDE (info, c); + } else { + ffmpegenc->picture->data[c] = NULL; + ffmpegenc->picture->linesize[c] = 0; + } + } + + ffmpegenc->picture->pts = + gst_ffmpeg_time_gst_to_ff (frame->pts / + ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); + + ffmpegenc_setup_working_buf (ffmpegenc); + + ret_size = avcodec_encode_video (ffmpegenc->context, + ffmpegenc->working_buf, ffmpegenc->working_buf_size, ffmpegenc->picture); + + if (ret_size < 0) + goto encode_fail; + + /* Encoder needs more data */ + if (!ret_size) + return GST_FLOW_OK; + + /* save stats info if there is some as well as a stats file */ + if (ffmpegenc->file && ffmpegenc->context->stats_out) + if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) + GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, + (("Could not write to file \"%s\"."), ffmpegenc->filename), + GST_ERROR_SYSTEM); + + /* Get oldest frame */ + frame = gst_video_encoder_get_oldest_frame (encoder); + + /* Allocate output buffer */ + frame->output_buffer = outbuf = gst_buffer_new_and_alloc (ret_size); + memcpy (GST_BUFFER_DATA (outbuf), ffmpegenc->working_buf, ret_size); + + /* buggy codec may not set coded_frame */ + if (ffmpegenc->context->coded_frame) { + if (ffmpegenc->context->coded_frame->key_frame) + GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); + } else + GST_WARNING_OBJECT (ffmpegenc, "codec did not provide keyframe info"); + + /* Reset frame type */ + if (ffmpegenc->picture->pict_type) + ffmpegenc->picture->pict_type = 0; + + return gst_video_encoder_finish_frame (encoder, frame); + + /* ERRORS */ +encode_fail: + { +#ifndef GST_DISABLE_GST_DEBUG + GstFFMpegVidEncClass *oclass = + (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); + GST_ERROR_OBJECT (ffmpegenc, + "ffenc_%s: failed to encode buffer", oclass->in_plugin->name); +#endif /* GST_DISABLE_GST_DEBUG */ + return GST_FLOW_OK; + } +} + +static void +gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send) +{ + GstVideoCodecFrame *frame; + GstBuffer *outbuf; + gint ret_size; + + GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send); + + /* no need to empty codec if there is none */ + if (!ffmpegenc->opened) + return; + + while ((frame = + gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (ffmpegenc)))) { + + ffmpegenc_setup_working_buf (ffmpegenc); + + ret_size = avcodec_encode_video (ffmpegenc->context, + ffmpegenc->working_buf, ffmpegenc->working_buf_size, NULL); + + if (ret_size < 0) { /* there should be something, notify and give up */ +#ifndef GST_DISABLE_GST_DEBUG + GstFFMpegVidEncClass *oclass = + (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); + GST_WARNING_OBJECT (ffmpegenc, + "ffenc_%s: failed to flush buffer", oclass->in_plugin->name); +#endif /* GST_DISABLE_GST_DEBUG */ + break; + } + + /* save stats info if there is some as well as a stats file */ + if (ffmpegenc->file && ffmpegenc->context->stats_out) + if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) + GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, + (("Could not write to file \"%s\"."), ffmpegenc->filename), + GST_ERROR_SYSTEM); + + frame->output_buffer = outbuf = gst_buffer_new_and_alloc (ret_size); + memcpy (GST_BUFFER_DATA (outbuf), ffmpegenc->working_buf, ret_size); + + if (ffmpegenc->context->coded_frame->key_frame) + GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); + + gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); + } +} + + +static void +gst_ffmpegvidenc_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstFFMpegVidEnc *ffmpegenc; + + /* Get a pointer of the right type. */ + ffmpegenc = (GstFFMpegVidEnc *) (object); + + if (ffmpegenc->opened) { + GST_WARNING_OBJECT (ffmpegenc, + "Can't change properties once decoder is setup !"); + return; + } + + /* Check the argument id to see which argument we're setting. */ + switch (prop_id) { + case ARG_BIT_RATE: + ffmpegenc->bitrate = g_value_get_ulong (value); + break; + case ARG_GOP_SIZE: + ffmpegenc->gop_size = g_value_get_int (value); + break; + case ARG_ME_METHOD: + ffmpegenc->me_method = g_value_get_enum (value); + break; + case ARG_BUFSIZE: + break; + case ARG_RTP_PAYLOAD_SIZE: + ffmpegenc->rtp_payload_size = g_value_get_ulong (value); + break; + default: + if (!gst_ffmpeg_cfg_set_property (object, value, pspec)) + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* The set function is simply the inverse of the get fuction. */ +static void +gst_ffmpegvidenc_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstFFMpegVidEnc *ffmpegenc; + + /* It's not null if we got it, but it might not be ours */ + ffmpegenc = (GstFFMpegVidEnc *) (object); + + switch (prop_id) { + case ARG_BIT_RATE: + g_value_set_ulong (value, ffmpegenc->bitrate); + break; + case ARG_GOP_SIZE: + g_value_set_int (value, ffmpegenc->gop_size); + break; + case ARG_ME_METHOD: + g_value_set_enum (value, ffmpegenc->me_method); + break; + case ARG_BUFSIZE: + g_value_set_ulong (value, ffmpegenc->buffer_size); + break; + case ARG_RTP_PAYLOAD_SIZE: + g_value_set_ulong (value, ffmpegenc->rtp_payload_size); + break; + default: + if (!gst_ffmpeg_cfg_get_property (object, value, pspec)) + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_ffmpegvidenc_stop (GstVideoEncoder * encoder) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + + gst_ffmpegvidenc_flush_buffers (ffmpegenc, FALSE); + if (ffmpegenc->opened) { + gst_ffmpeg_avcodec_close (ffmpegenc->context); + ffmpegenc->opened = FALSE; + } + if (ffmpegenc->file) { + fclose (ffmpegenc->file); + ffmpegenc->file = NULL; + } + if (ffmpegenc->working_buf) { + g_free (ffmpegenc->working_buf); + ffmpegenc->working_buf = NULL; + } + + return TRUE; +} + +static GstFlowReturn +gst_ffmpegvidenc_finish (GstVideoEncoder * encoder) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + + gst_ffmpegvidenc_flush_buffers (ffmpegenc, TRUE); + + return GST_FLOW_OK; +} + +gboolean +gst_ffmpegvidenc_register (GstPlugin * plugin) +{ + GTypeInfo typeinfo = { + sizeof (GstFFMpegVidEncClass), + (GBaseInitFunc) gst_ffmpegvidenc_base_init, + NULL, + (GClassInitFunc) gst_ffmpegvidenc_class_init, + NULL, + NULL, + sizeof (GstFFMpegVidEnc), + 0, + (GInstanceInitFunc) gst_ffmpegvidenc_init, + }; + GType type; + AVCodec *in_plugin; + + + GST_LOG ("Registering encoders"); + + /* build global ffmpeg param/property info */ + gst_ffmpeg_cfg_init (); + + in_plugin = av_codec_next (NULL); + while (in_plugin) { + gchar *type_name; + + /* Skip non-AV codecs */ + if (in_plugin->type != AVMEDIA_TYPE_VIDEO) + goto next; + + /* no quasi codecs, please */ + if (in_plugin->id == CODEC_ID_RAWVIDEO || + in_plugin->id == CODEC_ID_V210 || + in_plugin->id == CODEC_ID_V210X || + in_plugin->id == CODEC_ID_R210 || + in_plugin->id == CODEC_ID_ZLIB || + (in_plugin->id >= CODEC_ID_PCM_S16LE && + in_plugin->id <= CODEC_ID_PCM_BLURAY)) { + goto next; + } + + /* No encoders depending on external libraries (we don't build them, but + * people who build against an external ffmpeg might have them. + * We have native gstreamer plugins for all of those libraries anyway. */ + if (!strncmp (in_plugin->name, "lib", 3)) { + GST_DEBUG + ("Not using external library encoder %s. Use the gstreamer-native ones instead.", + in_plugin->name); + goto next; + } + + /* only encoders */ + if (!in_plugin->encode) { + goto next; + } + + /* FIXME : We should have a method to know cheaply whether we have a mapping + * for the given plugin or not */ + + GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name); + + /* no codecs for which we're GUARANTEED to have better alternatives */ + if (!strcmp (in_plugin->name, "gif")) { + GST_LOG ("Ignoring encoder %s", in_plugin->name); + goto next; + } + + /* construct the type */ + type_name = g_strdup_printf ("ffenc_%s", in_plugin->name); + + type = g_type_from_name (type_name); + + if (!type) { + + /* create the glib type now */ + type = + g_type_register_static (GST_TYPE_VIDEO_ENCODER, type_name, &typeinfo, + 0); + g_type_set_qdata (type, GST_FFENC_PARAMS_QDATA, (gpointer) in_plugin); + + { + static const GInterfaceInfo preset_info = { + NULL, + NULL, + NULL + }; + g_type_add_interface_static (type, GST_TYPE_PRESET, &preset_info); + } + } + + if (!gst_element_register (plugin, type_name, GST_RANK_SECONDARY, type)) { + g_free (type_name); + return FALSE; + } + + g_free (type_name); + + next: + in_plugin = av_codec_next (in_plugin); + } + + GST_LOG ("Finished registering encoders"); + + return TRUE; +} diff --git a/ext/ffmpeg/gstffmpegvidenc.h b/ext/ffmpeg/gstffmpegvidenc.h new file mode 100644 index 0000000000..1816623cf4 --- /dev/null +++ b/ext/ffmpeg/gstffmpegvidenc.h @@ -0,0 +1,95 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + +/* First, include the header file for the plugin, to bring in the + * object definition and other useful things. + */ + +#ifndef __GST_FFMPEGVIDENC_H__ +#define __GST_FFMPEGVIDENC_H__ + +G_BEGIN_DECLS + +#include + +typedef struct _GstFFMpegVidEnc GstFFMpegVidEnc; + +struct _GstFFMpegVidEnc +{ + GstVideoEncoder parent; + + GstVideoCodecState *input_state; + + AVCodecContext *context; + AVFrame *picture; + gboolean opened; + gboolean discont; + + /* cache */ + gulong bitrate; + gint me_method; + gint gop_size; + gulong buffer_size; + gulong rtp_payload_size; + + guint8 *working_buf; + gulong working_buf_size; + + /* settings with some special handling */ + guint pass; + gfloat quantizer; + gchar *filename; + guint lmin; + guint lmax; + gint max_key_interval; + gboolean interlaced; + + /* statistics file */ + FILE *file; + + /* other settings are copied over straight, + * include a context here, rather than copy-and-past it from avcodec.h */ + AVCodecContext config; +}; + +typedef struct _GstFFMpegVidEncClass GstFFMpegVidEncClass; + +struct _GstFFMpegVidEncClass +{ + GstVideoEncoderClass parent_class; + + AVCodec *in_plugin; + GstPadTemplate *srctempl, *sinktempl; + GstCaps *sinkcaps; +}; + +#define GST_TYPE_FFMPEGVIDENC \ + (gst_ffmpegvidenc_get_type()) +#define GST_FFMPEGVIDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGVIDENC,GstFFMpegVidEnc)) +#define GST_FFMPEGVIDENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGVIDENC,GstFFMpegVidEncClass)) +#define GST_IS_FFMPEGVIDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGVIDENC)) +#define GST_IS_FFMPEGVIDENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGVIDENC)) + +G_END_DECLS + +#endif /* __GST_FFMPEGVIDENC_H__ */