- Add PTS and framerate information.

- Simplify parsing with an AVCodeParserContext.
This commit is contained in:
gb 2010-04-27 15:26:19 +00:00 committed by Gwenole Beauchesne
parent 29f1784dd7
commit cc28cdbd60
4 changed files with 265 additions and 514 deletions

View file

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

View file

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

View file

@ -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);
}
/**

View file

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