@ -20,127 +20,74 @@
# include <stdlib.h>
# include "libavutil/dict.h"
# include "libavutil/hwcontext.h"
# include "libavutil/hwcontext_qsv.h"
# include "libavutil/mem.h"
# include "libavutil/opt.h"
# include "libavcodec/qsv.h"
# include "ffmpeg.h"
typedef struct QSVContext {
OutputStream * ost ;
mfxSession session ;
mfxExtOpaqueSurfaceAlloc opaque_alloc ;
AVBufferRef * opaque_surfaces_buf ;
uint8_t * surface_used ;
mfxFrameSurface1 * * surface_ptrs ;
int nb_surfaces ;
mfxExtBuffer * ext_buffers [ 1 ] ;
} QSVContext ;
static void buffer_release ( void * opaque , uint8_t * data )
{
* ( uint8_t * ) opaque = 0 ;
}
static int qsv_get_buffer ( AVCodecContext * s , AVFrame * frame , int flags )
{
InputStream * ist = s - > opaque ;
QSVContext * qsv = ist - > hwaccel_ctx ;
int i ;
for ( i = 0 ; i < qsv - > nb_surfaces ; i + + ) {
if ( qsv - > surface_used [ i ] )
continue ;
frame - > buf [ 0 ] = av_buffer_create ( ( uint8_t * ) qsv - > surface_ptrs [ i ] , sizeof ( * qsv - > surface_ptrs [ i ] ) ,
buffer_release , & qsv - > surface_used [ i ] , 0 ) ;
if ( ! frame - > buf [ 0 ] )
return AVERROR ( ENOMEM ) ;
frame - > data [ 3 ] = ( uint8_t * ) qsv - > surface_ptrs [ i ] ;
qsv - > surface_used [ i ] = 1 ;
return 0 ;
}
return AVERROR ( ENOMEM ) ;
}
static int init_opaque_surf ( QSVContext * qsv )
{
AVQSVContext * hwctx_enc = qsv - > ost - > enc_ctx - > hwaccel_context ;
mfxFrameSurface1 * surfaces ;
int i ;
qsv - > nb_surfaces = hwctx_enc - > nb_opaque_surfaces ;
qsv - > opaque_surfaces_buf = av_buffer_ref ( hwctx_enc - > opaque_surfaces ) ;
qsv - > surface_ptrs = av_mallocz_array ( qsv - > nb_surfaces , sizeof ( * qsv - > surface_ptrs ) ) ;
qsv - > surface_used = av_mallocz_array ( qsv - > nb_surfaces , sizeof ( * qsv - > surface_used ) ) ;
if ( ! qsv - > opaque_surfaces_buf | | ! qsv - > surface_ptrs | | ! qsv - > surface_used )
return AVERROR ( ENOMEM ) ;
surfaces = ( mfxFrameSurface1 * ) qsv - > opaque_surfaces_buf - > data ;
for ( i = 0 ; i < qsv - > nb_surfaces ; i + + )
qsv - > surface_ptrs [ i ] = surfaces + i ;
qsv - > opaque_alloc . Out . Surfaces = qsv - > surface_ptrs ;
qsv - > opaque_alloc . Out . NumSurface = qsv - > nb_surfaces ;
qsv - > opaque_alloc . Out . Type = hwctx_enc - > opaque_alloc_type ;
qsv - > opaque_alloc . Header . BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION ;
qsv - > opaque_alloc . Header . BufferSz = sizeof ( qsv - > opaque_alloc ) ;
qsv - > ext_buffers [ 0 ] = ( mfxExtBuffer * ) & qsv - > opaque_alloc ;
return 0 ;
return av_hwframe_get_buffer ( ist - > hw_frames_ctx , frame , 0 ) ;
}
static void qsv_uninit ( AVCodecContext * s )
{
InputStream * ist = s - > opaque ;
QSVContext * qsv = ist - > hwaccel_ctx ;
av_buffer_unref ( & ist - > hw_frames_ctx ) ;
}
av_freep ( & qsv - > ost - > enc_ctx - > hwaccel_context ) ;
av_freep ( & s - > hwaccel_context ) ;
static int qsv_device_init ( InputStream * ist )
{
int err ;
av_buffer_unref ( & qsv - > opaque_surfaces_buf ) ;
av_freep ( & qsv - > surface_used ) ;
av_freep ( & qsv - > surface_ptrs ) ;
err = av_hwdevice_ctx_create ( & hw_device_ctx , AV_HWDEVICE_TYPE_QSV ,
ist - > hwaccel_device , NULL , 0 ) ;
if ( err < 0 ) {
av_log ( NULL , AV_LOG_ERROR , " Error creating a QSV device \n " ) ;
return err ;
}
av_freep ( & qsv ) ;
return 0 ;
}
int qsv_init ( AVCodecContext * s )
{
InputStream * ist = s - > opaque ;
QSVContext * qsv = ist - > hwaccel _ctx;
AVQSVContext * hwctx_dec ;
AVHWFramesContext * frames_ctx ;
AVQSVFramesContext * frames_hwctx ;
int ret ;
if ( ! qsv ) {
av_log ( NULL , AV_LOG_ERROR , " QSV transcoding is not initialized. "
" -hwaccel qsv should only be used for one-to-one QSV transcoding "
" with no filters. \n " ) ;
return AVERROR_BUG ;
if ( ! hw_device_ctx ) {
ret = qsv_device_init ( ist ) ;
if ( ret < 0 )
return ret ;
}
ret = init_opaque_surf ( qsv ) ;
if ( ret < 0 )
return ret ;
hwctx_dec = av_qsv_alloc_context ( ) ;
if ( ! hwctx_dec )
av_buffer_unref ( & ist - > hw_frames_ctx ) ;
ist - > hw_frames_ctx = av_hwframe_ctx_alloc ( hw_device_ctx ) ;
if ( ! ist - > hw_frames_ctx )
return AVERROR ( ENOMEM ) ;
hwctx_dec - > session = qsv - > session ;
hwctx_dec - > iopattern = MFX_IOPATTERN_OUT_OPAQUE_MEMORY ;
hwctx_dec - > ext_buffers = qsv - > ext_buffers ;
hwctx_dec - > nb_ext_buffers = FF_ARRAY_ELEMS ( qsv - > ext_buffers ) ;
frames_ctx = ( AVHWFramesContext * ) ist - > hw_frames_ctx - > data ;
frames_hwctx = frames_ctx - > hwctx ;
frames_ctx - > width = FFALIGN ( s - > coded_width , 32 ) ;
frames_ctx - > height = FFALIGN ( s - > coded_height , 32 ) ;
frames_ctx - > format = AV_PIX_FMT_QSV ;
frames_ctx - > sw_format = s - > sw_pix_fmt ;
frames_ctx - > initial_pool_size = 64 ;
frames_hwctx - > frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET ;
av_freep ( & s - > hwaccel_context ) ;
s - > hwaccel_context = hwctx_dec ;
ret = av_hwframe_ctx_init ( ist - > hw_frames_ctx ) ;
if ( ret < 0 ) {
av_log ( NULL , AV_LOG_ERROR , " Error initializing a QSV frame pool \n " ) ;
return ret ;
}
ist - > hwaccel_get_buffer = qsv_get_buffer ;
ist - > hwaccel_uninit = qsv_uninit ;
@ -148,53 +95,15 @@ int qsv_init(AVCodecContext *s)
return 0 ;
}
static mfxIMPL choose_implementation ( const InputStream * ist )
{
static const struct {
const char * name ;
mfxIMPL impl ;
} impl_map [ ] = {
{ " auto " , MFX_IMPL_AUTO } ,
{ " sw " , MFX_IMPL_SOFTWARE } ,
{ " hw " , MFX_IMPL_HARDWARE } ,
{ " auto_any " , MFX_IMPL_AUTO_ANY } ,
{ " hw_any " , MFX_IMPL_HARDWARE_ANY } ,
{ " hw2 " , MFX_IMPL_HARDWARE2 } ,
{ " hw3 " , MFX_IMPL_HARDWARE3 } ,
{ " hw4 " , MFX_IMPL_HARDWARE4 } ,
} ;
mfxIMPL impl = MFX_IMPL_AUTO_ANY ;
int i ;
if ( ist - > hwaccel_device ) {
for ( i = 0 ; i < FF_ARRAY_ELEMS ( impl_map ) ; i + + )
if ( ! strcmp ( ist - > hwaccel_device , impl_map [ i ] . name ) ) {
impl = impl_map [ i ] . impl ;
break ;
}
if ( i = = FF_ARRAY_ELEMS ( impl_map ) )
impl = strtol ( ist - > hwaccel_device , NULL , 0 ) ;
}
return impl ;
}
int qsv_transcode_init ( OutputStream * ost )
{
InputStream * ist ;
const enum AVPixelFormat * pix_fmt ;
AVDictionaryEntry * e ;
const AVOption * opt ;
int flags = 0 ;
int err , i ;
QSVContext * qsv = NULL ;
AVQSVContext * hwctx = NULL ;
mfxIMPL impl ;
mfxVersion ver = { { 3 , 1 } } ;
AVBufferRef * encode_frames_ref = NULL ;
AVHWFramesContext * encode_frames ;
AVQSVFramesContext * qsv_frames ;
/* check if the encoder supports QSV */
if ( ! ost - > enc - > pix_fmts )
@ -225,43 +134,45 @@ int qsv_transcode_init(OutputStream *ost)
av_log ( NULL , AV_LOG_VERBOSE , " Setting up QSV transcoding \n " ) ;
qsv = av_mallocz ( sizeof ( * qsv ) ) ;
hwctx = av_qsv_alloc_context ( ) ;
if ( ! qsv | | ! hwctx )
goto fail ;
if ( ! hw_device_ctx ) {
err = qsv_device_init ( ist ) ;
if ( err < 0 )
goto fail ;
}
impl = choose_implementation ( ist ) ;
// This creates a dummy hw_frames_ctx for the encoder to be
// suitably initialised. It only contains one real frame, so
// hopefully doesn't waste too much memory.
err = MFXInit ( impl , & ver , & qsv - > session ) ;
if ( err ! = MFX_ERR_NONE ) {
av_log ( NULL , AV_LOG_ERROR , " Error initializing an MFX session: %d \n " , err ) ;
encode_frames_ref = av_hwframe_ctx_alloc ( hw_device_ctx ) ;
if ( ! encode_frames_ref ) {
err = AVERROR ( ENOMEM ) ;
goto fail ;
}
encode_frames = ( AVHWFramesContext * ) encode_frames_ref - > data ;
qsv_frames = encode_frames - > hwctx ;
e = av_dict_get ( ost - > encoder_opts , " flags " , NULL , 0 ) ;
opt = av_opt_find ( ost - > enc_ctx , " flags " , NULL , 0 , 0 ) ;
if ( e & & opt )
av_opt_eval_flags ( ost - > enc_ctx , opt , e - > value , & flags ) ;
encode_frames - > width = FFALIGN ( ist - > resample_width , 32 ) ;
encode_frames - > height = FFALIGN ( ist - > resample_height , 32 ) ;
encode_frames - > format = AV_PIX_FMT_QSV ;
encode_frames - > sw_format = AV_PIX_FMT_NV12 ;
encode_frames - > initial_pool_size = 1 ;
qsv - > ost = ost ;
qsv_frames - > frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET ;
hwctx - > session = qsv - > session ;
hwctx - > iopattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY ;
hwctx - > opaque_alloc = 1 ;
hwctx - > nb_opaque_surfaces = 16 ;
err = av_hwframe_ctx_init ( encode_frames_ref ) ;
if ( err < 0 )
goto fail ;
ost - > hwaccel_ctx = qsv ;
ost - > enc_ctx - > hwaccel_context = hwctx ;
ost - > enc_ctx - > pix_fmt = AV_PIX_FMT_QSV ;
ist - > dec_ctx - > pix_fmt = AV_PIX_FMT_QSV ;
ist - > resample_pix_fmt = AV_PIX_FMT_QSV ;
ist - > hwaccel_ctx = qsv ;
ist - > dec_ctx - > pix_fmt = AV_PIX_FMT_QSV ;
ist - > resample_pix_fmt = AV_PIX_FMT_QSV ;
ost - > enc_ctx - > pix_fmt = AV_PIX_FMT_QSV ;
ost - > enc_ctx - > hw_frames_ctx = encode_frames_ref ;
return 0 ;
fail :
av_freep ( & hwctx ) ;
av_freep ( & qsv ) ;
return AVERROR_UNKNOWN ;
av_buffer_unref ( & encode_frames_ref ) ;
return err ;
}