#include <vlc_codec.h>
#include <vlc_avcodec.h>
#include <vlc_cpu.h>
-#include <vlc_modules.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
*****************************************************************************/
static void ffmpeg_InitCodec ( decoder_t * );
static void ffmpeg_CopyPicture ( decoder_t *, picture_t *, AVFrame * );
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+static int lavc_GetFrame(struct AVCodecContext *, AVFrame *, int);
+#else
static int ffmpeg_GetFrameBuf ( struct AVCodecContext *, AVFrame * );
static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * );
+#endif
static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *,
const enum PixelFormat * );
-static void vlc_va_Delete( vlc_va_t * );
static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
{
msg_Dbg( p_dec, "direct rendering is disabled" );
}
+ p_sys->p_context->get_format = ffmpeg_GetFormat;
/* Always use our get_buffer wrapper so we can calculate the
* PTS correctly */
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+ p_sys->p_context->get_buffer2 = lavc_GetFrame;
+#else
p_sys->p_context->get_buffer = ffmpeg_GetFrameBuf;
p_sys->p_context->reget_buffer = avcodec_default_reget_buffer;
p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
+#endif
p_sys->p_context->opaque = p_dec;
#ifdef HAVE_AVCODEC_MT
i_thread_count = __MIN( i_thread_count, 16 );
msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
p_sys->p_context->thread_count = i_thread_count;
-
- if( i_codec_id == AV_CODEC_ID_MPEG4 )
- p_sys->p_context->thread_count = 1;
-
p_sys->p_context->thread_safe_callbacks = true;
-#endif
- char *hw = var_CreateGetString( p_dec, "avcodec-hw" ); /* FIXME */
- if( (hw == NULL || strcasecmp( hw, "none" )) &&
- (i_codec_id == AV_CODEC_ID_MPEG1VIDEO || i_codec_id == AV_CODEC_ID_MPEG2VIDEO ||
- i_codec_id == AV_CODEC_ID_MPEG4 || i_codec_id == AV_CODEC_ID_H263 ||
- i_codec_id == AV_CODEC_ID_H264 ||
- i_codec_id == AV_CODEC_ID_VC1 || i_codec_id == AV_CODEC_ID_WMV3) )
+ switch( i_codec_id )
{
-#if defined(HAVE_AVCODEC_MT) //&& LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)
- if( p_sys->p_context->thread_type & FF_THREAD_FRAME )
- {
- msg_Warn( p_dec, "threaded frame decoding is not compatible with avcodec-hw, disabled" );
- p_sys->p_context->thread_type &= ~FF_THREAD_FRAME;
- }
- if( ( p_sys->p_context->thread_type & FF_THREAD_SLICE ) &&
- ( i_codec_id == AV_CODEC_ID_MPEG1VIDEO || i_codec_id == AV_CODEC_ID_MPEG2VIDEO ) )
- {
- msg_Warn( p_dec, "threaded slice decoding is not compatible with avcodec-hw, disabled" );
+ case AV_CODEC_ID_MPEG4:
+ case AV_CODEC_ID_H263:
+ p_sys->p_context->thread_type = 0;
+ break;
+ case AV_CODEC_ID_MPEG1VIDEO:
+ case AV_CODEC_ID_MPEG2VIDEO:
p_sys->p_context->thread_type &= ~FF_THREAD_SLICE;
- }
-#endif
- p_sys->p_context->get_format = ffmpeg_GetFormat;
+ /* fall through */
+# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
+ case AV_CODEC_ID_H264:
+ case AV_CODEC_ID_VC1:
+ case AV_CODEC_ID_WMV3:
+ p_sys->p_context->thread_type &= ~FF_THREAD_FRAME;
+# endif
}
- free( hw );
-#ifdef HAVE_AVCODEC_MT
+
if( p_sys->p_context->thread_type & FF_THREAD_FRAME )
p_dec->i_extra_picture_buffers = 2 * p_sys->p_context->thread_count;
#endif
-
/* ***** misc init ***** */
p_sys->i_pts = VLC_TS_INVALID;
p_sys->b_has_b_frames = false;
block_Release( p_block );
return NULL;
}
- else if( i_used > p_block->i_buffer ||
+ else if( (unsigned)i_used > p_block->i_buffer ||
p_context->thread_count > 1 )
{
i_used = p_block->i_buffer;
}
}
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+typedef struct
+{
+ vlc_va_t *va;
+ AVFrame *frame;
+} lavc_hw_ref_t;
+
+static void lavc_va_ReleaseFrame(void *opaque, uint8_t *data)
+{
+ lavc_hw_ref_t *ref = opaque;
+
+ vlc_va_Release(ref->va, ref->frame);
+ free(ref);
+ (void) data;
+}
+
+static int lavc_va_GetFrame(struct AVCodecContext *ctx, AVFrame *frame)
+{
+ decoder_t *dec = ctx->opaque;
+ decoder_sys_t *sys = dec->p_sys;
+ vlc_va_t *va = sys->p_va;
+
+ if (vlc_va_Setup(va, &ctx->hwaccel_context, &dec->fmt_out.video.i_chroma,
+ ctx->coded_width, ctx->coded_height))
+ {
+ msg_Err(dec, "hardware acceleration setup failed");
+ return -1;
+ }
+ if (vlc_va_Get(va, frame))
+ {
+ msg_Err(dec, "hardware acceleration picture allocation failed");
+ return -1;
+ }
+
+ lavc_hw_ref_t *ref = malloc(sizeof (*ref));
+ if (unlikely(ref == NULL))
+ {
+ vlc_va_Release(va, frame);
+ return -1;
+ }
+ ref->va = va;
+ ref->frame = frame;
+
+ frame->buf[0] = av_buffer_create(frame->data[0], 0, lavc_va_ReleaseFrame,
+ ref, 0);
+ if (unlikely(frame->buf[0] == NULL))
+ {
+ lavc_va_ReleaseFrame(ref, frame->data[0]);
+ return -1;
+ }
+ return 0;
+}
+
+typedef struct
+{
+ decoder_t *decoder;
+ picture_t *picture;
+} lavc_pic_ref_t;
+
+static void lavc_dr_ReleaseFrame(void *opaque, uint8_t *data)
+{
+ lavc_pic_ref_t *ref = opaque;
+
+ decoder_UnlinkPicture(ref->decoder, ref->picture);
+ free(ref);
+ (void) data;
+}
+
+static picture_t *lavc_dr_GetFrame(struct AVCodecContext *ctx,
+ AVFrame *frame, unsigned flags)
+{
+ decoder_t *dec = (decoder_t *)ctx->opaque;
+
+ if (GetVlcChroma(&dec->fmt_out.video, ctx->pix_fmt) != VLC_SUCCESS)
+ return NULL;
+ dec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
+ if (ctx->pix_fmt == PIX_FMT_PAL8)
+ return NULL;
+
+ int width = frame->width;
+ int height = frame->height;
+ int aligns[AV_NUM_DATA_POINTERS];
+
+ avcodec_align_dimensions2(ctx, &width, &height, aligns);
+
+ picture_t *pic = ffmpeg_NewPictBuf(dec, ctx);
+ if (pic == NULL)
+ return NULL;
+
+ /* Check that the picture is suitable for libavcodec */
+ if (pic->p[0].i_pitch < width * pic->p[0].i_pixel_pitch
+ || pic->p[0].i_lines < height)
+ goto no_dr;
+
+ for (int i = 0; i < pic->i_planes; i++)
+ {
+ if (pic->p[i].i_pitch % aligns[i])
+ goto no_dr;
+ if (((uintptr_t)pic->p[i].p_pixels) % aligns[i])
+ goto no_dr;
+ }
+
+ /* Allocate buffer references */
+ for (int i = 0; i < pic->i_planes; i++)
+ {
+ lavc_pic_ref_t *ref = malloc(sizeof (*ref));
+ if (ref == NULL)
+ goto error;
+ ref->decoder = dec;
+ ref->picture = pic;
+ decoder_LinkPicture(dec, pic);
+
+ uint8_t *data = pic->p[i].p_pixels;
+ int size = pic->p[i].i_pitch * pic->p[i].i_lines;
+
+ frame->buf[i] = av_buffer_create(data, size, lavc_dr_ReleaseFrame,
+ ref, 0);
+ if (unlikely(frame->buf[i] == NULL))
+ {
+ lavc_dr_ReleaseFrame(ref, data);
+ goto error;
+ }
+ }
+ decoder_UnlinkPicture(dec, pic);
+ (void) flags;
+ return pic;
+error:
+ for (unsigned i = 0; frame->buf[i] != NULL; i++)
+ av_buffer_unref(&frame->buf[i]);
+no_dr:
+ decoder_DeletePicture(dec, pic);
+ return NULL;
+}
+
+/**
+ * Callback used by libavcodec to get a frame buffer.
+ *
+ * It is used for direct rendering as well as to get the right PTS for each
+ * decoded picture (even in indirect rendering mode).
+ */
+static int lavc_GetFrame(struct AVCodecContext *ctx, AVFrame *frame, int flags)
+{
+ decoder_t *dec = ctx->opaque;
+ decoder_sys_t *sys = dec->p_sys;
+ picture_t *pic;
+
+ for (unsigned i = 0; i < AV_NUM_DATA_POINTERS; i++)
+ {
+ frame->data[i] = NULL;
+ frame->linesize[i] = 0;
+ frame->buf[i] = NULL;
+ }
+
+ if (sys->p_va != NULL)
+ return lavc_va_GetFrame(ctx, frame);
+
+ frame->opaque = NULL;
+ if (!sys->b_direct_rendering)
+ return avcodec_default_get_buffer2(ctx, frame, flags);
+
+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
+ * so we need to check for direct rendering again. */
+ wait_mt(sys);
+ pic = lavc_dr_GetFrame(ctx, frame, flags);
+ if (pic == NULL)
+ {
+ if (sys->i_direct_rendering_used != 0)
+ {
+ msg_Warn(dec, "disabling direct rendering");
+ sys->i_direct_rendering_used = 0;
+ }
+ post_mt(sys);
+ return avcodec_default_get_buffer2(ctx, frame, flags);
+ }
+
+ if (sys->i_direct_rendering_used != 1)
+ {
+ msg_Dbg(dec, "enabling direct rendering");
+ sys->i_direct_rendering_used = 1;
+ }
+ post_mt(sys);
+
+ frame->opaque = pic;
+ static_assert(PICTURE_PLANE_MAX <= AV_NUM_DATA_POINTERS, "Oops!");
+ for (unsigned i = 0; i < PICTURE_PLANE_MAX; i++)
+ {
+ frame->data[i] = pic->p[i].p_pixels;
+ frame->linesize[i] = pic->p[i].i_pitch;
+ }
+ for (unsigned i = PICTURE_PLANE_MAX; i < AV_NUM_DATA_POINTERS; i++)
+ {
+ frame->data[i] = NULL;
+ frame->linesize[i] = 0;
+ }
+ return 0;
+}
+#else
static int ffmpeg_va_GetFrameBuf( struct AVCodecContext *p_context, AVFrame *p_ff_pic )
{
decoder_t *p_dec = (decoder_t *)p_context->opaque;
return NULL;
}
-/*****************************************************************************
- * ffmpeg_GetFrameBuf: callback used by ffmpeg to get a frame buffer.
- *****************************************************************************
- * It is used for direct rendering as well as to get the right PTS for each
- * decoded picture (even in indirect rendering mode).
- *****************************************************************************/
static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
AVFrame *p_ff_pic )
{
for( int i = 0; i < 4; i++ )
p_ff_pic->data[i] = NULL;
}
-
-static int ffmpeg_va_Start( void *func, va_list ap )
-{
- vlc_va_t *va = va_arg( ap, vlc_va_t * );
- int codec = va_arg( ap, int );
- const es_format_t *fmt = va_arg( ap, const es_format_t * );
- int (*open)( vlc_va_t *, int, const es_format_t * ) = func;
-
- return open( va, codec, fmt );
-}
-
-static vlc_va_t *vlc_va_New( vlc_object_t *parent, int codec_id,
- const es_format_t *fmt )
-{
- vlc_va_t *p_va = vlc_object_create( parent, sizeof( *p_va ) );
- if( unlikely(p_va == NULL) )
- return NULL;
-
- p_va->module = vlc_module_load( p_va, "hw decoder", "$avcodec-hw",
- true, ffmpeg_va_Start, p_va,
- codec_id, fmt );
- if( p_va->module == NULL )
- {
- vlc_object_release( p_va );
- p_va = NULL;
- }
- return p_va;
-}
-
-static void ffmpeg_va_Stop( void *func, va_list ap )
-{
- vlc_va_t *va = va_arg( ap, vlc_va_t * );
- void (*close)( vlc_va_t * ) = func;
-
- close( va );
-}
-
-static void vlc_va_Delete( vlc_va_t *va )
-{
- vlc_module_unload( va->module, ffmpeg_va_Stop, va );
- vlc_object_release( va );
-}
+#endif
static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
const enum PixelFormat *pi_fmt )
if( p_va != NULL )
vlc_va_Delete( p_va );
+ /* Enumerate available formats */
+ bool can_hwaccel = false;
+ for( size_t i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ )
+ {
+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]);
+ if (dsc == NULL)
+ continue;
+ bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
+
+ msg_Dbg( p_dec, "available %sware decoder output format %d (%s)",
+ hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name );
+ if (hwaccel)
+ can_hwaccel = true;
+ }
+
+ if (!can_hwaccel)
+ goto end;
+
/* Profile and level informations are needed now.
* TODO: avoid code duplication with avcodec.c */
if( p_context->profile != FF_PROFILE_UNKNOWN)
if( p_va == NULL )
goto end;
- /* Try too look for a supported hw acceleration */
for( size_t i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ )
{
- const char *name = av_get_pix_fmt_name(pi_fmt[i]);
- msg_Dbg( p_dec, "Available decoder output format %d (%s)",
- pi_fmt[i], name ? name : "unknown" );
if( p_va->pix_fmt != pi_fmt[i] )
continue;
return pi_fmt[i];
}
- msg_Err( p_dec, "acceleration not available" );
vlc_va_Delete( p_va );
- p_sys->p_va = NULL;
-
end:
/* Fallback to default behaviour */
+ p_sys->p_va = NULL;
return avcodec_default_get_format( p_context, pi_fmt );
}