X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Favcodec%2Fvideo.c;h=c8b18f0707db10dadba910547a38f99183e0c6e6;hb=f73ef73e576a14deb50c879597a28c5bc00a4179;hp=9b42d478eb5cc8af99a8eae94892edfde52a3f0c;hpb=8f24e725ff9945975f6d7232477cff7f7a8e2cff;p=vlc diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c index 9b42d478eb..c8b18f0707 100644 --- a/modules/codec/avcodec/video.c +++ b/modules/codec/avcodec/video.c @@ -1,25 +1,25 @@ /***************************************************************************** - * video.c: video decoder using the ffmpeg library + * video.c: video decoder using the libavcodec library ***************************************************************************** - * Copyright (C) 1999-2001 the VideoLAN team + * Copyright (C) 1999-2001 VLC authors and VideoLAN * $Id$ * * Authors: Laurent Aimar * Gildas Bazin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -29,40 +29,19 @@ # include "config.h" #endif -#if defined(HAVE_LIBAVCODEC_AVCODEC_H) && defined(HAVE_AVCODEC_DXVA2) -# if _WIN32_WINNT < 0x600 -/* dxva2 needs Vista support */ -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x600 -# endif -#endif - #include #include #include #include +#include #include -/* ffmpeg header */ -#ifdef HAVE_LIBAVCODEC_AVCODEC_H -# include -# ifdef HAVE_AVCODEC_VAAPI -# include -# endif -# ifdef HAVE_AVCODEC_DXVA2 -# include -# endif -#elif defined(HAVE_FFMPEG_AVCODEC_H) -# include -#else -# include -#endif +#include +#include +#include #include "avcodec.h" #include "va.h" -#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2) -# define HAVE_AVCODEC_VA -#endif /***************************************************************************** * decoder_sys_t : decoder descriptor @@ -125,13 +104,10 @@ struct decoder_sys_t static void ffmpeg_InitCodec ( 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 * ); - -#ifdef HAVE_AVCODEC_VA static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *, const enum PixelFormat * ); -#endif +static void vlc_va_Delete( vlc_va_t * ); static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) { @@ -195,7 +171,7 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec, else if( p_context->time_base.num > 0 && p_context->time_base.den > 0 ) { p_dec->fmt_out.video.i_frame_rate = p_context->time_base.den; - p_dec->fmt_out.video.i_frame_rate_base = p_context->time_base.num; + p_dec->fmt_out.video.i_frame_rate_base = p_context->time_base.num * __MAX( p_context->ticks_per_frame, 1 ); } return decoder_NewPicture( p_dec ); @@ -234,36 +210,34 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, /* ***** Get configuration of ffmpeg plugin ***** */ p_sys->p_context->workaround_bugs = - var_InheritInteger( p_dec, "ffmpeg-workaround-bugs" ); -#if LIBAVCODEC_VERSION_MAJOR < 54 - p_sys->p_context->error_recognition = - var_InheritInteger( p_dec, "ffmpeg-error-resilience" ); -#else -# warning FIXME (moved to AVFormat) -#endif + var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); + p_sys->p_context->err_recognition = + var_InheritInteger( p_dec, "avcodec-error-resilience" ); if( var_CreateGetBool( p_dec, "grayscale" ) ) p_sys->p_context->flags |= CODEC_FLAG_GRAY; - i_val = var_CreateGetInteger( p_dec, "ffmpeg-vismv" ); + i_val = var_CreateGetInteger( p_dec, "avcodec-vismv" ); if( i_val ) p_sys->p_context->debug_mv = i_val; - i_val = var_CreateGetInteger( p_dec, "ffmpeg-lowres" ); - if( i_val > 0 && i_val <= 2 ) p_sys->p_context->lowres = i_val; - - i_val = var_CreateGetInteger( p_dec, "ffmpeg-skiploopfilter" ); + i_val = var_CreateGetInteger( p_dec, "avcodec-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" ) ) + if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) p_sys->p_context->flags2 |= CODEC_FLAG2_FAST; - /* ***** ffmpeg frame skipping ***** */ - p_sys->b_hurry_up = var_CreateGetBool( p_dec, "ffmpeg-hurry-up" ); +#if LIBAVCODEC_VERSION_CHECK( 54, 41, 0, 91, 100 ) + if( var_InheritBool( p_dec, "avcodec-ignorecrop" ) ) + p_sys->p_context->flags2 |= CODEC_FLAG2_IGNORE_CROP; +#endif + + /* ***** libavcodec frame skipping ***** */ + p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); - switch( var_CreateGetInteger( p_dec, "ffmpeg-skip-frame" ) ) + switch( var_CreateGetInteger( p_dec, "avcodec-skip-frame" ) ) { case -1: p_sys->p_context->skip_frame = AVDISCARD_NONE; @@ -286,7 +260,7 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, } p_sys->i_skip_frame = p_sys->p_context->skip_frame; - switch( var_CreateGetInteger( p_dec, "ffmpeg-skip-idct" ) ) + switch( var_CreateGetInteger( p_dec, "avcodec-skip-idct" ) ) { case -1: p_sys->p_context->skip_idct = AVDISCARD_NONE; @@ -309,18 +283,14 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, } p_sys->i_skip_idct = p_sys->p_context->skip_idct; - /* ***** ffmpeg direct rendering ***** */ + /* ***** libavcodec direct rendering ***** */ p_sys->b_direct_rendering = false; p_sys->i_direct_rendering_used = -1; - if( var_CreateGetBool( p_dec, "ffmpeg-dr" ) && + if( var_CreateGetBool( p_dec, "avcodec-dr" ) && (p_sys->p_codec->capabilities & CODEC_CAP_DR1) && /* No idea why ... but this fixes flickering on some TSCC streams */ - p_sys->i_codec_id != CODEC_ID_TSCC && p_sys->i_codec_id != CODEC_ID_CSCD && -#if (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 68, 2 ) ) && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT( 52, 100, 1 ) ) - /* avcodec native vp8 decode doesn't handle EMU_EDGE flag, and I - don't have idea howto implement fallback to libvpx decoder */ - p_sys->i_codec_id != CODEC_ID_VP8 && -#endif + p_sys->i_codec_id != AV_CODEC_ID_TSCC && p_sys->i_codec_id != AV_CODEC_ID_CSCD && + p_sys->i_codec_id != AV_CODEC_ID_CINEPAK && !p_sys->p_context->debug_mv ) { /* Some codecs set pix_fmt only after the 1st frame has been decoded, @@ -328,7 +298,7 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, p_sys->b_direct_rendering = true; } - /* ffmpeg doesn't properly release old pictures when frames are skipped */ + /* libavcodec doesn't properly release old pictures when frames are skipped */ //if( p_sys->b_hurry_up ) p_sys->b_direct_rendering = false; if( p_sys->b_direct_rendering ) { @@ -343,47 +313,49 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context, /* Always use our get_buffer wrapper so we can calculate the * PTS correctly */ p_sys->p_context->get_buffer = ffmpeg_GetFrameBuf; - p_sys->p_context->reget_buffer = ffmpeg_ReGetFrameBuf; + p_sys->p_context->reget_buffer = avcodec_default_reget_buffer; p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf; p_sys->p_context->opaque = p_dec; #ifdef HAVE_AVCODEC_MT - int i_thread_count = var_InheritInteger( p_dec, "ffmpeg-threads" ); + int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); if( i_thread_count <= 0 ) { i_thread_count = vlc_GetCPUCount(); if( i_thread_count > 1 ) i_thread_count++; + + //FIXME: take in count the decoding time + i_thread_count = __MIN( i_thread_count, 4 ); } 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; #endif -#ifdef HAVE_AVCODEC_VA - const bool b_use_hw = var_CreateGetBool( p_dec, "ffmpeg-hw" ); - if( b_use_hw && - (i_codec_id == CODEC_ID_MPEG1VIDEO || i_codec_id == CODEC_ID_MPEG2VIDEO || - i_codec_id == CODEC_ID_MPEG4 || - i_codec_id == CODEC_ID_H264 || - i_codec_id == CODEC_ID_VC1 || i_codec_id == CODEC_ID_WMV3) ) + 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) ) { -#ifdef HAVE_AVCODEC_MT +#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 ffmpeg-hw, disabled" ); + msg_Warn( p_dec, "threaded frame decoding is not compatible with libavcodec-hw, disabled" ); p_sys->p_context->thread_type &= ~FF_THREAD_FRAME; } if( ( p_sys->p_context->thread_type & FF_THREAD_SLICE ) && - ( i_codec_id == CODEC_ID_MPEG1VIDEO || i_codec_id == CODEC_ID_MPEG2VIDEO ) ) + ( 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 ffmpeg-hw, disabled" ); + msg_Warn( p_dec, "threaded slice decoding is not compatible with libavcodec-hw, disabled" ); p_sys->p_context->thread_type &= ~FF_THREAD_SLICE; } #endif p_sys->p_context->get_format = ffmpeg_GetFormat; } -#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; @@ -573,7 +545,7 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) /* * Do the actual decoding now */ - /* Don't forget that ffmpeg requires a little more bytes + /* Don't forget that libavcodec requires a little more bytes * that the real frame size */ if( p_block->i_buffer > 0 ) { @@ -595,24 +567,18 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) picture_t *p_pic; AVPacket pkt; - /* Set the PTS/DTS in the context reordered_opaque field */ - if( p_block->i_pts > VLC_TS_INVALID ) - p_context->reordered_opaque = (p_block->i_pts << 1) | 0; - else if( p_block->i_dts > VLC_TS_INVALID ) - p_context->reordered_opaque = (p_block->i_dts << 1) | 1; - else - p_context->reordered_opaque = INT64_MIN; - p_sys->p_ff_pic->reordered_opaque = p_context->reordered_opaque; - - /* Make sure we don't reuse the same timestamps twice */ - p_block->i_pts = - p_block->i_dts = VLC_TS_INVALID; - post_mt( p_sys ); av_init_packet( &pkt ); pkt.data = p_block->p_buffer; pkt.size = p_block->i_buffer; + pkt.pts = p_block->i_pts; + pkt.dts = p_block->i_dts; + + /* Make sure we don't reuse the same timestamps twice */ + p_block->i_pts = + p_block->i_dts = VLC_TS_INVALID; + i_used = avcodec_decode_video2( p_context, p_sys->p_ff_pic, &b_gotpicture, &pkt ); @@ -666,33 +632,11 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) } /* Compute the PTS */ - mtime_t i_pts = VLC_TS_INVALID; - if( p_sys->p_ff_pic->reordered_opaque != INT64_MIN ) - { - mtime_t i_ts = p_sys->p_ff_pic->reordered_opaque >> 1; - bool b_dts = p_sys->p_ff_pic->reordered_opaque & 1; - if( b_dts ) - { - if( !p_context->has_b_frames || - !p_sys->b_has_b_frames || - !p_sys->p_ff_pic->reference || - p_sys->i_pts <= VLC_TS_INVALID ) - i_pts = i_ts; - - /* Guess what ? The rules are different for Real Video :( */ - if( (p_dec->fmt_in.i_codec == VLC_CODEC_RV30 || - p_dec->fmt_in.i_codec == VLC_CODEC_RV40) && - p_sys->b_has_b_frames ) - { - i_pts = VLC_TS_INVALID; - if(p_sys->p_ff_pic->reference) i_pts = i_ts; - } - } - else - { - i_pts = i_ts; - } - } + mtime_t i_pts = + p_sys->p_ff_pic->pkt_pts; + if (i_pts <= VLC_TS_INVALID) + i_pts = p_sys->p_ff_pic->pkt_dts; + if( i_pts <= VLC_TS_INVALID ) i_pts = p_sys->i_pts; @@ -742,7 +686,7 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) if( !b_drawpicture || ( !p_sys->p_va && !p_sys->p_ff_pic->linesize[0] ) ) continue; - if( !p_sys->p_ff_pic->opaque ) + if( p_sys->p_va != NULL || p_sys->p_ff_pic->opaque == NULL ) { /* Get a new picture */ p_pic = ffmpeg_NewPictBuf( p_dec, p_context ); @@ -793,24 +737,6 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) p_pic->b_progressive = !p_sys->p_ff_pic->interlaced_frame; p_pic->b_top_field_first = p_sys->p_ff_pic->top_field_first; - p_pic->i_qstride = p_sys->p_ff_pic->qstride; - int i_mb_h = ( p_pic->format.i_height + 15 ) / 16; - p_pic->p_q = malloc( p_pic->i_qstride * i_mb_h ); - memcpy( p_pic->p_q, p_sys->p_ff_pic->qscale_table, - p_pic->i_qstride * i_mb_h ); - switch( p_sys->p_ff_pic->qscale_type ) - { - case FF_QSCALE_TYPE_MPEG1: - p_pic->i_qtype = QTYPE_MPEG1; - break; - case FF_QSCALE_TYPE_MPEG2: - p_pic->i_qtype = QTYPE_MPEG2; - break; - case FF_QSCALE_TYPE_H264: - p_pic->i_qtype = QTYPE_H264; - break; - } - return p_pic; } else @@ -841,13 +767,12 @@ void EndVideoDec( decoder_t *p_dec ) wait_mt( p_sys ); - if( p_sys->p_ff_pic ) av_free( p_sys->p_ff_pic ); + if( p_sys->p_ff_pic ) + av_free( p_sys->p_ff_pic ); if( p_sys->p_va ) - { vlc_va_Delete( p_sys->p_va ); - p_sys->p_va = NULL; - } + vlc_sem_destroy( &p_sys->sem_mt ); } @@ -861,7 +786,7 @@ static void ffmpeg_InitCodec( decoder_t *p_dec ) if( !i_size ) return; - if( p_sys->i_codec_id == CODEC_ID_SVQ3 ) + if( p_sys->i_codec_id == AV_CODEC_ID_SVQ3 ) { uint8_t *p; @@ -928,7 +853,7 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec, { vlc_va_Extract( p_sys->p_va, p_pic, p_ff_pic ); } - else if( TestFfmpegChroma( p_sys->p_context->pix_fmt, -1 ) == VLC_SUCCESS ) + else if( FindVlcChroma( p_sys->p_context->pix_fmt ) ) { int i_plane, i_size, i_line; uint8_t *p_dst, *p_src; @@ -945,7 +870,7 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec, for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines; i_line++ ) { - vlc_memcpy( p_dst, p_src, i_size ); + memcpy( p_dst, p_src, i_size ); p_src += i_src_stride; p_dst += i_dst_stride; } @@ -953,96 +878,74 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec, } else { - msg_Err( p_dec, "don't know how to convert chroma %i", - p_sys->p_context->pix_fmt ); + const char *name = av_get_pix_fmt_name( p_sys->p_context->pix_fmt ); + msg_Err( p_dec, "Unsupported decoded output format %d (%s)", + p_sys->p_context->pix_fmt, name ? name : "unknown" ); p_dec->b_error = 1; } } -/***************************************************************************** - * 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 ) +static int ffmpeg_va_GetFrameBuf( struct AVCodecContext *p_context, AVFrame *p_ff_pic ) { decoder_t *p_dec = (decoder_t *)p_context->opaque; decoder_sys_t *p_sys = p_dec->p_sys; - picture_t *p_pic; + vlc_va_t *p_va = p_sys->p_va; - /* */ - p_ff_pic->reordered_opaque = p_context->reordered_opaque; - p_ff_pic->opaque = NULL; - - if( p_sys->p_va ) + /* hwaccel_context is not present in old ffmpeg version */ + if( vlc_va_Setup( p_va, + &p_context->hwaccel_context, &p_dec->fmt_out.video.i_chroma, + p_context->width, p_context->height ) ) { -#ifdef HAVE_AVCODEC_VA - /* hwaccel_context is not present in old ffmpeg version */ - if( vlc_va_Setup( p_sys->p_va, - &p_context->hwaccel_context, &p_dec->fmt_out.video.i_chroma, - p_context->width, p_context->height ) ) - { - msg_Err( p_dec, "vlc_va_Setup failed" ); - return -1; - } -#else - assert(0); -#endif - - /* */ - p_ff_pic->type = FF_BUFFER_TYPE_USER; - -#if LIBAVCODEC_VERSION_MAJOR < 54 - p_ff_pic->age = 256*256*256*64; -#endif - - if( vlc_va_Get( p_sys->p_va, p_ff_pic ) ) - { - msg_Err( p_dec, "VaGrabSurface failed" ); - return -1; - } - return 0; + msg_Err( p_dec, "vlc_va_Setup failed" ); + return -1; } - else if( !p_sys->b_direct_rendering ) + + if( vlc_va_Get( p_va, p_ff_pic ) ) { - /* Not much to do in indirect rendering mode. */ - return avcodec_default_get_buffer( p_context, p_ff_pic ); + msg_Err( p_dec, "VaGrabSurface failed" ); + return -1; } - wait_mt( p_sys ); - /* Some codecs set pix_fmt only after the 1st frame has been decoded, - * so we need to check for direct rendering again. */ + p_ff_pic->type = FF_BUFFER_TYPE_USER; + return 0; +} + +static picture_t *ffmpeg_dr_GetFrameBuf(struct AVCodecContext *p_context) +{ + decoder_t *p_dec = (decoder_t *)p_context->opaque; + decoder_sys_t *p_sys = p_dec->p_sys; int i_width = p_context->width; int i_height = p_context->height; avcodec_align_dimensions( p_context, &i_width, &i_height ); - if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS || - p_context->pix_fmt == PIX_FMT_PAL8 ) + picture_t *p_pic = NULL; + if (GetVlcChroma(&p_dec->fmt_out.video, p_context->pix_fmt) != VLC_SUCCESS) + goto no_dr; + + if (p_context->pix_fmt == PIX_FMT_PAL8) goto no_dr; p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; - /* Get a new picture */ p_pic = ffmpeg_NewPictBuf( p_dec, p_context ); if( !p_pic ) goto no_dr; - bool b_compatible = true; + if( p_pic->p[0].i_pitch / p_pic->p[0].i_pixel_pitch < i_width || p_pic->p[0].i_lines < i_height ) - b_compatible = false; - for( int i = 0; i < p_pic->i_planes && b_compatible; i++ ) + goto no_dr; + + for( int i = 0; i < p_pic->i_planes; i++ ) { unsigned i_align; switch( p_sys->i_codec_id ) { - case CODEC_ID_SVQ1: - case CODEC_ID_VP5: - case CODEC_ID_VP6: - case CODEC_ID_VP6F: - case CODEC_ID_VP6A: + case AV_CODEC_ID_SVQ1: + case AV_CODEC_ID_VP5: + case AV_CODEC_ID_VP6: + case AV_CODEC_ID_VP6F: + case AV_CODEC_ID_VP6A: i_align = 16; break; default: @@ -1050,24 +953,71 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context, break; } if( p_pic->p[i].i_pitch % i_align ) - b_compatible = false; + goto no_dr; if( (intptr_t)p_pic->p[i].p_pixels % i_align ) - b_compatible = false; + goto no_dr; } - if( p_context->pix_fmt == PIX_FMT_YUV422P && b_compatible ) + + if( p_context->pix_fmt == PIX_FMT_YUV422P ) { if( 2 * p_pic->p[1].i_pitch != p_pic->p[0].i_pitch || 2 * p_pic->p[2].i_pitch != p_pic->p[0].i_pitch ) - b_compatible = false; + goto no_dr; } - if( !b_compatible ) - { + + return p_pic; + +no_dr: + if (p_pic) decoder_DeletePicture( p_dec, p_pic ); - goto no_dr; + + 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 ) +{ + decoder_t *p_dec = (decoder_t *)p_context->opaque; + decoder_sys_t *p_sys = p_dec->p_sys; + + /* */ + p_ff_pic->opaque = NULL; +#if ! LIBAVCODEC_VERSION_CHECK(54, 34, 0, 79, 101) + p_ff_pic->pkt_pts = p_context->pkt ? p_context->pkt->pts : AV_NOPTS_VALUE; +#endif +#if LIBAVCODEC_VERSION_MAJOR < 54 + p_ff_pic->age = 256*256*256*64; +#endif + + if( p_sys->p_va ) + return ffmpeg_va_GetFrameBuf(p_context, p_ff_pic); + + if( !p_sys->b_direct_rendering ) + return avcodec_default_get_buffer( p_context, p_ff_pic ); + + wait_mt( p_sys ); + /* Some codecs set pix_fmt only after the 1st frame has been decoded, + * so we need to check for direct rendering again. */ + + picture_t *p_pic = ffmpeg_dr_GetFrameBuf(p_context); + if (!p_pic) { + if( p_sys->i_direct_rendering_used != 0 ) + { + msg_Warn( p_dec, "disabling direct rendering" ); + p_sys->i_direct_rendering_used = 0; + } + + post_mt( p_sys ); + return avcodec_default_get_buffer( p_context, p_ff_pic ); } - if( p_sys->i_direct_rendering_used != 1 ) - { + if( p_sys->i_direct_rendering_used != 1 ) { msg_Dbg( p_dec, "using direct rendering" ); p_sys->i_direct_rendering_used = 1; } @@ -1086,28 +1036,8 @@ 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 LIBAVCODEC_VERSION_MAJOR < 54 - p_ff_pic->age = 256*256*256*64; -#endif - post_mt( p_sys ); return 0; - -no_dr: - if( p_sys->i_direct_rendering_used != 0 ) - { - msg_Warn( p_dec, "disabling direct rendering" ); - p_sys->i_direct_rendering_used = 0; - } - post_mt( p_sys ); - return avcodec_default_get_buffer( p_context, p_ff_pic ); -} -static int ffmpeg_ReGetFrameBuf( struct AVCodecContext *p_context, AVFrame *p_ff_pic ) -{ - p_ff_pic->reordered_opaque = p_context->reordered_opaque; - - /* We always use default reget function, it works perfectly fine */ - return avcodec_default_reget_buffer( p_context, p_ff_pic ); } static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context, @@ -1117,116 +1047,122 @@ static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context, decoder_sys_t *p_sys = p_dec->p_sys; if( p_sys->p_va ) - { vlc_va_Release( p_sys->p_va, p_ff_pic ); - } - else if( !p_ff_pic->opaque ) - { + else if( p_ff_pic->opaque ) + decoder_UnlinkPicture( p_dec, (picture_t*)p_ff_pic->opaque); + else if( p_ff_pic->type == FF_BUFFER_TYPE_INTERNAL ) /* We can end up here without the AVFrame being allocated by * avcodec_default_get_buffer() if VA is used and the frame is * released when the decoder is closed */ - if( p_ff_pic->type == FF_BUFFER_TYPE_INTERNAL ) - avcodec_default_release_buffer( p_context, p_ff_pic ); - } - else - { - picture_t *p_pic = (picture_t*)p_ff_pic->opaque; + avcodec_default_release_buffer( p_context, p_ff_pic ); - decoder_UnlinkPicture( p_dec, p_pic ); - } for( int i = 0; i < 4; i++ ) p_ff_pic->data[i] = NULL; } -#ifdef HAVE_AVCODEC_VA +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 ); +} + static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, const enum PixelFormat *pi_fmt ) { decoder_t *p_dec = p_context->opaque; decoder_sys_t *p_sys = p_dec->p_sys; + vlc_va_t *p_va = p_sys->p_va; - if( p_sys->p_va ) - { - vlc_va_Delete( p_sys->p_va ); - p_sys->p_va = NULL; - } + if( p_va != NULL ) + vlc_va_Delete( p_va ); + + /* Profile and level informations are needed now. + * TODO: avoid code duplication with avcodec.c */ + if( p_context->profile != FF_PROFILE_UNKNOWN) + p_dec->fmt_in.i_profile = p_context->profile; + if( p_context->level != FF_LEVEL_UNKNOWN) + p_dec->fmt_in.i_level = p_context->level; + + p_va = vlc_va_New( VLC_OBJECT(p_dec), p_sys->i_codec_id, &p_dec->fmt_in ); + if( p_va == NULL ) + goto end; /* Try too look for a supported hw acceleration */ - for( int i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ ) + for( size_t 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", -#ifdef HAVE_AVCODEC_DXVA2 - [PIX_FMT_DXVA2_VLD] = "PIX_FMT_DXVA2_VLD", -#endif - [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 ) - { - if( !var_InheritBool( p_dec, "xlib" ) ) - { - msg_Warn( p_dec, "Ignoring VA API" ); - continue; - } -#ifdef HAVE_AVCODEC_VAAPI - msg_Dbg( p_dec, "Trying VA API" ); - p_sys->p_va = vlc_va_NewVaapi( VLC_OBJECT(p_dec), p_sys->i_codec_id ); - if( !p_sys->p_va ) - msg_Warn( p_dec, "Failed to open VA API" ); -#else + 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; -#endif - } -#ifdef HAVE_AVCODEC_DXVA2 - if( pi_fmt[i] == PIX_FMT_DXVA2_VLD ) - { - msg_Dbg( p_dec, "Trying DXVA2" ); - p_sys->p_va = vlc_va_NewDxva2( VLC_OBJECT(p_dec), p_sys->i_codec_id ); - if( !p_sys->p_va ) - msg_Warn( p_dec, "Failed to open DXVA2" ); - } -#endif - if( p_sys->p_va && - p_context->width > 0 && p_context->height > 0 ) + /* We try to call vlc_va_Setup when possible to detect errors when + * possible (later is too late) */ + if( p_context->width > 0 && p_context->height > 0 + && vlc_va_Setup( p_va, &p_context->hwaccel_context, + &p_dec->fmt_out.video.i_chroma, + p_context->width, p_context->height ) ) { - /* We try to call vlc_va_Setup when possible to detect errors when - * possible (later is too late) */ - if( vlc_va_Setup( p_sys->p_va, - &p_context->hwaccel_context, - &p_dec->fmt_out.video.i_chroma, - p_context->width, p_context->height ) ) - { - msg_Err( p_dec, "vlc_va_Setup failed" ); - vlc_va_Delete( p_sys->p_va ); - p_sys->p_va = NULL; - } + msg_Err( p_dec, "acceleration setup failure" ); + break; } - if( p_sys->p_va ) - { - if( p_sys->p_va->description ) - msg_Info( p_dec, "Using %s for hardware decoding.", p_sys->p_va->description ); - - /* FIXME this will disabled direct rendering - * even if a new pixel format is renegociated - */ - p_sys->b_direct_rendering = false; - p_context->draw_horiz_band = NULL; - return pi_fmt[i]; - } + if( p_va->description ) + msg_Info( p_dec, "Using %s for hardware decoding.", + p_va->description ); + + /* FIXME this will disable direct rendering + * even if a new pixel format is renegotiated + */ + p_sys->b_direct_rendering = false; + p_sys->p_va = p_va; + p_context->draw_horiz_band = NULL; + 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 */ return avcodec_default_get_format( p_context, pi_fmt ); } -#endif -