2018-08-29 09:53:02 +00:00
/*
* This file is part of FFmpeg .
*
* FFmpeg 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 .
*
* FFmpeg 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <nppi.h>
# include <stdio.h>
# include <string.h>
# include "libavutil/common.h"
# include "libavutil/hwcontext.h"
# include "libavutil/hwcontext_cuda_internal.h"
2018-11-11 06:47:28 +00:00
# include "libavutil/cuda_check.h"
2018-08-29 09:53:02 +00:00
# include "libavutil/internal.h"
# include "libavutil/opt.h"
# include "libavutil/pixdesc.h"
# include "avfilter.h"
# include "formats.h"
# include "internal.h"
# include "video.h"
2018-11-11 06:47:28 +00:00
# define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, device_hwctx->internal->cuda_dl, x)
2018-08-29 09:53:02 +00:00
static const enum AVPixelFormat supported_formats [ ] = {
AV_PIX_FMT_YUV420P ,
AV_PIX_FMT_YUV444P
} ;
enum TransposeStage {
STAGE_ROTATE ,
STAGE_TRANSPOSE ,
STAGE_NB
} ;
enum Transpose {
NPP_TRANSPOSE_CCLOCK_FLIP = 0 ,
NPP_TRANSPOSE_CLOCK = 1 ,
NPP_TRANSPOSE_CCLOCK = 2 ,
NPP_TRANSPOSE_CLOCK_FLIP = 3
} ;
enum Passthrough {
NPP_TRANSPOSE_PT_TYPE_NONE = 0 ,
NPP_TRANSPOSE_PT_TYPE_LANDSCAPE ,
NPP_TRANSPOSE_PT_TYPE_PORTRAIT
} ;
typedef struct NPPTransposeStageContext {
int stage_needed ;
enum AVPixelFormat in_fmt ;
enum AVPixelFormat out_fmt ;
struct {
int width ;
int height ;
} planes_in [ 3 ] , planes_out [ 3 ] ;
AVBufferRef * frames_ctx ;
AVFrame * frame ;
} NPPTransposeStageContext ;
typedef struct NPPTransposeContext {
const AVClass * class ;
NPPTransposeStageContext stages [ STAGE_NB ] ;
AVFrame * tmp_frame ;
int passthrough ; ///< PassthroughType, landscape passthrough mode enabled
int dir ; ///< TransposeDir
} NPPTransposeContext ;
static int npptranspose_init ( AVFilterContext * ctx )
{
NPPTransposeContext * s = ctx - > priv ;
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( s - > stages ) ; i + + ) {
s - > stages [ i ] . frame = av_frame_alloc ( ) ;
if ( ! s - > stages [ i ] . frame )
return AVERROR ( ENOMEM ) ;
}
s - > tmp_frame = av_frame_alloc ( ) ;
if ( ! s - > tmp_frame )
return AVERROR ( ENOMEM ) ;
return 0 ;
}
static void npptranspose_uninit ( AVFilterContext * ctx )
{
NPPTransposeContext * s = ctx - > priv ;
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( s - > stages ) ; i + + ) {
av_frame_free ( & s - > stages [ i ] . frame ) ;
av_buffer_unref ( & s - > stages [ i ] . frames_ctx ) ;
}
av_frame_free ( & s - > tmp_frame ) ;
}
static int init_stage ( NPPTransposeStageContext * stage , AVBufferRef * device_ctx )
{
AVBufferRef * out_ref = NULL ;
AVHWFramesContext * out_ctx ;
int in_sw , in_sh , out_sw , out_sh ;
int ret , i ;
av_pix_fmt_get_chroma_sub_sample ( stage - > in_fmt , & in_sw , & in_sh ) ;
av_pix_fmt_get_chroma_sub_sample ( stage - > out_fmt , & out_sw , & out_sh ) ;
if ( ! stage - > planes_out [ 0 ] . width ) {
stage - > planes_out [ 0 ] . width = stage - > planes_in [ 0 ] . width ;
stage - > planes_out [ 0 ] . height = stage - > planes_in [ 0 ] . height ;
}
for ( i = 1 ; i < FF_ARRAY_ELEMS ( stage - > planes_in ) ; i + + ) {
stage - > planes_in [ i ] . width = stage - > planes_in [ 0 ] . width > > in_sw ;
stage - > planes_in [ i ] . height = stage - > planes_in [ 0 ] . height > > in_sh ;
stage - > planes_out [ i ] . width = stage - > planes_out [ 0 ] . width > > out_sw ;
stage - > planes_out [ i ] . height = stage - > planes_out [ 0 ] . height > > out_sh ;
}
out_ref = av_hwframe_ctx_alloc ( device_ctx ) ;
if ( ! out_ref )
return AVERROR ( ENOMEM ) ;
out_ctx = ( AVHWFramesContext * ) out_ref - > data ;
out_ctx - > format = AV_PIX_FMT_CUDA ;
out_ctx - > sw_format = stage - > out_fmt ;
out_ctx - > width = FFALIGN ( stage - > planes_out [ 0 ] . width , 32 ) ;
out_ctx - > height = FFALIGN ( stage - > planes_out [ 0 ] . height , 32 ) ;
ret = av_hwframe_ctx_init ( out_ref ) ;
if ( ret < 0 )
goto fail ;
av_frame_unref ( stage - > frame ) ;
ret = av_hwframe_get_buffer ( out_ref , stage - > frame , 0 ) ;
if ( ret < 0 )
goto fail ;
stage - > frame - > width = stage - > planes_out [ 0 ] . width ;
stage - > frame - > height = stage - > planes_out [ 0 ] . height ;
av_buffer_unref ( & stage - > frames_ctx ) ;
stage - > frames_ctx = out_ref ;
return 0 ;
fail :
av_buffer_unref ( & out_ref ) ;
return ret ;
}
static int format_is_supported ( enum AVPixelFormat fmt )
{
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( supported_formats ) ; i + + )
if ( supported_formats [ i ] = = fmt )
return 1 ;
return 0 ;
}
static int init_processing_chain ( AVFilterContext * ctx , int in_width , int in_height ,
int out_width , int out_height )
{
NPPTransposeContext * s = ctx - > priv ;
AVHWFramesContext * in_frames_ctx ;
enum AVPixelFormat format ;
int i , ret , last_stage = - 1 ;
int rot_width = out_width , rot_height = out_height ;
/* check that we have a hw context */
if ( ! ctx - > inputs [ 0 ] - > hw_frames_ctx ) {
av_log ( ctx , AV_LOG_ERROR , " No hw context provided on input \n " ) ;
return AVERROR ( EINVAL ) ;
}
in_frames_ctx = ( AVHWFramesContext * ) ctx - > inputs [ 0 ] - > hw_frames_ctx - > data ;
format = in_frames_ctx - > sw_format ;
if ( ! format_is_supported ( format ) ) {
av_log ( ctx , AV_LOG_ERROR , " Unsupported input format: %s \n " ,
av_get_pix_fmt_name ( format ) ) ;
return AVERROR ( ENOSYS ) ;
}
if ( s - > dir ! = NPP_TRANSPOSE_CCLOCK_FLIP ) {
s - > stages [ STAGE_ROTATE ] . stage_needed = 1 ;
}
if ( s - > dir = = NPP_TRANSPOSE_CCLOCK_FLIP | | s - > dir = = NPP_TRANSPOSE_CLOCK_FLIP ) {
s - > stages [ STAGE_TRANSPOSE ] . stage_needed = 1 ;
/* Rotating by 180° in case of clock_flip, or not at all for cclock_flip, so width/height unchanged by rotation */
rot_width = in_width ;
rot_height = in_height ;
}
s - > stages [ STAGE_ROTATE ] . in_fmt = format ;
s - > stages [ STAGE_ROTATE ] . out_fmt = format ;
s - > stages [ STAGE_ROTATE ] . planes_in [ 0 ] . width = in_width ;
s - > stages [ STAGE_ROTATE ] . planes_in [ 0 ] . height = in_height ;
s - > stages [ STAGE_ROTATE ] . planes_out [ 0 ] . width = rot_width ;
s - > stages [ STAGE_ROTATE ] . planes_out [ 0 ] . height = rot_height ;
s - > stages [ STAGE_TRANSPOSE ] . in_fmt = format ;
s - > stages [ STAGE_TRANSPOSE ] . out_fmt = format ;
s - > stages [ STAGE_TRANSPOSE ] . planes_in [ 0 ] . width = rot_width ;
s - > stages [ STAGE_TRANSPOSE ] . planes_in [ 0 ] . height = rot_height ;
s - > stages [ STAGE_TRANSPOSE ] . planes_out [ 0 ] . width = out_width ;
s - > stages [ STAGE_TRANSPOSE ] . planes_out [ 0 ] . height = out_height ;
/* init the hardware contexts */
for ( i = 0 ; i < FF_ARRAY_ELEMS ( s - > stages ) ; i + + ) {
if ( ! s - > stages [ i ] . stage_needed )
continue ;
ret = init_stage ( & s - > stages [ i ] , in_frames_ctx - > device_ref ) ;
if ( ret < 0 )
return ret ;
last_stage = i ;
}
if ( last_stage > = 0 ) {
ctx - > outputs [ 0 ] - > hw_frames_ctx = av_buffer_ref ( s - > stages [ last_stage ] . frames_ctx ) ;
} else {
ctx - > outputs [ 0 ] - > hw_frames_ctx = av_buffer_ref ( ctx - > inputs [ 0 ] - > hw_frames_ctx ) ;
s - > passthrough = 1 ;
}
if ( ! ctx - > outputs [ 0 ] - > hw_frames_ctx )
return AVERROR ( ENOMEM ) ;
return 0 ;
}
static int npptranspose_config_props ( AVFilterLink * outlink )
{
AVFilterContext * ctx = outlink - > src ;
AVFilterLink * inlink = ctx - > inputs [ 0 ] ;
NPPTransposeContext * s = ctx - > priv ;
int ret ;
if ( ( inlink - > w > = inlink - > h & & s - > passthrough = = NPP_TRANSPOSE_PT_TYPE_LANDSCAPE ) | |
( inlink - > w < = inlink - > h & & s - > passthrough = = NPP_TRANSPOSE_PT_TYPE_PORTRAIT ) )
{
if ( inlink - > hw_frames_ctx ) {
outlink - > hw_frames_ctx = av_buffer_ref ( inlink - > hw_frames_ctx ) ;
if ( ! outlink - > hw_frames_ctx )
return AVERROR ( ENOMEM ) ;
}
av_log ( ctx , AV_LOG_VERBOSE ,
" w:%d h:%d -> w:%d h:%d (passthrough mode) \n " ,
inlink - > w , inlink - > h , inlink - > w , inlink - > h ) ;
return 0 ;
} else {
s - > passthrough = NPP_TRANSPOSE_PT_TYPE_NONE ;
}
outlink - > w = inlink - > h ;
outlink - > h = inlink - > w ;
outlink - > sample_aspect_ratio = ( AVRational ) { inlink - > sample_aspect_ratio . den , inlink - > sample_aspect_ratio . num } ;
ret = init_processing_chain ( ctx , inlink - > w , inlink - > h , outlink - > w , outlink - > h ) ;
if ( ret < 0 )
return ret ;
av_log ( ctx , AV_LOG_VERBOSE , " w:%d h:%d -transpose-> w:%d h:%d \n " ,
inlink - > w , inlink - > h , outlink - > w , outlink - > h ) ;
return 0 ;
}
static int npptranspose_rotate ( AVFilterContext * ctx , NPPTransposeStageContext * stage ,
AVFrame * out , AVFrame * in )
{
NPPTransposeContext * s = ctx - > priv ;
NppStatus err ;
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( stage - > planes_in ) & & i < FF_ARRAY_ELEMS ( in - > data ) & & in - > data [ i ] ; i + + ) {
int iw = stage - > planes_in [ i ] . width ;
int ih = stage - > planes_in [ i ] . height ;
int ow = stage - > planes_out [ i ] . width ;
int oh = stage - > planes_out [ i ] . height ;
// nppRotate uses 0,0 as the rotation point
// need to shift the image accordingly after rotation
// need to substract 1 to get the correct coordinates
double angle = s - > dir = = NPP_TRANSPOSE_CLOCK ? - 90.0 : s - > dir = = NPP_TRANSPOSE_CCLOCK ? 90.0 : 180.0 ;
int shiftw = ( s - > dir = = NPP_TRANSPOSE_CLOCK | | s - > dir = = NPP_TRANSPOSE_CLOCK_FLIP ) ? ow - 1 : 0 ;
int shifth = ( s - > dir = = NPP_TRANSPOSE_CCLOCK | | s - > dir = = NPP_TRANSPOSE_CLOCK_FLIP ) ? oh - 1 : 0 ;
err = nppiRotate_8u_C1R ( in - > data [ i ] , ( NppiSize ) { iw , ih } ,
in - > linesize [ i ] , ( NppiRect ) { 0 , 0 , iw , ih } ,
out - > data [ i ] , out - > linesize [ i ] ,
( NppiRect ) { 0 , 0 , ow , oh } ,
angle , shiftw , shifth , NPPI_INTER_NN ) ;
if ( err ! = NPP_SUCCESS ) {
av_log ( ctx , AV_LOG_ERROR , " NPP rotate error: %d \n " , err ) ;
return AVERROR_UNKNOWN ;
}
}
return 0 ;
}
static int npptranspose_transpose ( AVFilterContext * ctx , NPPTransposeStageContext * stage ,
AVFrame * out , AVFrame * in )
{
NppStatus err ;
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( stage - > planes_in ) & & i < FF_ARRAY_ELEMS ( in - > data ) & & in - > data [ i ] ; i + + ) {
int iw = stage - > planes_in [ i ] . width ;
int ih = stage - > planes_in [ i ] . height ;
err = nppiTranspose_8u_C1R ( in - > data [ i ] , in - > linesize [ i ] ,
out - > data [ i ] , out - > linesize [ i ] ,
( NppiSize ) { iw , ih } ) ;
if ( err ! = NPP_SUCCESS ) {
av_log ( ctx , AV_LOG_ERROR , " NPP transpose error: %d \n " , err ) ;
return AVERROR_UNKNOWN ;
}
}
return 0 ;
}
static int ( * const npptranspose_process [ ] ) ( AVFilterContext * ctx , NPPTransposeStageContext * stage ,
AVFrame * out , AVFrame * in ) = {
[ STAGE_ROTATE ] = npptranspose_rotate ,
[ STAGE_TRANSPOSE ] = npptranspose_transpose
} ;
static int npptranspose_filter ( AVFilterContext * ctx , AVFrame * out , AVFrame * in )
{
NPPTransposeContext * s = ctx - > priv ;
AVFrame * src = in ;
int i , ret , last_stage = - 1 ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( s - > stages ) ; i + + ) {
if ( ! s - > stages [ i ] . stage_needed )
continue ;
ret = npptranspose_process [ i ] ( ctx , & s - > stages [ i ] , s - > stages [ i ] . frame , src ) ;
if ( ret < 0 )
return ret ;
src = s - > stages [ i ] . frame ;
last_stage = i ;
}
if ( last_stage < 0 )
return AVERROR_BUG ;
ret = av_hwframe_get_buffer ( src - > hw_frames_ctx , s - > tmp_frame , 0 ) ;
if ( ret < 0 )
return ret ;
av_frame_move_ref ( out , src ) ;
av_frame_move_ref ( src , s - > tmp_frame ) ;
ret = av_frame_copy_props ( out , in ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int npptranspose_filter_frame ( AVFilterLink * link , AVFrame * in )
{
AVFilterContext * ctx = link - > dst ;
NPPTransposeContext * s = ctx - > priv ;
AVFilterLink * outlink = ctx - > outputs [ 0 ] ;
AVHWFramesContext * frames_ctx = ( AVHWFramesContext * ) outlink - > hw_frames_ctx - > data ;
AVCUDADeviceContext * device_hwctx = frames_ctx - > device_ctx - > hwctx ;
AVFrame * out = NULL ;
CUcontext dummy ;
int ret = 0 ;
if ( s - > passthrough )
return ff_filter_frame ( outlink , in ) ;
out = av_frame_alloc ( ) ;
if ( ! out ) {
ret = AVERROR ( ENOMEM ) ;
goto fail ;
}
2018-11-11 06:47:28 +00:00
ret = CHECK_CU ( device_hwctx - > internal - > cuda_dl - > cuCtxPushCurrent ( device_hwctx - > cuda_ctx ) ) ;
if ( ret < 0 )
2018-08-29 09:53:02 +00:00
goto fail ;
ret = npptranspose_filter ( ctx , out , in ) ;
2018-11-11 06:47:28 +00:00
CHECK_CU ( device_hwctx - > internal - > cuda_dl - > cuCtxPopCurrent ( & dummy ) ) ;
2018-08-29 09:53:02 +00:00
if ( ret < 0 )
goto fail ;
av_frame_free ( & in ) ;
return ff_filter_frame ( outlink , out ) ;
fail :
av_frame_free ( & in ) ;
av_frame_free ( & out ) ;
return ret ;
}
# define OFFSET(x) offsetof(NPPTransposeContext, x)
# define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
static const AVOption options [ ] = {
{ " dir " , " set transpose direction " , OFFSET ( dir ) , AV_OPT_TYPE_INT , { . i64 = NPP_TRANSPOSE_CCLOCK_FLIP } , 0 , 3 , FLAGS , " dir " } ,
{ " cclock_flip " , " rotate counter-clockwise with vertical flip " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_CCLOCK_FLIP } , 0 , 0 , FLAGS , " dir " } ,
{ " clock " , " rotate clockwise " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_CLOCK } , 0 , 0 , FLAGS , " dir " } ,
{ " cclock " , " rotate counter-clockwise " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_CCLOCK } , 0 , 0 , FLAGS , " dir " } ,
{ " clock_flip " , " rotate clockwise with vertical flip " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_CLOCK_FLIP } , 0 , 0 , FLAGS , " dir " } ,
{ " passthrough " , " do not apply transposition if the input matches the specified geometry " , OFFSET ( passthrough ) , AV_OPT_TYPE_INT , { . i64 = NPP_TRANSPOSE_PT_TYPE_NONE } , 0 , 2 , FLAGS , " passthrough " } ,
{ " none " , " always apply transposition " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_PT_TYPE_NONE } , 0 , 0 , FLAGS , " passthrough " } ,
{ " landscape " , " preserve landscape geometry " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_PT_TYPE_LANDSCAPE } , 0 , 0 , FLAGS , " passthrough " } ,
{ " portrait " , " preserve portrait geometry " , 0 , AV_OPT_TYPE_CONST , { . i64 = NPP_TRANSPOSE_PT_TYPE_PORTRAIT } , 0 , 0 , FLAGS , " passthrough " } ,
{ NULL } ,
} ;
static const AVClass npptranspose_class = {
. class_name = " npptranspose " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
static const AVFilterPad npptranspose_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = npptranspose_filter_frame ,
} ,
} ;
static const AVFilterPad npptranspose_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = npptranspose_config_props ,
} ,
} ;
2021-04-19 16:33:56 +00:00
const AVFilter ff_vf_transpose_npp = {
2018-08-29 09:53:02 +00:00
. name = " transpose_npp " ,
. description = NULL_IF_CONFIG_SMALL ( " NVIDIA Performance Primitives video transpose " ) ,
. init = npptranspose_init ,
. uninit = npptranspose_uninit ,
. priv_size = sizeof ( NPPTransposeContext ) ,
. priv_class = & npptranspose_class ,
2021-08-12 11:05:31 +00:00
FILTER_INPUTS ( npptranspose_inputs ) ,
FILTER_OUTPUTS ( npptranspose_outputs ) ,
2021-09-27 20:46:18 +00:00
FILTER_SINGLE_PIXFMT ( AV_PIX_FMT_CUDA ) ,
2018-08-29 09:53:02 +00:00
. flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE ,
} ;