@ -1,9 +1,8 @@
/*
* AVISynth support
* Copyright ( c ) 2006 DivX , Inc .
*
* This file is part of FFmpeg .
* Avi / AvxSynth support
* Copyright ( c ) 2012 AvxSynth Team .
*
* 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
@ -21,214 +20,619 @@
# include "avformat.h"
# include "internal.h"
# include "riff.h"
# include "libavcodec/internal.h"
// Enable function pointer definitions for runtime loading.
# define AVSC_NO_DECLSPEC
// Shut up ffmpeg error messages.
// avisynth_c.h contains inline functions that call these functions.
# undef malloc
# undef free
# undef printf
// Platform-specific directives for AviSynth vs AvxSynth.
# ifdef _WIN32
# include <windows.h>
# undef EXTERN_C
# include "compat/avisynth/avisynth_c.h"
# define AVISYNTH_LIB "avisynth"
# else
# include <dlfcn.h>
# include "compat/avisynth/avxsynth_c.h"
# if defined (__APPLE__)
# define AVISYNTH_LIB "libavxsynth.dylib"
# else
# define AVISYNTH_LIB "libavxsynth.so"
# endif
# define LoadLibrary(x) dlopen(x, RTLD_NOW | RTLD_GLOBAL)
# define GetProcAddress dlsym
# define FreeLibrary dlclose
# endif
# include <windows.h>
# include <vfw.h>
// AvxSynth doesn't have these colorspaces, so disable them
# ifndef _WIN32
# define avs_is_yv24(vi) 0
# define avs_is_yv16(vi) 0
# define avs_is_yv411(vi) 0
# define avs_is_y8(vi) 0
# endif
typedef struct {
PAVISTREAM handle ;
AVISTREAMINFO info ;
DWORD read ;
LONG chunck_size ;
LONG chunck_samples ;
} AVISynthStream ;
void * library ;
# define AVSC_DECLARE_FUNC(name) name##_func name
AVSC_DECLARE_FUNC ( avs_create_script_environment ) ;
AVSC_DECLARE_FUNC ( avs_delete_script_environment ) ;
AVSC_DECLARE_FUNC ( avs_get_error ) ;
AVSC_DECLARE_FUNC ( avs_clip_get_error ) ;
AVSC_DECLARE_FUNC ( avs_invoke ) ;
AVSC_DECLARE_FUNC ( avs_release_value ) ;
AVSC_DECLARE_FUNC ( avs_get_video_info ) ;
AVSC_DECLARE_FUNC ( avs_take_clip ) ;
AVSC_DECLARE_FUNC ( avs_release_clip ) ;
AVSC_DECLARE_FUNC ( avs_bit_blt ) ;
AVSC_DECLARE_FUNC ( avs_get_audio ) ;
AVSC_DECLARE_FUNC ( avs_get_frame ) ;
AVSC_DECLARE_FUNC ( avs_release_video_frame ) ;
# undef AVSC_DECLARE_FUNC
} AviSynthLibrary ;
struct AviSynthContext {
AVS_ScriptEnvironment * env ;
AVS_Clip * clip ;
const AVS_VideoInfo * vi ;
// avisynth_read_packet_video() iterates over this.
int n_planes ;
const int * planes ;
int curr_stream ;
int curr_frame ;
int64_t curr_sample ;
int error ;
// Linked list pointers.
struct AviSynthContext * next ;
} ;
typedef struct AviSynthContext AviSynthContext ;
typedef struct {
PAVIFILE file ;
AVISynthStream * streams ;
int nb_streams ;
int next_stream ;
} AVISynthContext ;
static int avisynth_read_header ( AVFormatContext * s )
{
AVISynthContext * avs = s - > priv_data ;
HRESULT res ;
AVIFILEINFO info ;
DWORD id ;
AVStream * st ;
AVISynthStream * stream ;
wchar_t filename_wchar [ 1024 ] = { 0 } ;
char filename_char [ 1024 ] = { 0 } ;
AVIFileInit ( ) ;
/* avisynth can't accept UTF-8 filename */
MultiByteToWideChar ( CP_UTF8 , 0 , s - > filename , - 1 , filename_wchar , 1024 ) ;
WideCharToMultiByte ( CP_THREAD_ACP , 0 , filename_wchar , - 1 , filename_char , 1024 , NULL , NULL ) ;
res = AVIFileOpen ( & avs - > file , filename_char , OF_READ | OF_SHARE_DENY_WRITE , NULL ) ;
if ( res ! = S_OK )
{
av_log ( s , AV_LOG_ERROR , " AVIFileOpen failed with error %ld \n " , res ) ;
AVIFileExit ( ) ;
return - 1 ;
}
res = AVIFileInfo ( avs - > file , & info , sizeof ( info ) ) ;
if ( res ! = S_OK )
{
av_log ( s , AV_LOG_ERROR , " AVIFileInfo failed with error %ld \n " , res ) ;
AVIFileExit ( ) ;
return - 1 ;
}
avs - > streams = av_mallocz ( info . dwStreams * sizeof ( AVISynthStream ) ) ;
for ( id = 0 ; id < info . dwStreams ; id + + )
{
stream = & avs - > streams [ id ] ;
stream - > read = 0 ;
if ( AVIFileGetStream ( avs - > file , & stream - > handle , 0 , id ) = = S_OK )
{
if ( AVIStreamInfo ( stream - > handle , & stream - > info , sizeof ( stream - > info ) ) = = S_OK )
{
if ( stream - > info . fccType = = streamtypeAUDIO )
{
WAVEFORMATEX wvfmt ;
LONG struct_size = sizeof ( WAVEFORMATEX ) ;
if ( AVIStreamReadFormat ( stream - > handle , 0 , & wvfmt , & struct_size ) ! = S_OK )
continue ;
st = avformat_new_stream ( s , NULL ) ;
st - > id = id ;
st - > codec - > codec_type = AVMEDIA_TYPE_AUDIO ;
st - > codec - > block_align = wvfmt . nBlockAlign ;
st - > codec - > channels = wvfmt . nChannels ;
st - > codec - > sample_rate = wvfmt . nSamplesPerSec ;
st - > codec - > bit_rate = wvfmt . nAvgBytesPerSec * 8 ;
st - > codec - > bits_per_coded_sample = wvfmt . wBitsPerSample ;
stream - > chunck_samples = wvfmt . nSamplesPerSec * ( uint64_t ) info . dwScale / ( uint64_t ) info . dwRate ;
stream - > chunck_size = stream - > chunck_samples * wvfmt . nChannels * wvfmt . wBitsPerSample / 8 ;
st - > codec - > codec_tag = wvfmt . wFormatTag ;
st - > codec - > codec_id = ff_wav_codec_get_id ( wvfmt . wFormatTag , st - > codec - > bits_per_coded_sample ) ;
}
else if ( stream - > info . fccType = = streamtypeVIDEO )
{
BITMAPINFO imgfmt ;
LONG struct_size = sizeof ( BITMAPINFO ) ;
stream - > chunck_size = stream - > info . dwSampleSize ;
stream - > chunck_samples = 1 ;
if ( AVIStreamReadFormat ( stream - > handle , 0 , & imgfmt , & struct_size ) ! = S_OK )
continue ;
st = avformat_new_stream ( s , NULL ) ;
st - > id = id ;
st - > codec - > codec_type = AVMEDIA_TYPE_VIDEO ;
st - > avg_frame_rate . num = stream - > info . dwRate ;
st - > avg_frame_rate . den = stream - > info . dwScale ;
# if FF_API_R_FRAME_RATE
st - > r_frame_rate = st - > avg_frame_rate ;
# endif
static const int avs_planes_packed [ 1 ] = { 0 } ;
static const int avs_planes_grey [ 1 ] = { AVS_PLANAR_Y } ;
static const int avs_planes_yuv [ 3 ] = { AVS_PLANAR_Y , AVS_PLANAR_U , AVS_PLANAR_V } ;
// A conflict between C++ global objects, atexit, and dynamic loading requires
// us to register our own atexit handler to prevent double freeing.
static AviSynthLibrary * avs_library = NULL ;
static int avs_atexit_called = 0 ;
// Linked list of AviSynthContexts. An atexit handler destroys this list.
static AviSynthContext * avs_ctx_list = NULL ;
static av_cold void avisynth_atexit_handler ( void ) ;
static av_cold int avisynth_load_library ( void ) {
avs_library = av_mallocz ( sizeof ( AviSynthLibrary ) ) ;
if ( ! avs_library )
return AVERROR_UNKNOWN ;
avs_library - > library = LoadLibrary ( AVISYNTH_LIB ) ;
if ( ! avs_library - > library )
goto init_fail ;
# define LOAD_AVS_FUNC(name, continue_on_fail) \
{ \
avs_library - > name = ( void * ) GetProcAddress ( avs_library - > library , # name ) ; \
if ( ! continue_on_fail & & ! avs_library - > name ) \
goto fail ; \
}
LOAD_AVS_FUNC ( avs_create_script_environment , 0 ) ;
LOAD_AVS_FUNC ( avs_delete_script_environment , 0 ) ;
LOAD_AVS_FUNC ( avs_get_error , 1 ) ; // New to AviSynth 2.6
LOAD_AVS_FUNC ( avs_clip_get_error , 0 ) ;
LOAD_AVS_FUNC ( avs_invoke , 0 ) ;
LOAD_AVS_FUNC ( avs_release_value , 0 ) ;
LOAD_AVS_FUNC ( avs_get_video_info , 0 ) ;
LOAD_AVS_FUNC ( avs_take_clip , 0 ) ;
LOAD_AVS_FUNC ( avs_release_clip , 0 ) ;
LOAD_AVS_FUNC ( avs_bit_blt , 0 ) ;
LOAD_AVS_FUNC ( avs_get_audio , 0 ) ;
LOAD_AVS_FUNC ( avs_get_frame , 0 ) ;
LOAD_AVS_FUNC ( avs_release_video_frame , 0 ) ;
# undef LOAD_AVS_FUNC
atexit ( avisynth_atexit_handler ) ;
return 0 ;
fail :
FreeLibrary ( avs_library - > library ) ;
init_fail :
av_freep ( & avs_library ) ;
return AVERROR_UNKNOWN ;
}
// Note that avisynth_context_create and avisynth_context_destroy
// do not allocate or free the actual context! That is taken care of
// by libavformat.
static av_cold int avisynth_context_create ( AVFormatContext * s ) {
AviSynthContext * avs = ( AviSynthContext * ) s - > priv_data ;
int ret ;
if ( ! avs_library ) {
if ( ret = avisynth_load_library ( ) )
return ret ;
}
if ( ! avs )
return AVERROR_UNKNOWN ;
if ( ! avs_ctx_list ) {
avs_ctx_list = avs ;
} else {
avs - > next = avs_ctx_list ;
avs_ctx_list = avs ;
}
st - > codec - > width = imgfmt . bmiHeader . biWidth ;
st - > codec - > height = imgfmt . bmiHeader . biHeight ;
st - > codec - > bits_per_coded_sample = imgfmt . bmiHeader . biBitCount ;
st - > codec - > bit_rate = ( uint64_t ) stream - > info . dwSampleSize * ( uint64_t ) stream - > info . dwRate * 8 / ( uint64_t ) stream - > info . dwScale ;
st - > codec - > codec_tag = imgfmt . bmiHeader . biCompression ;
st - > codec - > codec_id = ff_codec_get_id ( ff_codec_bmp_tags , imgfmt . bmiHeader . biCompression ) ;
if ( st - > codec - > codec_id = = AV_CODEC_ID_RAWVIDEO & & imgfmt . bmiHeader . biCompression = = BI_RGB ) {
st - > codec - > extradata = av_malloc ( 9 + FF_INPUT_BUFFER_PADDING_SIZE ) ;
if ( st - > codec - > extradata ) {
st - > codec - > extradata_size = 9 ;
memcpy ( st - > codec - > extradata , " BottomUp " , 9 ) ;
}
}
st - > duration = stream - > info . dwLength ;
}
else
{
AVIStreamRelease ( stream - > handle ) ;
continue ;
}
avs - > nb_streams + + ;
st - > codec - > stream_codec_tag = stream - > info . fccHandler ;
avpriv_set_pts_info ( st , 64 , info . dwScale , info . dwRate ) ;
st - > start_time = stream - > info . dwStart ;
}
avs - > env = avs_library - > avs_create_script_environment ( 3 ) ;
if ( avs_library - > avs_get_error ) {
const char * error = avs_library - > avs_get_error ( avs - > env ) ;
if ( error ) {
av_log ( s , AV_LOG_ERROR , " %s \n " , error ) ;
av_free ( avs ) ;
return AVERROR_UNKNOWN ;
}
}
return 0 ;
return 0 ;
}
static int avisynth_read_packet ( AVFormatContext * s , AVPacket * pkt )
{
AVISynthContext * avs = s - > priv_data ;
HRESULT res ;
AVISynthStream * stream ;
int stream_id = avs - > next_stream ;
LONG read_size ;
static av_cold void avisynth_context_destroy ( AviSynthContext * avs ) {
if ( avs_atexit_called )
return ;
if ( avs = = avs_ctx_list ) {
avs_ctx_list = avs - > next ;
} else {
AviSynthContext * prev = avs_ctx_list ;
while ( prev - > next ! = avs )
prev = prev - > next ;
prev - > next = avs - > next ;
}
// handle interleaving manually...
stream = & avs - > streams [ stream_id ] ;
if ( avs - > clip ) {
avs_library - > avs_release_clip ( avs - > clip ) ;
avs - > clip = NULL ;
}
if ( avs - > env ) {
avs_library - > avs_delete_script_environment ( avs - > env ) ;
avs - > env = NULL ;
}
}
if ( stream - > read > = stream - > info . dwLength )
return AVERROR ( EIO ) ;
static av_cold void avisynth_atexit_handler ( void ) {
AviSynthContext * avs = avs_ctx_list ;
if ( av_new_packet ( pkt , stream - > chunck_size ) )
return AVERROR ( EIO ) ;
pkt - > stream_index = stream_id ;
pkt - > pts = avs - > streams [ stream_id ] . read / avs - > streams [ stream_id ] . chunck_samples ;
while ( avs ) {
AviSynthContext * next = avs - > next ;
avisynth_context_destroy ( avs ) ;
avs = next ;
}
FreeLibrary ( avs_library - > library ) ;
av_freep ( & avs_library ) ;
res = AVIStreamRead ( stream - > handle , stream - > read , stream - > chunck_samples , pkt - > data , stream - > chunck_size , & read_size , NULL ) ;
avs_atexit_called = 1 ;
}
pkt - > size = read_size ;
// Create AVStream from audio and video data.
static int avisynth_create_stream_video ( AVFormatContext * s , AVStream * st ) {
AviSynthContext * avs = s - > priv_data ;
int planar = 0 ; // 0: packed, 1: YUV, 2: Y8
st - > codec - > codec_type = AVMEDIA_TYPE_VIDEO ;
st - > codec - > codec_id = CODEC_ID_RAWVIDEO ;
st - > codec - > width = avs - > vi - > width ;
st - > codec - > height = avs - > vi - > height ;
st - > time_base = ( AVRational ) { avs - > vi - > fps_denominator , avs - > vi - > fps_numerator } ;
st - > avg_frame_rate = ( AVRational ) { avs - > vi - > fps_numerator , avs - > vi - > fps_denominator } ;
st - > start_time = 0 ;
st - > duration = avs - > vi - > num_frames ;
st - > nb_frames = avs - > vi - > num_frames ;
switch ( avs - > vi - > pixel_type ) {
# ifdef _WIN32
case AVS_CS_YV24 :
st - > codec - > pix_fmt = PIX_FMT_YUV444P ;
planar = 1 ;
break ;
case AVS_CS_YV16 :
st - > codec - > pix_fmt = PIX_FMT_YUV422P ;
planar = 1 ;
break ;
case AVS_CS_YV411 :
st - > codec - > pix_fmt = PIX_FMT_YUV411P ;
planar = 1 ;
break ;
case AVS_CS_Y8 :
st - > codec - > pix_fmt = PIX_FMT_GRAY8 ;
planar = 2 ;
break ;
# endif
case AVS_CS_BGR24 :
st - > codec - > pix_fmt = PIX_FMT_RGB24 ;
break ;
case AVS_CS_BGR32 :
st - > codec - > pix_fmt = PIX_FMT_RGB32 ;
break ;
case AVS_CS_YUY2 :
st - > codec - > pix_fmt = PIX_FMT_YUYV422 ;
break ;
case AVS_CS_YV12 :
st - > codec - > pix_fmt = PIX_FMT_YUV420P ;
planar = 1 ;
break ;
case AVS_CS_I420 : // Is this even used anywhere?
st - > codec - > pix_fmt = PIX_FMT_YUV420P ;
planar = 1 ;
break ;
default :
av_log ( s , AV_LOG_ERROR , " unknown AviSynth colorspace %d \n " , avs - > vi - > pixel_type ) ;
avs - > error = 1 ;
return AVERROR_UNKNOWN ;
}
stream - > read + = stream - > chunck_samples ;
switch ( planar ) {
case 2 : // Y8
avs - > n_planes = 1 ;
avs - > planes = avs_planes_grey ;
break ;
case 1 : // YUV
avs - > n_planes = 3 ;
avs - > planes = avs_planes_yuv ;
break ;
default :
avs - > n_planes = 1 ;
avs - > planes = avs_planes_packed ;
}
return 0 ;
}
// prepare for the next stream to read
do {
avs - > next_stream = ( avs - > next_stream + 1 ) % avs - > nb_streams ;
} while ( avs - > next_stream ! = stream_id & & s - > streams [ avs - > next_stream ] - > discard > = AVDISCARD_ALL ) ;
static int avisynth_create_stream_audio ( AVFormatContext * s , AVStream * st ) {
AviSynthContext * avs = s - > priv_data ;
st - > codec - > codec_type = AVMEDIA_TYPE_AUDIO ;
st - > codec - > sample_rate = avs - > vi - > audio_samples_per_second ;
st - > codec - > channels = avs - > vi - > nchannels ;
st - > time_base = ( AVRational ) { 1 , avs - > vi - > audio_samples_per_second } ;
switch ( avs - > vi - > sample_type ) {
case AVS_SAMPLE_INT8 :
st - > codec - > codec_id = CODEC_ID_PCM_U8 ;
break ;
case AVS_SAMPLE_INT16 :
st - > codec - > codec_id = CODEC_ID_PCM_S16LE ;
break ;
case AVS_SAMPLE_INT24 :
st - > codec - > codec_id = CODEC_ID_PCM_S24LE ;
break ;
case AVS_SAMPLE_INT32 :
st - > codec - > codec_id = CODEC_ID_PCM_S32LE ;
break ;
case AVS_SAMPLE_FLOAT :
st - > codec - > codec_id = CODEC_ID_PCM_F32LE ;
break ;
default :
av_log ( s , AV_LOG_ERROR , " unknown AviSynth sample type %d \n " , avs - > vi - > sample_type ) ;
avs - > error = 1 ;
return AVERROR_UNKNOWN ;
}
return 0 ;
}
return ( res = = S_OK ) ? pkt - > size : - 1 ;
static int avisynth_create_stream ( AVFormatContext * s ) {
AviSynthContext * avs = s - > priv_data ;
AVStream * st ;
int ret ;
int id = 0 ;
if ( avs_has_video ( avs - > vi ) ) {
st = avformat_new_stream ( s , NULL ) ;
if ( ! st )
return AVERROR_UNKNOWN ;
st - > id = id + + ;
if ( ret = avisynth_create_stream_video ( s , st ) )
return ret ;
}
if ( avs_has_audio ( avs - > vi ) ) {
st = avformat_new_stream ( s , NULL ) ;
if ( ! st )
return AVERROR_UNKNOWN ;
st - > id = id + + ;
if ( ret = avisynth_create_stream_audio ( s , st ) )
return ret ;
}
return 0 ;
}
static int avisynth_read_close ( AVFormatContext * s )
{
AVISynthContext * avs = s - > priv_data ;
int i ;
static int avisynth_open_file ( AVFormatContext * s ) {
AviSynthContext * avs = ( AviSynthContext * ) s - > priv_data ;
AVS_Value arg , val ;
int ret ;
if ( ret = avisynth_context_create ( s ) )
return ret ;
for ( i = 0 ; i < avs - > nb_streams ; i + + )
{
AVIStreamRelease ( avs - > streams [ i ] . handle ) ;
arg = avs_new_value_string ( s - > filename ) ;
val = avs_library - > avs_invoke ( avs - > env , " Import " , arg , 0 ) ;
if ( avs_is_error ( val ) ) {
av_log ( s , AV_LOG_ERROR , " %s \n " , avs_as_error ( val ) ) ;
ret = AVERROR_UNKNOWN ;
goto fail ;
}
if ( ! avs_is_clip ( val ) ) {
av_log ( s , AV_LOG_ERROR , " %s \n " , " AviSynth script did not return a clip " ) ;
ret = AVERROR_UNKNOWN ;
goto fail ;
}
avs - > clip = avs_library - > avs_take_clip ( val , avs - > env ) ;
avs - > vi = avs_library - > avs_get_video_info ( avs - > clip ) ;
// Release the AVS_Value as it will go out of scope.
avs_library - > avs_release_value ( val ) ;
if ( ret = avisynth_create_stream ( s ) )
goto fail ;
return 0 ;
av_free ( avs - > streams ) ;
AVIFileRelease ( avs - > file ) ;
AVIFileExit ( ) ;
return 0 ;
fail :
avisynth_context_destroy ( avs ) ;
return ret ;
}
static int avisynth_read_seek ( AVFormatContext * s , int stream_index , int64_t pts , int flags )
{
AVISynthContext * avs = s - > priv_data ;
int stream_id ;
static void avisynth_next_stream ( AVFormatContext * s , AVStream * * st , AVPacket * pkt , int * discard ) {
AviSynthContext * avs = s - > priv_data ;
for ( stream_id = 0 ; stream_id < avs - > nb_streams ; stream_id + + )
{
avs - > streams [ stream_id ] . read = pts * avs - > streams [ stream_id ] . chunck_samples ;
pkt - > stream_index = avs - > curr_stream + + ;
avs - > curr_stream % = s - > nb_streams ;
* st = s - > streams [ pkt - > stream_index ] ;
if ( ( * st ) - > discard = = AVDISCARD_ALL )
* discard = 1 ;
else
* discard = 0 ;
return ;
}
// Copy AviSynth clip data into an AVPacket.
static int avisynth_read_packet_video ( AVFormatContext * s , AVPacket * pkt , int discard ) {
AviSynthContext * avs = s - > priv_data ;
AVS_VideoFrame * frame ;
unsigned char * dst_p ;
const unsigned char * src_p ;
int i , plane , rowsize , planeheight , pitch , bits ;
const char * error ;
if ( avs - > curr_frame > = avs - > vi - > num_frames )
return AVERROR_EOF ;
// This must happen even if the stream is discarded to prevent desync.
avs - > curr_frame + + ;
if ( discard )
return 0 ;
pkt - > pts = avs - > curr_frame ;
pkt - > dts = avs - > curr_frame ;
pkt - > duration = 1 ;
// Define the bpp values for the new AviSynth 2.6 colorspaces
if ( avs_is_yv24 ( avs - > vi ) ) {
bits = 24 ;
} else if ( avs_is_yv16 ( avs - > vi ) ) {
bits = 16 ;
} else if ( avs_is_yv411 ( avs - > vi ) ) {
bits = 12 ;
} else if ( avs_is_y8 ( avs - > vi ) ) {
bits = 8 ;
} else {
bits = avs_bits_per_pixel ( avs - > vi ) ;
}
// Without cast to int64_t, calculation overflows at about 9k x 9k resolution.
pkt - > size = ( ( ( int64_t ) avs - > vi - > width * ( int64_t ) avs - > vi - > height ) * bits ) / 8 ;
if ( ! pkt - > size )
return AVERROR_UNKNOWN ;
pkt - > data = av_malloc ( pkt - > size ) ;
if ( ! pkt - > data )
return AVERROR_UNKNOWN ;
frame = avs_library - > avs_get_frame ( avs - > clip , avs - > curr_frame ) ;
error = avs_library - > avs_clip_get_error ( avs - > clip ) ;
if ( error ) {
av_log ( s , AV_LOG_ERROR , " %s \n " , error ) ;
avs - > error = 1 ;
av_freep ( & pkt - > data ) ;
return AVERROR_UNKNOWN ;
}
dst_p = pkt - > data ;
for ( i = 0 ; i < avs - > n_planes ; i + + ) {
plane = avs - > planes [ i ] ;
src_p = avs_get_read_ptr_p ( frame , plane ) ;
rowsize = avs_get_row_size_p ( frame , plane ) ;
planeheight = avs_get_height_p ( frame , plane ) ;
pitch = avs_get_pitch_p ( frame , plane ) ;
// Flip RGB video.
if ( avs_is_rgb24 ( avs - > vi ) | | avs_is_rgb ( avs - > vi ) ) {
src_p = src_p + ( planeheight - 1 ) * pitch ;
pitch = - pitch ;
}
avs_library - > avs_bit_blt ( avs - > env , dst_p , rowsize , src_p , pitch , rowsize , planeheight ) ;
dst_p + = rowsize * planeheight ;
}
avs_library - > avs_release_video_frame ( frame ) ;
return 0 ;
}
static int avisynth_read_packet_audio ( AVFormatContext * s , AVPacket * pkt , int discard ) {
AviSynthContext * avs = s - > priv_data ;
AVRational fps , samplerate ;
int samples ;
const char * error ;
if ( avs - > curr_sample > = avs - > vi - > num_audio_samples )
return AVERROR_EOF ;
fps . num = avs - > vi - > fps_numerator ;
fps . den = avs - > vi - > fps_denominator ;
samplerate . num = avs - > vi - > audio_samples_per_second ;
samplerate . den = 1 ;
if ( avs_has_video ( avs - > vi ) ) {
if ( avs - > curr_frame < avs - > vi - > num_frames )
samples = av_rescale_q ( avs - > curr_frame , samplerate , fps ) - avs - > curr_sample ;
else
samples = av_rescale_q ( 1 , samplerate , fps ) ;
} else {
samples = 1000 ;
}
// After seeking, audio may catch up with video.
if ( samples < = 0 ) {
pkt - > size = 0 ;
pkt - > data = NULL ;
return 0 ;
}
if ( avs - > curr_sample + samples > avs - > vi - > num_audio_samples )
samples = avs - > vi - > num_audio_samples - avs - > curr_sample ;
// This must happen even if the stream is discarded to prevent desync.
avs - > curr_sample + = samples ;
if ( discard )
return 0 ;
pkt - > pts = avs - > curr_sample ;
pkt - > dts = avs - > curr_sample ;
pkt - > duration = samples ;
pkt - > size = avs_bytes_per_channel_sample ( avs - > vi ) * samples * avs - > vi - > nchannels ;
if ( ! pkt - > size )
return AVERROR_UNKNOWN ;
pkt - > data = av_malloc ( pkt - > size ) ;
if ( ! pkt - > data )
return AVERROR_UNKNOWN ;
avs_library - > avs_get_audio ( avs - > clip , pkt - > data , avs - > curr_sample , samples ) ;
error = avs_library - > avs_clip_get_error ( avs - > clip ) ;
if ( error ) {
av_log ( s , AV_LOG_ERROR , " %s \n " , error ) ;
avs - > error = 1 ;
av_freep ( & pkt - > data ) ;
return AVERROR_UNKNOWN ;
}
return 0 ;
}
static av_cold int avisynth_read_header ( AVFormatContext * s ) {
int ret ;
// Calling library must implement a lock for thread-safe opens.
if ( ret = avpriv_lock_avformat ( ) )
return ret ;
if ( ret = avisynth_open_file ( s ) ) {
avpriv_unlock_avformat ( ) ;
return ret ;
}
avpriv_unlock_avformat ( ) ;
return 0 ;
}
static int avisynth_read_packet ( AVFormatContext * s , AVPacket * pkt ) {
AviSynthContext * avs = s - > priv_data ;
AVStream * st ;
int discard = 0 ;
int ret ;
if ( avs - > error )
return AVERROR_UNKNOWN ;
pkt - > destruct = av_destruct_packet ;
// If either stream reaches EOF, try to read the other one before giving up.
avisynth_next_stream ( s , & st , pkt , & discard ) ;
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
ret = avisynth_read_packet_video ( s , pkt , discard ) ;
if ( ret = = AVERROR_EOF & & avs_has_audio ( avs - > vi ) ) {
avisynth_next_stream ( s , & st , pkt , & discard ) ;
return avisynth_read_packet_audio ( s , pkt , discard ) ;
}
return ret ;
} else {
ret = avisynth_read_packet_audio ( s , pkt , discard ) ;
if ( ret = = AVERROR_EOF & & avs_has_video ( avs - > vi ) ) {
avisynth_next_stream ( s , & st , pkt , & discard ) ;
return avisynth_read_packet_video ( s , pkt , discard ) ;
}
return ret ;
}
}
static av_cold int avisynth_read_close ( AVFormatContext * s ) {
if ( avpriv_lock_avformat ( ) )
return AVERROR_UNKNOWN ;
avisynth_context_destroy ( s - > priv_data ) ;
avpriv_unlock_avformat ( ) ;
return 0 ;
}
static int avisynth_read_seek ( AVFormatContext * s , int stream_index , int64_t timestamp , int flags ) {
AviSynthContext * avs = s - > priv_data ;
AVStream * st ;
AVRational fps , samplerate ;
if ( avs - > error )
return AVERROR_UNKNOWN ;
fps = ( AVRational ) { avs - > vi - > fps_numerator , avs - > vi - > fps_denominator } ;
samplerate = ( AVRational ) { avs - > vi - > audio_samples_per_second , 1 } ;
st = s - > streams [ stream_index ] ;
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
// AviSynth frame counts are signed int.
if ( ( timestamp > = avs - > vi - > num_frames ) | | ( timestamp > INT_MAX ) | | ( timestamp < 0 ) )
return AVERROR_EOF ;
avs - > curr_frame = timestamp ;
if ( avs_has_audio ( avs - > vi ) )
avs - > curr_sample = av_rescale_q ( timestamp , samplerate , fps ) ;
} else {
if ( ( timestamp > = avs - > vi - > num_audio_samples ) | | ( timestamp < 0 ) )
return AVERROR_EOF ;
// Force frame granularity for seeking.
if ( avs_has_video ( avs - > vi ) ) {
avs - > curr_frame = av_rescale_q ( timestamp , fps , samplerate ) ;
avs - > curr_sample = av_rescale_q ( avs - > curr_frame , samplerate , fps ) ;
} else {
avs - > curr_sample = timestamp ;
}
}
return 0 ;
return 0 ;
}
AVInputFormat ff_avisynth_demuxer = {
. name = " avisynth " ,
. long_name = NULL_IF_CONFIG_SMALL ( " AVISynth " ) ,
. priv_data_size = sizeof ( AVISynthContext ) ,
. long_name = NULL_IF_CONFIG_SMALL ( " AviSynth script " ) ,
. priv_data_size = sizeof ( Avi SynthContext ) ,
. read_header = avisynth_read_header ,
. read_packet = avisynth_read_packet ,
. read_close = avisynth_read_close ,