diff --git a/gst-libs/gst/vaapi/gstvaapidecoder.c b/gst-libs/gst/vaapi/gstvaapidecoder.c index 2c07c57496..d9e17ebdda 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder.c +++ b/gst-libs/gst/vaapi/gstvaapidecoder.c @@ -50,66 +50,59 @@ gst_vaapi_decoder_start(GstVaapiDecoder *decoder); static gboolean gst_vaapi_decoder_stop(GstVaapiDecoder *decoder); +static GstBuffer * +pop_buffer(GstVaapiDecoder *decoder); + static gpointer decoder_thread_cb(gpointer data) { GstVaapiDecoder * const decoder = data; GstVaapiDecoderPrivate * const priv = decoder->priv; GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder); - GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_SUCCESS; - - if (!klass->decode) { - g_error("unimplemented GstVaapiDecoder::decode() function"); - return NULL; - } + GstBuffer *buffer; + g_object_ref(decoder); while (!priv->decoder_thread_cancel) { - g_mutex_lock(priv->adapter_mutex); - while (!gst_adapter_available(priv->adapter)) { - g_cond_wait(priv->adapter_cond, priv->adapter_mutex); - if (priv->decoder_thread_cancel) - break; - } - g_mutex_unlock(priv->adapter_mutex); - - if (!priv->decoder_thread_cancel) { - switch (status) { - case GST_VAAPI_DECODER_STATUS_SUCCESS: - case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: - GST_DEBUG("decode"); - g_object_ref(decoder); - status = klass->decode(decoder); - g_object_unref(decoder); - - /* Detect End-of-Stream conditions */ - if (status != GST_VAAPI_DECODER_STATUS_SUCCESS && - priv->is_eos && - gst_vaapi_decoder_read_avail(decoder) == 0) - status = GST_VAAPI_DECODER_STATUS_END_OF_STREAM; - - GST_DEBUG("decode frame (status = %d)", status); - break; - default: - /* XXX: something went wrong, simply destroy any - buffer until this decoder is destroyed */ - g_mutex_lock(priv->adapter_mutex); - gst_adapter_clear(priv->adapter); - g_mutex_unlock(priv->adapter_mutex); - - /* Signal the main thread we got an error */ - if (status != GST_VAAPI_DECODER_STATUS_END_OF_STREAM) - gst_vaapi_decoder_push_surface(decoder, NULL); - break; - } - } - - /* End-of-Stream reached, decoder thread is no longer necessary */ - if (status == GST_VAAPI_DECODER_STATUS_END_OF_STREAM) + buffer = pop_buffer(decoder); + priv->decoder_status = klass->decode(decoder, buffer); + GST_DEBUG("decode frame (status = %d)", priv->decoder_status); + switch (priv->decoder_status) { + case GST_VAAPI_DECODER_STATUS_SUCCESS: + case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: break; + default: + priv->decoder_thread_cancel = TRUE; + break; + } + gst_buffer_unref(buffer); } + g_object_unref(decoder); return NULL; } +static inline void +init_buffer(GstBuffer *buffer, const guchar *buf, guint buf_size) +{ + GST_BUFFER_DATA(buffer) = (guint8 *)buf; + GST_BUFFER_SIZE(buffer) = buf_size; + GST_BUFFER_TIMESTAMP(buffer) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE; +} + +static inline GstBuffer * +create_eos_buffer(void) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new(); + if (!buffer) + return NULL; + + init_buffer(buffer, NULL, 0); + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_EOS); + return buffer; +} + static GstBuffer * create_buffer(const guchar *buf, guint buf_size, gboolean copy) { @@ -129,13 +122,9 @@ create_buffer(const guchar *buf, guint buf_size, gboolean copy) return NULL; } memcpy(buffer->malloc_data, buf, buf_size); - GST_BUFFER_DATA(buffer) = buffer->malloc_data; - GST_BUFFER_SIZE(buffer) = buf_size; - } - else { - GST_BUFFER_DATA(buffer) = (guint8 *)buf; - GST_BUFFER_SIZE(buffer) = buf_size; + buf = buffer->malloc_data; } + init_buffer(buffer, buf, buf_size); return buffer; } @@ -145,31 +134,42 @@ push_buffer(GstVaapiDecoder *decoder, GstBuffer *buffer) GstVaapiDecoderPrivate * const priv = decoder->priv; if (!buffer) { - priv->is_eos = TRUE; - return TRUE; + buffer = create_eos_buffer(); + if (!buffer) + return FALSE; } - g_return_val_if_fail(priv->adapter_mutex && priv->adapter_cond, FALSE); - GST_DEBUG("queue encoded data buffer %p (%d bytes)", buffer, GST_BUFFER_SIZE(buffer)); if (!priv->decoder_thread && !gst_vaapi_decoder_start(decoder)) return FALSE; - /* XXX: add a mechanism to wait for enough buffer bytes to be consumed */ - g_mutex_lock(priv->adapter_mutex); - gst_adapter_push(priv->adapter, buffer); - g_cond_signal(priv->adapter_cond); - g_mutex_unlock(priv->adapter_mutex); + g_async_queue_push(priv->buffers, buffer); return TRUE; } -static void -unref_surface_cb(gpointer surface, gpointer user_data) +static GstBuffer * +pop_buffer(GstVaapiDecoder *decoder) { - if (surface) - g_object_unref(GST_VAAPI_SURFACE(surface)); + GstVaapiDecoderPrivate * const priv = decoder->priv; + GstBuffer *buffer; + + buffer = g_async_queue_pop(priv->buffers); + g_return_val_if_fail(buffer, NULL); + + if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) + GST_BUFFER_TIMESTAMP(buffer) = priv->next_ts; + return buffer; +} + +static void +clear_async_queue(GAsyncQueue *q) +{ + guint i, qlen = g_async_queue_length(q); + + for (i = 0; i < qlen; i++) + g_object_unref(g_async_queue_pop(q)); } static void @@ -180,37 +180,21 @@ gst_vaapi_decoder_finalize(GObject *object) gst_vaapi_decoder_stop(decoder); - if (priv->adapter) { - gst_adapter_clear(priv->adapter); - g_object_unref(priv->adapter); - priv->adapter = NULL; - } - - if (priv->adapter_cond) { - g_cond_free(priv->adapter_cond); - priv->adapter_cond = NULL; - } - - if (priv->adapter_mutex) { - g_mutex_free(priv->adapter_mutex); - priv->adapter_mutex = NULL; - } - if (priv->context) { g_object_unref(priv->context); priv->context = NULL; } - g_queue_foreach(&priv->surfaces, unref_surface_cb, NULL); - - if (priv->surfaces_cond) { - g_cond_free(priv->surfaces_cond); - priv->surfaces_cond = NULL; + if (priv->buffers) { + clear_async_queue(priv->buffers); + g_object_unref(priv->buffers); + priv->buffers = NULL; } - if (priv->surfaces_mutex) { - g_mutex_free(priv->surfaces_mutex); - priv->surfaces_mutex = NULL; + if (priv->surfaces) { + clear_async_queue(priv->surfaces); + g_object_unref(priv->surfaces); + priv->surfaces = NULL; } if (priv->display) { @@ -310,16 +294,13 @@ gst_vaapi_decoder_init(GstVaapiDecoder *decoder) decoder->priv = priv; priv->context = NULL; priv->codec = 0; - priv->adapter = gst_adapter_new(); - priv->adapter_mutex = g_mutex_new(); - priv->adapter_cond = g_cond_new(); - priv->surfaces_mutex = g_mutex_new(); - priv->surfaces_cond = g_cond_new(); + priv->fps_n = 1000; + priv->fps_d = 30; + priv->next_ts = 0; + priv->buffers = g_async_queue_new(); + priv->surfaces = g_async_queue_new(); priv->decoder_thread = NULL; priv->decoder_thread_cancel = FALSE; - priv->is_eos = FALSE; - - g_queue_init(&priv->surfaces); } /** @@ -367,18 +348,62 @@ gst_vaapi_decoder_stop(GstVaapiDecoder *decoder) GstVaapiDecoderPrivate * const priv = decoder->priv; if (priv->decoder_thread) { + push_buffer(decoder, NULL); priv->decoder_thread_cancel = TRUE; - if (priv->adapter_mutex && priv->adapter_cond) { - g_mutex_lock(priv->adapter_mutex); - g_cond_signal(priv->adapter_cond); - g_mutex_unlock(priv->adapter_mutex); - } g_thread_join(priv->decoder_thread); priv->decoder_thread = NULL; } return TRUE; } +/** + * gst_vaapi_decoder_get_frame_rate: + * @decoder: a #GstVaapiDecoder + * @num: return location for the numerator of the frame rate + * @den: return location for the denominator of the frame rate + * + * Retrieves the current frame rate as the fraction @num / @den. The + * default frame rate is 30 fps. + */ +void +gst_vaapi_decoder_get_frame_rate( + GstVaapiDecoder *decoder, + guint *num, + guint *den +) +{ + g_return_if_fail(GST_VAAPI_IS_DECODER(decoder)); + + if (num) + *num = decoder->priv->fps_n; + + if (den) + *den = decoder->priv->fps_d; +} + +/** + * gst_vaapi_decoder_set_frame_rate: + * @decoder: a #GstVaapiDecoder + * @num: the numerator of the frame rate + * @den: the denominator of the frame rate + * + * Sets the frame rate for the stream to @num / @den. By default, the + * decoder will use the frame rate encoded in the elementary stream. + * If none is available, the decoder will default to 30 fps. + */ +void +gst_vaapi_decoder_set_frame_rate( + GstVaapiDecoder *decoder, + guint num, + guint den +) +{ + g_return_if_fail(GST_VAAPI_IS_DECODER(decoder)); + + decoder->priv->fps_n = num; + decoder->priv->fps_d = den; +} + /** * gst_vaapi_decoder_put_buffer_data: * @decoder: a #GstVaapiDecoder @@ -467,36 +492,35 @@ gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf) * or %NULL if none is available (e.g. an error). Caller owns the * returned object. g_object_unref() after usage. */ -static GstVaapiSurface * +static GstVaapiSurfaceProxy * _gst_vaapi_decoder_get_surface( GstVaapiDecoder *decoder, - GTimeVal *timeout, + GTimeVal *end_time, GstVaapiDecoderStatus *pstatus ) { GstVaapiDecoderPrivate * const priv = decoder->priv; + GstVaapiDecoderStatus status; GstVaapiSurface *surface; + GstVaapiSurfaceProxy *proxy = NULL; - g_mutex_lock(priv->surfaces_mutex); - while (g_queue_is_empty(&priv->surfaces)) - if (!g_cond_timed_wait(priv->surfaces_cond, priv->surfaces_mutex, timeout)) - break; - surface = g_queue_pop_head(&priv->surfaces); - g_mutex_unlock(priv->surfaces_mutex); + surface = g_async_queue_timed_pop(priv->surfaces, end_time); - if (surface) - *pstatus = GST_VAAPI_DECODER_STATUS_SUCCESS; - else { - g_mutex_lock(priv->adapter_mutex); - if (gst_adapter_available(priv->adapter)) - *pstatus = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; - else if (timeout) - *pstatus = GST_VAAPI_DECODER_STATUS_TIMEOUT; - else - *pstatus = GST_VAAPI_DECODER_STATUS_END_OF_STREAM; - g_mutex_unlock(priv->adapter_mutex); + if (surface) { + proxy = gst_vaapi_surface_proxy_new(priv->context, surface); + status = (proxy ? + GST_VAAPI_DECODER_STATUS_SUCCESS : + GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED); + g_object_unref(surface); } - return surface; + else if (end_time) + status = GST_VAAPI_DECODER_STATUS_TIMEOUT; + else + status = priv->decoder_status; + + if (pstatus) + *pstatus = status; + return proxy; } GstVaapiSurfaceProxy * @@ -505,23 +529,9 @@ gst_vaapi_decoder_get_surface( GstVaapiDecoderStatus *pstatus ) { - GstVaapiSurfaceProxy *proxy = NULL; - GstVaapiSurface *surface; - GstVaapiDecoderStatus status; - g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL); - surface = _gst_vaapi_decoder_get_surface(decoder, NULL, &status); - if (surface) { - proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface); - if (!proxy) - status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; - g_object_unref(surface); - } - - if (pstatus) - *pstatus = status; - return proxy; + return _gst_vaapi_decoder_get_surface(decoder, NULL, pstatus); } /** @@ -545,9 +555,6 @@ gst_vaapi_decoder_timed_get_surface( GstVaapiDecoderStatus *pstatus ) { - GstVaapiSurfaceProxy *proxy = NULL; - GstVaapiSurface *surface; - GstVaapiDecoderStatus status; GTimeVal end_time; g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL); @@ -555,17 +562,7 @@ gst_vaapi_decoder_timed_get_surface( g_get_current_time(&end_time); g_time_val_add(&end_time, timeout); - surface = _gst_vaapi_decoder_get_surface(decoder, &end_time, &status); - if (surface) { - proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface); - if (!proxy) - status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; - g_object_unref(surface); - } - - if (pstatus) - *pstatus = status; - return proxy; + return _gst_vaapi_decoder_get_surface(decoder, &end_time, pstatus); } gboolean @@ -593,73 +590,6 @@ gst_vaapi_decoder_ensure_context( return priv->context != NULL; } -guint -gst_vaapi_decoder_copy( - GstVaapiDecoder *decoder, - guint offset, - guchar *buf, - guint buf_size -) -{ - GstVaapiDecoderPrivate * const priv = decoder->priv; - guint avail; - - if (!buf || !buf_size) - return 0; - - avail = gst_vaapi_decoder_read_avail(decoder); - if (offset >= avail) - return 0; - if (buf_size > avail - offset) - buf_size = avail - offset; - - if (buf_size > 0) { - g_mutex_lock(priv->adapter_mutex); - gst_adapter_copy(priv->adapter, buf, offset, buf_size); - g_mutex_unlock(priv->adapter_mutex); - } - return buf_size; -} - -guint -gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder) -{ - GstVaapiDecoderPrivate * const priv = decoder->priv; - guint avail; - - g_mutex_lock(priv->adapter_mutex); - avail = gst_adapter_available(priv->adapter); - g_mutex_unlock(priv->adapter_mutex); - return avail; -} - -guint -gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size) -{ - buf_size = gst_vaapi_decoder_copy(decoder, 0, buf, buf_size); - if (buf_size > 0) - gst_vaapi_decoder_flush(decoder, buf_size); - return buf_size; -} - -void -gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size) -{ - GstVaapiDecoderPrivate * const priv = decoder->priv; - guint avail; - - if (!buf_size) - return; - - avail = gst_vaapi_decoder_read_avail(decoder); - if (buf_size > avail) - buf_size = avail; - - g_mutex_lock(priv->adapter_mutex); - gst_adapter_flush(priv->adapter, buf_size); - g_mutex_unlock(priv->adapter_mutex); -} - gboolean gst_vaapi_decoder_push_surface( GstVaapiDecoder *decoder, @@ -668,15 +598,11 @@ gst_vaapi_decoder_push_surface( { GstVaapiDecoderPrivate * const priv = decoder->priv; - if (surface) - GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT, - GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface))); - else - GST_DEBUG("queue null surface to signal an error"); + g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE); - g_mutex_lock(priv->surfaces_mutex); - g_queue_push_tail(&priv->surfaces, surface ? g_object_ref(surface) : NULL); - g_cond_signal(priv->surfaces_cond); - g_mutex_unlock(priv->surfaces_mutex); + GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT, + GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface))); + + g_async_queue_push(priv->surfaces, g_object_ref(surface)); return TRUE; } diff --git a/gst-libs/gst/vaapi/gstvaapidecoder.h b/gst-libs/gst/vaapi/gstvaapidecoder.h index 3a33db086c..4a96bb081a 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder.h +++ b/gst-libs/gst/vaapi/gstvaapidecoder.h @@ -102,12 +102,26 @@ struct _GstVaapiDecoderClass { /*< private >*/ GObjectClass parent_class; - GstVaapiDecoderStatus (*decode)(GstVaapiDecoder *decoder); + GstVaapiDecoderStatus (*decode)(GstVaapiDecoder *decoder, GstBuffer *buffer); }; GType gst_vaapi_decoder_get_type(void); +void +gst_vaapi_decoder_get_frame_rate( + GstVaapiDecoder *decoder, + guint *num, + guint *den +); + +void +gst_vaapi_decoder_set_frame_rate( + GstVaapiDecoder *decoder, + guint num, + guint den +); + gboolean gst_vaapi_decoder_put_buffer_data( GstVaapiDecoder *decoder, diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c index d83e94d556..7a6d6edc76 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c +++ b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c @@ -44,9 +44,6 @@ G_DEFINE_TYPE(GstVaapiDecoderFfmpeg, GST_VAAPI_TYPE_DECODER_FFMPEG, \ GstVaapiDecoderFfmpegPrivate)) -/** Default I/O buffer size (32 KB) */ -#define DEFAULT_IOBUF_SIZE (32 * 1024) - typedef struct _GstVaapiContextFfmpeg GstVaapiContextFfmpeg; struct _GstVaapiContextFfmpeg { struct vaapi_context base; @@ -54,16 +51,10 @@ struct _GstVaapiContextFfmpeg { }; struct _GstVaapiDecoderFfmpegPrivate { - AVPacket packet; AVFrame *frame; - guchar *iobuf; - guint iobuf_pos; - guint iobuf_size; - ByteIOContext ioctx; - AVFormatContext *fmtctx; + AVCodecParserContext *pctx; AVCodecContext *avctx; GstVaapiContextFfmpeg *vactx; - guint video_stream_index; guint is_constructed : 1; }; @@ -82,21 +73,6 @@ get_codec_id_from_codec(GstVaapiCodec codec) return CODEC_ID_NONE; } -/** Converts codec to FFmpeg raw bitstream format */ -static const gchar * -get_raw_format_from_codec(GstVaapiCodec codec) -{ - switch (codec) { - case GST_VAAPI_CODEC_MPEG1: return "mpegvideo"; - case GST_VAAPI_CODEC_MPEG2: return "mpegvideo"; - case GST_VAAPI_CODEC_MPEG4: return "m4v"; - case GST_VAAPI_CODEC_H263: return "h263"; - case GST_VAAPI_CODEC_H264: return "h264"; - case GST_VAAPI_CODEC_VC1: return "vc1"; - } - return NULL; -} - /** Finds a suitable profile from FFmpeg context */ static GstVaapiProfile get_profile(AVCodecContext *avctx) @@ -159,101 +135,6 @@ get_profile(AVCodecContext *avctx) return 0; } -/** Probes FFmpeg format from input stream */ -static AVInputFormat * -get_probed_format(GstVaapiDecoder *decoder) -{ - GstVaapiDecoderFfmpegPrivate * const priv = - GST_VAAPI_DECODER_FFMPEG(decoder)->priv; - - AVProbeData pd; - pd.filename = ""; - pd.buf = priv->iobuf; - pd.buf_size = MIN(gst_vaapi_decoder_read_avail(decoder), priv->iobuf_size); - if (!gst_vaapi_decoder_copy(decoder, 0, pd.buf, pd.buf_size)) - return FALSE; - - GST_DEBUG("probing format from buffer %p [%d bytes]", pd.buf, pd.buf_size); - return av_probe_input_format(&pd, 1); -} - -/** Tries to get an FFmpeg format from the raw bitstream */ -static AVInputFormat * -get_raw_format(GstVaapiDecoder *decoder) -{ - const gchar *raw_format; - - raw_format = get_raw_format_from_codec(GST_VAAPI_DECODER_CODEC(decoder)); - if (!raw_format) - return NULL; - - GST_DEBUG("trying raw format %s", raw_format); - return av_find_input_format(raw_format); -} - -/** Flushes n bytes from the stream */ -static void -stream_flush(GstVaapiDecoder *decoder, int buf_size) -{ - GstVaapiDecoderFfmpegPrivate * const priv = - GST_VAAPI_DECODER_FFMPEG(decoder)->priv; - - gst_vaapi_decoder_flush(decoder, buf_size); - if (priv->iobuf_pos > buf_size) - priv->iobuf_pos -= buf_size; - else - priv->iobuf_pos = 0; -} - -/** Reads one packet */ -static int -stream_read(void *opaque, uint8_t *buf, int buf_size) -{ - GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque); - GstVaapiDecoderFfmpegPrivate * const priv = - GST_VAAPI_DECODER_FFMPEG(decoder)->priv; - - if (buf_size > 0) { - buf_size = gst_vaapi_decoder_copy( - decoder, - priv->iobuf_pos, - buf, buf_size - ); - priv->iobuf_pos += buf_size; - } - return buf_size; -} - -/** Seeks into stream */ -static int64_t -stream_seek(void *opaque, int64_t offset, int whence) -{ - GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque); - GstVaapiDecoderFfmpegPrivate * const priv = - GST_VAAPI_DECODER_FFMPEG(decoder)->priv; - - /* If we parsed the headers (decoder is constructed), we can no - longer seek into the stream */ - if (priv->is_constructed && - !((whence == SEEK_SET || whence == SEEK_CUR) && offset == 0)) - return -1; - - switch (whence) { - case SEEK_SET: - priv->iobuf_pos = offset; - break; - case SEEK_CUR: - priv->iobuf_pos += offset; - break; - case SEEK_END: - priv->iobuf_pos = gst_vaapi_decoder_read_avail(decoder) + offset; - break; - default: - return -1; - } - return priv->iobuf_pos; -} - /** AVCodecContext.get_format() implementation */ static enum PixelFormat gst_vaapi_decoder_ffmpeg_get_format(AVCodecContext *avctx, const enum PixelFormat *fmt) @@ -347,29 +228,22 @@ gst_vaapi_decoder_ffmpeg_destroy(GstVaapiDecoderFfmpeg *ffdecoder) { GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv; - if (priv->avctx) { - avcodec_close(priv->avctx); - priv->avctx = NULL; - } - if (priv->vactx) { g_free(priv->vactx); priv->vactx = NULL; } - if (priv->fmtctx) { - av_close_input_stream(priv->fmtctx); - priv->fmtctx = NULL; + if (priv->avctx) { + avcodec_close(priv->avctx); + priv->avctx = NULL; } - if (priv->iobuf) { - g_free(priv->iobuf); - priv->iobuf = NULL; - priv->iobuf_pos = 0; + if (priv->pctx) { + av_parser_close(priv->pctx); + priv->pctx = NULL; } av_freep(&priv->frame); - av_free_packet(&priv->packet); } static gboolean @@ -378,13 +252,28 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder) GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(ffdecoder); GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv; GstVaapiCodec codec = GST_VAAPI_DECODER_CODEC(decoder); - enum CodecID codec_id = get_codec_id_from_codec(codec); - typedef AVInputFormat *(*GetFormatFunc)(GstVaapiDecoder *); - GetFormatFunc get_format[2]; - AVInputFormat *format; - AVStream *video_stream; + enum CodecID codec_id; AVCodec *ffcodec; - guint i; + + codec_id = get_codec_id_from_codec(codec); + if (codec_id == CODEC_ID_NONE) + return FALSE; + + ffcodec = avcodec_find_decoder(codec_id); + if (!ffcodec) + return FALSE; + + if (!priv->frame) { + priv->frame = avcodec_alloc_frame(); + if (!priv->frame) + return FALSE; + } + + if (!priv->avctx) { + priv->avctx = avcodec_alloc_context(); + if (!priv->avctx) + return FALSE; + } if (!priv->vactx) { priv->vactx = g_new(GstVaapiContextFfmpeg, 1); @@ -394,70 +283,6 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder) memset(&priv->vactx->base, 0, sizeof(priv->vactx->base)); priv->vactx->decoder = ffdecoder; - if (!priv->frame) { - priv->frame = avcodec_alloc_frame(); - if (!priv->frame) - return FALSE; - } - - if (!priv->iobuf) { - priv->iobuf = g_malloc0(priv->iobuf_size + FF_INPUT_BUFFER_PADDING_SIZE); - if (!priv->iobuf) - return FALSE; - } - - get_format[ !codec] = get_raw_format; - get_format[!!codec] = get_probed_format; - for (i = 0; i < 2; i++) { - format = get_format[i](decoder); - if (!format) - continue; - - priv->iobuf_pos = 0; - init_put_byte( - &priv->ioctx, - priv->iobuf, - priv->iobuf_size, - 0, /* write flags */ - ffdecoder, - stream_read, - NULL, /* no packet writer callback */ - stream_seek - ); - priv->ioctx.is_streamed = 1; - - if (av_open_input_stream(&priv->fmtctx, &priv->ioctx, "", format, NULL) < 0) - continue; - - if (av_find_stream_info(priv->fmtctx) >= 0) - break; - - av_close_input_stream(priv->fmtctx); - priv->fmtctx = NULL; - } - if (!priv->fmtctx) - return FALSE; - - if (av_find_stream_info(priv->fmtctx) < 0) - return FALSE; - dump_format(priv->fmtctx, 0, "", 0); - - video_stream = NULL; - for (i = 0; i < priv->fmtctx->nb_streams; i++) { - AVStream * const stream = priv->fmtctx->streams[i]; - if (!video_stream && - stream->codec->codec_type == CODEC_TYPE_VIDEO && - (codec ? (stream->codec->codec_id == codec_id) : 1)) { - video_stream = stream; - } - else - stream->discard = AVDISCARD_ALL; - } - if (!video_stream) - return FALSE; - - priv->video_stream_index = video_stream->index; - priv->avctx = video_stream->codec; priv->avctx->hwaccel_context = priv->vactx; priv->avctx->get_format = gst_vaapi_decoder_ffmpeg_get_format; priv->avctx->get_buffer = gst_vaapi_decoder_ffmpeg_get_buffer; @@ -467,20 +292,24 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder) priv->avctx->draw_horiz_band = NULL; priv->avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD; - ffcodec = avcodec_find_decoder(priv->avctx->codec_id); - if (!ffcodec || avcodec_open(priv->avctx, ffcodec) < 0) + if (priv->pctx) + av_parser_close(priv->pctx); + priv->pctx = av_parser_init(codec_id); + if (!priv->pctx) return FALSE; - av_init_packet(&priv->packet); + /* XXX: lock display? */ + if (avcodec_open(priv->avctx, ffcodec) < 0) + return FALSE; return TRUE; } -static GstVaapiSurface * +static GstVaapiDecoderStatus decode_frame(GstVaapiDecoderFfmpeg *ffdecoder, guchar *buf, guint buf_size) { GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv; GstVaapiDisplay * const display = GST_VAAPI_DECODER_DISPLAY(ffdecoder); - GstVaapiSurface *surface = NULL; + GstVaapiSurface *surface; int bytes_read, got_picture = 0; GST_VAAPI_DISPLAY_LOCK(display); @@ -491,51 +320,65 @@ decode_frame(GstVaapiDecoderFfmpeg *ffdecoder, guchar *buf, guint buf_size) buf, buf_size ); GST_VAAPI_DISPLAY_UNLOCK(display); + if (bytes_read < 0) + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + if (!got_picture) + return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; - if (bytes_read > 0) - stream_flush(GST_VAAPI_DECODER_CAST(ffdecoder), bytes_read); + surface = gst_vaapi_context_find_surface_by_id( + GST_VAAPI_DECODER_CONTEXT(ffdecoder), + GPOINTER_TO_UINT(priv->frame->data[3]) + ); + if (!surface) + return GST_VAAPI_DECODER_STATUS_ERROR_INVALID_SURFACE; - if (got_picture) { - surface = gst_vaapi_context_find_surface_by_id( - GST_VAAPI_DECODER_CONTEXT(ffdecoder), - GPOINTER_TO_UINT(priv->frame->data[3]) - ); - } - return surface; + gst_vaapi_decoder_push_surface(GST_VAAPI_DECODER_CAST(ffdecoder), surface); + return GST_VAAPI_DECODER_STATUS_SUCCESS; } GstVaapiDecoderStatus -gst_vaapi_decoder_ffmpeg_decode(GstVaapiDecoder *decoder) +gst_vaapi_decoder_ffmpeg_decode(GstVaapiDecoder *decoder, GstBuffer *buffer) { GstVaapiDecoderFfmpeg * const ffdecoder = GST_VAAPI_DECODER_FFMPEG(decoder); GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv; - GstVaapiSurface *surface = NULL; - AVPacket packet; + GstVaapiDecoderStatus status; + GstClockTime inbuf_ts; + guchar *inbuf, *outbuf; + gint inbuf_size, outbuf_size; + gboolean got_frame; if (!priv->is_constructed) { priv->is_constructed = gst_vaapi_decoder_ffmpeg_create(ffdecoder); - if (!priv->is_constructed) { - gst_vaapi_decoder_ffmpeg_destroy(ffdecoder); + if (!priv->is_constructed) return GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED; + } + + inbuf = GST_BUFFER_DATA(buffer); + inbuf_size = GST_BUFFER_SIZE(buffer); + inbuf_ts = GST_BUFFER_TIMESTAMP(buffer); + + do { + int parsed_size = av_parser_parse( + priv->pctx, + priv->avctx, + &outbuf, &outbuf_size, + inbuf, inbuf_size, + inbuf_ts, inbuf_ts + ); + got_frame = outbuf && outbuf_size > 0; + GST_DEBUG("outbuf %p (%d bytes), got frame %d, parsed size %d", + outbuf, outbuf_size, got_frame, parsed_size); + + if (parsed_size > 0) { + inbuf += parsed_size; + inbuf_size -= parsed_size; } - } + } while (!got_frame && inbuf_size > 0); - av_init_packet(&packet); - while (av_read_frame(priv->fmtctx, &packet) == 0) { - if (packet.stream_index != priv->video_stream_index) - continue; + if (!got_frame && !GST_BUFFER_IS_EOS(buffer)) + return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; - surface = decode_frame(ffdecoder, packet.data, packet.size); - if (surface) /* decode a single frame only */ - break; - } - if (!surface) - surface = decode_frame(ffdecoder, NULL, 0); - av_free_packet(&packet); - - if (surface && gst_vaapi_decoder_push_surface(decoder, surface)) - return GST_VAAPI_DECODER_STATUS_SUCCESS; - return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + return decode_frame(ffdecoder, outbuf, outbuf_size); } static void @@ -586,16 +429,10 @@ gst_vaapi_decoder_ffmpeg_init(GstVaapiDecoderFfmpeg *decoder) priv = GST_VAAPI_DECODER_FFMPEG_GET_PRIVATE(decoder); decoder->priv = priv; priv->frame = NULL; - priv->iobuf = NULL; - priv->iobuf_pos = 0; - priv->iobuf_size = DEFAULT_IOBUF_SIZE; - priv->fmtctx = NULL; + priv->pctx = NULL; priv->avctx = NULL; priv->vactx = NULL; - priv->video_stream_index = 0; priv->is_constructed = FALSE; - - av_init_packet(&priv->packet); } /** diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_priv.h b/gst-libs/gst/vaapi/gstvaapidecoder_priv.h index fed19005b4..ab1c6f1b17 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder_priv.h +++ b/gst-libs/gst/vaapi/gstvaapidecoder_priv.h @@ -63,16 +63,11 @@ G_BEGIN_DECLS #define GST_VAAPI_DECODER_CODEC(decoder) \ GST_VAAPI_DECODER_CAST(decoder)->priv->codec -/** - * GST_VAAPI_DECODER_IS_EOS: - * @decoder: a #GstVaapiDecoder - * - * Macro that checks if the @decoder reached an End-Of-Stream. - * This is an internal macro that does not do any run-time type check. - */ -#undef GST_VAAPI_DECODER_IS_EOS -#define GST_VAAPI_DECODER_IS_EOS(decoder) \ - GST_VAAPI_DECODER_CAST(decoder)->priv->is_eos +/* End-of-Stream buffer */ +#define GST_BUFFER_FLAG_EOS (GST_BUFFER_FLAG_LAST + 0) + +#define GST_BUFFER_IS_EOS(buffer) \ + GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_EOS) #define GST_VAAPI_DECODER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ @@ -80,18 +75,17 @@ G_BEGIN_DECLS GstVaapiDecoderPrivate)) struct _GstVaapiDecoderPrivate { - GstVaapiDisplay *display; - GstVaapiContext *context; - GstVaapiCodec codec; - GstAdapter *adapter; - GMutex *adapter_mutex; - GCond *adapter_cond; - GQueue surfaces; - GMutex *surfaces_mutex; - GCond *surfaces_cond; + GstVaapiDisplay *display; + GstVaapiContext *context; + GstVaapiCodec codec; + guint fps_n; + guint fps_d; + GstClockTime next_ts; + GAsyncQueue *buffers; + GAsyncQueue *surfaces; GThread *decoder_thread; + GstVaapiDecoderStatus decoder_status; guint decoder_thread_cancel : 1; - guint is_eos : 1; }; gboolean @@ -103,26 +97,6 @@ gst_vaapi_decoder_ensure_context( guint height ) attribute_hidden; -guint -gst_vaapi_decoder_copy( - GstVaapiDecoder *decoder, - guint offset, - guchar *buf, - guint buf_size -) attribute_hidden; - -guint -gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder) - attribute_hidden; - -guint -gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size) - attribute_hidden; - -void -gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size) - attribute_hidden; - gboolean gst_vaapi_decoder_push_surface( GstVaapiDecoder *decoder,