X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Favcodec%2Fvideo.c;h=e1e468690df0f4b8579cda5c1a7640182419e68b;hb=097759016d089bb4bb17ea6d28ad3448f137ea5d;hp=5c63fa262fae3de4951768bebc89d2ba166f1368;hpb=4e797456664930fab817a373c20728e32239de55;p=vlc diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c index 5c63fa262f..e1e468690d 100644 --- a/modules/codec/avcodec/video.c +++ b/modules/codec/avcodec/video.c @@ -31,12 +31,16 @@ #include #include -#include #include /* BITMAPINFOHEADER */ +#include +#include /* ffmpeg header */ #ifdef HAVE_LIBAVCODEC_AVCODEC_H # include +# ifdef HAVE_AVCODEC_VAAPI +# include +# endif #elif defined(HAVE_FFMPEG_AVCODEC_H) # include #else @@ -44,6 +48,7 @@ #endif #include "avcodec.h" +#include "vaapi.h" /***************************************************************************** * decoder_sys_t : decoder descriptor @@ -58,10 +63,9 @@ struct decoder_sys_t mtime_t i_pts; AVFrame *p_ff_pic; - BITMAPINFOHEADER *p_format; /* for frame skipping algo */ - int b_hurry_up; + bool b_hurry_up; enum AVDiscard i_skip_frame; enum AVDiscard i_skip_idct; @@ -70,21 +74,21 @@ struct decoder_sys_t mtime_t i_late_frames_start; /* for direct rendering */ - int b_direct_rendering; + bool b_direct_rendering; bool b_has_b_frames; /* Hack to force display of still pictures */ bool b_first_frame; - int i_buffer_orig, i_buffer; - char *p_buffer_orig, *p_buffer; - /* */ AVPaletteControl palette; /* */ bool b_flush; + + /* VA API */ + vlc_va_t *p_va; }; /* FIXME (dummy palette for now) */ @@ -94,12 +98,18 @@ static const AVPaletteControl palette_control; * Local prototypes *****************************************************************************/ static void ffmpeg_InitCodec ( decoder_t * ); +static int ffmpeg_OpenCodec ( decoder_t * ); static void ffmpeg_CopyPicture ( decoder_t *, picture_t *, AVFrame * ); static int ffmpeg_GetFrameBuf ( struct AVCodecContext *, AVFrame * ); static int ffmpeg_ReGetFrameBuf( struct AVCodecContext *, AVFrame * ); static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * ); static void ffmpeg_NextPts( decoder_t * ); +#ifdef HAVE_AVCODEC_VAAPI +static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *, + const enum PixelFormat * ); +#endif + static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) { uint8_t *p = (uint8_t*)&fcc; @@ -114,7 +124,7 @@ static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec, AVCodecContext *p_context ) { - picture_t *p_pic; + decoder_sys_t *p_sys = p_dec->p_sys; p_dec->fmt_out.video.i_width = p_context->width; p_dec->fmt_out.video.i_height = p_context->height; @@ -124,10 +134,12 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec, return NULL; /* invalid display size */ } - if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) + if( !p_sys->p_va && GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) ) { - /* we are doomed, but not really, because most codecs set their pix_fmt much later */ - p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); + /* we are doomed, but not really, because most codecs set their pix_fmt + * much later + * FIXME does it make sense here ? */ + p_dec->fmt_out.video.i_chroma = VLC_CODEC_I420; } p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; @@ -151,8 +163,8 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec, } } - if( p_dec->fmt_out.video.i_frame_rate > 0 && - p_dec->fmt_out.video.i_frame_rate_base > 0 ) + if( p_dec->fmt_in.video.i_frame_rate > 0 && + p_dec->fmt_in.video.i_frame_rate_base > 0 ) { p_dec->fmt_out.video.i_frame_rate = p_dec->fmt_in.video.i_frame_rate; @@ -165,9 +177,7 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec, p_dec->fmt_out.video.i_frame_rate_base = p_context->time_base.num; } - p_pic = decoder_NewPicture( p_dec ); - - return p_pic; + return decoder_NewPicture( p_dec ); } /***************************************************************************** @@ -180,36 +190,30 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, AVCodec *p_codec, int i_codec_id, const char *psz_namecodec ) { decoder_sys_t *p_sys; - vlc_value_t val; + int i_val; /* Allocate the memory needed to store the decoder's structure */ - if( ( p_dec->p_sys = p_sys = - (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) - { + if( ( p_dec->p_sys = p_sys = calloc( 1, sizeof(decoder_sys_t) ) ) == NULL ) return VLC_ENOMEM; - } - memset( p_sys, 0, sizeof(decoder_sys_t) ); - p_dec->p_sys->p_context = p_context; - p_dec->p_sys->p_codec = p_codec; - p_dec->p_sys->i_codec_id = i_codec_id; - p_dec->p_sys->psz_namecodec = psz_namecodec; + p_codec->type = CODEC_TYPE_VIDEO; + p_context->codec_type = CODEC_TYPE_VIDEO; + p_context->codec_id = i_codec_id; + p_sys->p_context = p_context; + p_sys->p_codec = p_codec; + p_sys->i_codec_id = i_codec_id; + p_sys->psz_namecodec = psz_namecodec; p_sys->p_ff_pic = avcodec_alloc_frame(); + p_sys->b_delayed_open = true; + p_sys->p_va = NULL; /* ***** Fill p_context with init values ***** */ - p_sys->p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_codec ); - p_sys->p_context->width = p_dec->fmt_in.video.i_width; - p_sys->p_context->height = p_dec->fmt_in.video.i_height; -#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) - p_sys->p_context->bits_per_sample = p_dec->fmt_in.video.i_bits_per_pixel; -#else - p_sys->p_context->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; -#endif + p_sys->p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?: p_dec->fmt_in.i_codec ); /* ***** Get configuration of ffmpeg plugin ***** */ p_sys->p_context->workaround_bugs = config_GetInt( p_dec, "ffmpeg-workaround-bugs" ); -#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT( 52, 0, 0 ) p_sys->p_context->error_resilience = config_GetInt( p_dec, "ffmpeg-error-resilience" ); #else @@ -217,34 +221,28 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, config_GetInt( p_dec, "ffmpeg-error-resilience" ); #endif - var_Create( p_dec, "grayscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "grayscale", &val ); - if( val.b_bool ) p_sys->p_context->flags |= CODEC_FLAG_GRAY; + if( var_CreateGetBool( p_dec, "grayscale" ) ) + p_sys->p_context->flags |= CODEC_FLAG_GRAY; - var_Create( p_dec, "ffmpeg-vismv", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-vismv", &val ); - if( val.i_int ) p_sys->p_context->debug_mv = val.i_int; + i_val = var_CreateGetInteger( p_dec, "ffmpeg-vismv" ); + if( i_val ) p_sys->p_context->debug_mv = i_val; - var_Create( p_dec, "ffmpeg-lowres", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-lowres", &val ); - if( val.i_int > 0 && val.i_int <= 2 ) p_sys->p_context->lowres = val.i_int; + i_val = var_CreateGetInteger( p_dec, "ffmpeg-lowres" ); + if( i_val > 0 && i_val <= 2 ) p_sys->p_context->lowres = i_val; - var_Create( p_dec, "ffmpeg-skiploopfilter", - VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-skiploopfilter", &val ); - if( val.i_int > 0 ) p_sys->p_context->skip_loop_filter = AVDISCARD_NONREF; - if( val.i_int > 1 ) p_sys->p_context->skip_loop_filter = AVDISCARD_BIDIR; - if( val.i_int > 2 ) p_sys->p_context->skip_loop_filter = AVDISCARD_NONKEY; - if( val.i_int > 3 ) p_sys->p_context->skip_loop_filter = AVDISCARD_ALL; + i_val = var_CreateGetInteger( p_dec, "ffmpeg-skiploopfilter" ); + if( i_val >= 4 ) p_sys->p_context->skip_loop_filter = AVDISCARD_ALL; + else if( i_val == 3 ) p_sys->p_context->skip_loop_filter = AVDISCARD_NONKEY; + else if( i_val == 2 ) p_sys->p_context->skip_loop_filter = AVDISCARD_BIDIR; + else if( i_val == 1 ) p_sys->p_context->skip_loop_filter = AVDISCARD_NONREF; + + if( var_CreateGetBool( p_dec, "ffmpeg-fast" ) ) + p_sys->p_context->flags2 |= CODEC_FLAG2_FAST; /* ***** ffmpeg frame skipping ***** */ - var_Create( p_dec, "ffmpeg-hurry-up", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-hurry-up", &val ); - p_sys->b_hurry_up = val.b_bool; + p_sys->b_hurry_up = var_CreateGetBool( p_dec, "ffmpeg-hurry-up" ); - var_Create( p_dec, "ffmpeg-skip-frame", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-skip-frame", &val ); - switch( val.i_int ) + switch( var_CreateGetInteger( p_dec, "ffmpeg-skip-frame" ) ) { case -1: p_sys->p_context->skip_frame = AVDISCARD_NONE; @@ -267,9 +265,7 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, } p_sys->i_skip_frame = p_sys->p_context->skip_frame; - var_Create( p_dec, "ffmpeg-skip-idct", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-skip-idct", &val ); - switch( val.i_int ) + switch( var_CreateGetInteger( p_dec, "ffmpeg-skip-idct" ) ) { case -1: p_sys->p_context->skip_idct = AVDISCARD_NONE; @@ -293,23 +289,24 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, p_sys->i_skip_idct = p_sys->p_context->skip_idct; /* ***** ffmpeg direct rendering ***** */ - p_sys->b_direct_rendering = 0; - var_Create( p_dec, "ffmpeg-dr", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_dec, "ffmpeg-dr", &val ); - if( val.b_bool && (p_sys->p_codec->capabilities & CODEC_CAP_DR1) && + p_sys->b_direct_rendering = false; + if( var_CreateGetBool( p_dec, "ffmpeg-dr" ) && + (p_sys->p_codec->capabilities & CODEC_CAP_DR1) && /* Apparently direct rendering doesn't work with YUV422P */ p_sys->p_context->pix_fmt != PIX_FMT_YUV422P && /* H264 uses too many reference frames */ p_sys->i_codec_id != CODEC_ID_H264 && + /* No idea why ... but this fixes flickering on some TSCC streams */ + p_sys->i_codec_id != CODEC_ID_TSCC && !p_sys->p_context->debug_mv ) { /* Some codecs set pix_fmt only after the 1st frame has been decoded, * so we need to do another check in ffmpeg_GetFrameBuf() */ - p_sys->b_direct_rendering = 1; + p_sys->b_direct_rendering = true; } /* ffmpeg doesn't properly release old pictures when frames are skipped */ - //if( p_sys->b_hurry_up ) p_sys->b_direct_rendering = 0; + //if( p_sys->b_hurry_up ) p_sys->b_direct_rendering = false; if( p_sys->b_direct_rendering ) { msg_Dbg( p_dec, "using direct rendering" ); @@ -323,8 +320,10 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf; p_sys->p_context->opaque = p_dec; - /* ***** init this codec with special data ***** */ - ffmpeg_InitCodec( p_dec ); +#ifdef HAVE_AVCODEC_VAAPI + if( var_CreateGetBool( p_dec, "ffmpeg-hw" ) ) + p_sys->p_context->get_format = ffmpeg_GetFormat; +#endif /* ***** misc init ***** */ p_sys->input_pts = p_sys->input_dts = 0; @@ -333,21 +332,13 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, p_sys->b_first_frame = true; p_sys->b_flush = false; p_sys->i_late_frames = 0; - p_sys->i_buffer = 0; - p_sys->i_buffer_orig = 1; - p_sys->p_buffer_orig = p_sys->p_buffer = malloc( p_sys->i_buffer_orig ); - if( !p_sys->p_buffer_orig ) - { - free( p_sys ); - return VLC_ENOMEM; - } /* Set output properties */ p_dec->fmt_out.i_cat = VIDEO_ES; if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) { /* we are doomed. but not really, because most codecs set their pix_fmt later on */ - p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); + p_dec->fmt_out.i_codec = VLC_CODEC_I420; } p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; @@ -381,19 +372,17 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, p_sys->p_context->palctrl = &p_sys->palette; } + /* ***** init this codec with special data ***** */ + ffmpeg_InitCodec( p_dec ); + /* ***** Open the codec ***** */ - vlc_mutex_lock( &avcodec_lock ); - if( avcodec_open( p_sys->p_context, p_sys->p_codec ) < 0 ) + if( ffmpeg_OpenCodec( p_dec ) < 0 ) { - vlc_mutex_unlock( &avcodec_lock ); msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec ); - free( p_sys->p_buffer_orig ); + av_free( p_sys->p_ff_pic ); free( p_sys ); return VLC_EGENERIC; } - vlc_mutex_unlock( &avcodec_lock ); - msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec ); - return VLC_SUCCESS; } @@ -408,16 +397,28 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) int b_null_size = false; block_t *p_block; - if( !pp_block || !*pp_block ) return NULL; + if( !pp_block || !*pp_block ) + return NULL; if( !p_sys->p_context->extradata_size && p_dec->fmt_in.i_extra ) + { ffmpeg_InitCodec( p_dec ); + if( p_sys->b_delayed_open ) + { + if( ffmpeg_OpenCodec( p_dec ) ) + msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec ); + } + } p_block = *pp_block; + if( p_sys->b_delayed_open ) + { + block_Release( p_block ); + return NULL; + } if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { - p_sys->i_buffer = 0; p_sys->i_pts = 0; /* To make sure we recover properly */ p_sys->input_pts = p_sys->input_dts = 0; @@ -479,7 +480,6 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) * but break picture until a new I, and for mpeg4 ...*/ p_sys->i_late_frames--; /* needed else it will never be decrease */ block_Release( p_block ); - p_sys->i_buffer = 0; return NULL; } } @@ -499,6 +499,16 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) p_sys->p_context->skip_frame = p_sys->i_skip_frame; b_null_size = true; } + else if( !b_drawpicture ) + { + /* It creates broken picture + * FIXME either our parser or ffmpeg is broken */ +#if 0 + if( p_sys->b_hurry_up ) + p_sys->p_context->skip_frame = __MAX( p_sys->p_context->skip_frame, + AVDISCARD_NONREF ); +#endif + } /* * Do the actual decoding now @@ -510,37 +520,24 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) { p_sys->b_flush = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; - p_sys->i_buffer = p_block->i_buffer; - if( p_sys->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE > - p_sys->i_buffer_orig ) - { - free( p_sys->p_buffer_orig ); - p_sys->i_buffer_orig = - p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE; - p_sys->p_buffer_orig = malloc( p_sys->i_buffer_orig ); - } - p_sys->p_buffer = p_sys->p_buffer_orig; - p_sys->i_buffer = p_block->i_buffer; - if( !p_sys->p_buffer ) - { - block_Release( p_block ); + p_block = block_Realloc( p_block, 0, + p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); + if( !p_block ) return NULL; - } - vlc_memcpy( p_sys->p_buffer, p_block->p_buffer, p_block->i_buffer ); - memset( p_sys->p_buffer + p_block->i_buffer, 0, + p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; + *pp_block = p_block; + memset( p_block->p_buffer + p_block->i_buffer, 0, FF_INPUT_BUFFER_PADDING_SIZE ); - - p_block->i_buffer = 0; } - while( p_sys->i_buffer > 0 || p_sys->b_flush ) + while( p_block->i_buffer > 0 || p_sys->b_flush ) { int i_used, b_gotpicture; picture_t *p_pic; i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic, &b_gotpicture, - p_sys->i_buffer <= 0 && p_sys->b_flush ? NULL : (uint8_t*)p_sys->p_buffer, p_sys->i_buffer ); + p_block->i_buffer <= 0 && p_sys->b_flush ? NULL : p_block->p_buffer, p_block->i_buffer ); if( b_null_size && p_sys->p_context->width > 0 && p_sys->p_context->height > 0 && @@ -551,31 +548,32 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) if( p_sys->b_hurry_up ) p_sys->p_context->skip_frame = p_sys->i_skip_frame; i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic, - &b_gotpicture, - (uint8_t*)p_sys->p_buffer, p_sys->i_buffer ); + &b_gotpicture, p_block->p_buffer, + p_block->i_buffer ); } if( p_sys->b_flush ) p_sys->b_first_frame = true; - if( p_sys->i_buffer <= 0 ) + if( p_block->i_buffer <= 0 ) p_sys->b_flush = false; if( i_used < 0 ) { - msg_Warn( p_dec, "cannot decode one frame (%d bytes)", - p_sys->i_buffer ); + if( b_drawpicture ) + msg_Warn( p_dec, "cannot decode one frame (%zu bytes)", + p_block->i_buffer ); block_Release( p_block ); return NULL; } - else if( i_used > p_sys->i_buffer ) + else if( i_used > p_block->i_buffer ) { - i_used = p_sys->i_buffer; + i_used = p_block->i_buffer; } /* Consumed bytes */ - p_sys->i_buffer -= i_used; - p_sys->p_buffer += i_used; + p_block->i_buffer -= i_used; + p_block->p_buffer += i_used; /* Nothing to display */ if( !b_gotpicture ) @@ -604,7 +602,7 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) p_sys->i_late_frames = 0; } - if( !b_drawpicture || !p_sys->p_ff_pic->linesize[0] ) + if( !b_drawpicture || ( !p_sys->p_va && !p_sys->p_ff_pic->linesize[0] ) ) { /* Do not display the picture */ p_pic = (picture_t *)p_sys->p_ff_pic->opaque; @@ -717,8 +715,14 @@ void EndVideoDec( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; + /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ + if( p_sys->p_context->codec ) + avcodec_flush_buffers( p_sys->p_context ); + if( p_sys->p_ff_pic ) av_free( p_sys->p_ff_pic ); - free( p_sys->p_buffer_orig ); + + if( p_sys->p_va ) + VaDelete( p_sys->p_va ); } /***************************************************************************** @@ -770,25 +774,6 @@ static void ffmpeg_InitCodec( decoder_t *p_dec ) } } } - else if( p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '0' ) || - p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '3' ) || - p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '2', '0' ) ) - { - if( p_dec->fmt_in.i_extra == 8 ) - { - p_sys->p_context->extradata_size = 8; - p_sys->p_context->extradata = malloc( 8 ); - if( p_sys->p_context->extradata ) - { - memcpy( p_sys->p_context->extradata, - p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra ); - p_sys->p_context->sub_id = ((uint32_t*)p_dec->fmt_in.p_extra)[1]; - - msg_Warn( p_dec, "using extra data for RV codec sub_id=%08x", - p_sys->p_context->sub_id ); - } - } - } else { p_sys->p_context->extradata_size = i_size; @@ -804,6 +789,52 @@ static void ffmpeg_InitCodec( decoder_t *p_dec ) } } +/***************************************************************************** + * ffmpeg_OpenCodec: + *****************************************************************************/ +static int ffmpeg_OpenCodec( decoder_t *p_dec ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + + if( p_sys->p_context->extradata_size <= 0 ) + { + if( p_sys->i_codec_id == CODEC_ID_VC1 || + p_sys->i_codec_id == CODEC_ID_VORBIS || + p_sys->i_codec_id == CODEC_ID_THEORA ) + { + msg_Warn( p_dec, "waiting for extra data for codec %s", + p_sys->psz_namecodec ); + return 1; + } + } + p_sys->p_context->width = p_dec->fmt_in.video.i_width; + p_sys->p_context->height = p_dec->fmt_in.video.i_height; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 0, 0) + p_sys->p_context->bits_per_sample = p_dec->fmt_in.video.i_bits_per_pixel; +#else + p_sys->p_context->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; +#endif + + int ret; + vlc_avcodec_lock(); + ret = avcodec_open( p_sys->p_context, p_sys->p_codec ); + vlc_avcodec_unlock(); + if( ret < 0 ) + return VLC_EGENERIC; + msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec ); + + p_sys->b_delayed_open = false; + + if( p_sys->p_va ) + { + char psz_version[128]; + + VaVersion( p_sys->p_va, psz_version, sizeof(psz_version) ); + msg_Info( p_dec, "Using VA API version %s for hardware decoding.", psz_version ); + } + + return VLC_SUCCESS; +} /***************************************************************************** * ffmpeg_CopyPicture: copy a picture from ffmpeg internal buffers to a * picture_t structure (when not in direct rendering mode). @@ -813,7 +844,11 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec, { decoder_sys_t *p_sys = p_dec->p_sys; - if( TestFfmpegChroma( p_sys->p_context->pix_fmt, -1 ) == VLC_SUCCESS ) + if( p_sys->p_va ) + { + VaExtract( p_sys->p_va, p_pic, p_ff_pic ); + } + else if( TestFfmpegChroma( p_sys->p_context->pix_fmt, -1 ) == VLC_SUCCESS ) { int i_plane, i_size, i_line; uint8_t *p_dst, *p_src; @@ -863,11 +898,38 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context, ffmpeg_SetFrameBufferPts( p_dec, p_ff_pic ); /* */ - p_ff_pic->opaque = 0; + p_ff_pic->opaque = NULL; - /* Not much to do in indirect rendering mode */ - if( !p_sys->b_direct_rendering ) + if( p_sys->p_va ) { +#ifdef HAVE_AVCODEC_VAAPI + /* hwaccel_context is not present in old fffmpeg version */ + if( VaSetup( p_sys->p_va, + &p_sys->p_context->hwaccel_context, &p_dec->fmt_out.video.i_chroma, + p_sys->p_context->width, p_sys->p_context->height ) ) + { + msg_Err( p_dec, "VaSetup failed" ); + return -1; + } +#else + assert(0); +#endif + + /* */ + p_ff_pic->type = FF_BUFFER_TYPE_USER; + /* FIXME what is that, should give good value */ + p_ff_pic->age = 256*256*256*64; // FIXME FIXME from ffmpeg + + if( VaGrabSurface( p_sys->p_va, p_ff_pic ) ) + { + msg_Err( p_dec, "VaGrabSurface failed" ); + return -1; + } + return 0; + } + else if( !p_sys->b_direct_rendering ) + { + /* Not much to do in indirect rendering mode */ return avcodec_default_get_buffer( p_context, p_ff_pic ); } @@ -883,21 +945,15 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context, /* We only pad picture up to 16 */ PAD(p_sys->p_context->width,16) < i_width || PAD(p_sys->p_context->height,16) < i_height || p_context->pix_fmt == PIX_FMT_PAL8 ) - { - msg_Dbg( p_dec, "disabling direct rendering" ); - p_sys->b_direct_rendering = 0; return avcodec_default_get_buffer( p_context, p_ff_pic ); - } + p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; /* Get a new picture */ - //p_sys->p_vout->render.b_allow_modify_pics = 0; p_pic = ffmpeg_NewPictBuf( p_dec, p_sys->p_context ); if( !p_pic ) - { - p_sys->b_direct_rendering = 0; return avcodec_default_get_buffer( p_context, p_ff_pic ); - } + p_sys->p_context->draw_horiz_band = NULL; p_ff_pic->opaque = (void*)p_pic; @@ -912,10 +968,7 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context, p_ff_pic->linesize[2] = p_pic->p[2].i_pitch; p_ff_pic->linesize[3] = 0; - if( p_ff_pic->reference != 0 ) - { - decoder_LinkPicture( p_dec, p_pic ); - } + decoder_LinkPicture( p_dec, p_pic ); /* FIXME what is that, should give good value */ p_ff_pic->age = 256*256*256*64; // FIXME FIXME from ffmpeg @@ -979,24 +1032,29 @@ static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context, AVFrame *p_ff_pic ) { decoder_t *p_dec = (decoder_t *)p_context->opaque; - picture_t *p_pic; + decoder_sys_t *p_sys = p_dec->p_sys; - if( !p_ff_pic->opaque ) + if( p_sys->p_va ) + { + VaUngrabSurface( p_sys->p_va, p_ff_pic ); + + /* */ + for( int i = 0; i < 4; i++ ) + p_ff_pic->data[i] = NULL; + } + else if( !p_ff_pic->opaque ) { avcodec_default_release_buffer( p_context, p_ff_pic ); - return; } - - p_pic = (picture_t*)p_ff_pic->opaque; - - p_ff_pic->data[0] = NULL; - p_ff_pic->data[1] = NULL; - p_ff_pic->data[2] = NULL; - p_ff_pic->data[3] = NULL; - - if( p_ff_pic->reference != 0 ) + else { + picture_t *p_pic = (picture_t*)p_ff_pic->opaque; + decoder_UnlinkPicture( p_dec, p_pic ); + + /* */ + for( int i = 0; i < 4; i++ ) + p_ff_pic->data[i] = NULL; } } @@ -1018,9 +1076,69 @@ static void ffmpeg_NextPts( decoder_t *p_dec ) } else if( p_sys->p_context->time_base.den > 0 ) { +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,20,0) + int i_tick = p_sys->p_context->ticks_per_frame; + if( i_tick <= 0 ) + i_tick = 1; +#else + int i_tick = 1; +#endif + p_sys->i_pts += INT64_C(1000000) * (2 + p_sys->p_ff_pic->repeat_pict) * - p_sys->p_context->time_base.num / + i_tick * p_sys->p_context->time_base.num / (2 * p_sys->p_context->time_base.den); } } + +#ifdef HAVE_AVCODEC_VAAPI +static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_codec, + const enum PixelFormat *pi_fmt ) +{ + decoder_t *p_dec = p_codec->opaque; + decoder_sys_t *p_sys = p_dec->p_sys; + + if( p_sys->p_va ) + { + VaDelete( p_sys->p_va ); + p_sys->p_va = NULL; + } + + /* Try too look for a supported hw acceleration */ + for( int i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ ) + { + static const char *ppsz_name[PIX_FMT_NB] = { + [PIX_FMT_VDPAU_H264] = "PIX_FMT_VDPAU_H264", + [PIX_FMT_VAAPI_IDCT] = "PIX_FMT_VAAPI_IDCT", + [PIX_FMT_VAAPI_VLD] = "PIX_FMT_VAAPI_VLD", + [PIX_FMT_VAAPI_MOCO] = "PIX_FMT_VAAPI_MOCO", + [PIX_FMT_YUYV422] = "PIX_FMT_YUYV422", + [PIX_FMT_YUV420P] = "PIX_FMT_YUV420P", + }; + msg_Dbg( p_dec, "Available decoder output format %d (%s)", pi_fmt[i], ppsz_name[pi_fmt[i]] ?: "Unknown" ); + + /* Only VLD supported */ + if( pi_fmt[i] == PIX_FMT_VAAPI_VLD ) + { + msg_Dbg( p_dec, "Trying VA API" ); + p_sys->p_va = VaNew( p_sys->i_codec_id ); + if( p_sys->p_va ) + { + /* FIXME this will disabled direct rendering + * even if a new pixel format is renegociated + * + * FIXME Try to call VaSetup when possible + * to detect errors when possible (later is too late) */ + p_sys->b_direct_rendering = false; + p_sys->p_context->draw_horiz_band = NULL; + return pi_fmt[i]; + } + msg_Warn( p_dec, "Failed to open VA API" ); + } + } + + /* Fallback to default behaviour */ + return avcodec_default_get_format( p_codec, pi_fmt ); +} +#endif +