2013-05-04 10:40:05 +00:00
# include "../plugin.h"
2012-11-27 03:23:19 +00:00
# include "../engine.h"
2013-05-11 14:02:55 +00:00
# include "libavformat/avformat.h"
# include "libavformat/avio.h"
# include "libavcodec/avcodec.h"
# include "libswscale/swscale.h"
2016-07-12 00:40:13 +00:00
# include "libavutil/imgutils.h"
# include "libavutil/opt.h"
2013-05-04 10:40:05 +00:00
//#include <libavutil/channel_layout.h>
2012-11-27 03:23:19 +00:00
/*
Most of the logic in here came from here :
http : //svn.gnumonks.org/tags/21c3-video/upstream/ffmpeg-0.4.9-pre1/output_example.c
*/
struct encctx
{
2016-07-12 00:40:13 +00:00
char abspath [ MAX_OSPATH ] ;
2012-11-27 03:23:19 +00:00
AVFormatContext * fc ;
qboolean doneheaders ;
AVStream * video_st ;
struct SwsContext * scale_ctx ;
2013-05-04 10:40:05 +00:00
AVFrame * picture ;
uint8_t * video_outbuf ;
2012-11-27 03:23:19 +00:00
int video_outbuf_size ;
AVStream * audio_st ;
AVFrame * audio ;
2016-07-12 00:40:13 +00:00
uint8_t * audio_outbuf ;
uint32_t audio_outcount ;
int64_t audio_pts ;
2012-11-27 03:23:19 +00:00
} ;
2016-07-12 00:40:13 +00:00
# define VARIABLE_AUDIO_FRAME_MIN_SIZE 512 //audio frames smaller than a certain size are just wasteful
# define VARIABLE_AUDIO_FRAME_MAX_SIZE 1024
2012-11-27 03:23:19 +00:00
static void AVEnc_End ( void * ctx ) ;
2016-07-12 00:40:13 +00:00
static AVFrame * alloc_frame ( enum AVPixelFormat pix_fmt , int width , int height )
2013-05-04 10:40:05 +00:00
{
2016-12-19 13:31:05 +00:00
AVFrame * picture ;
uint8_t * picture_buf ;
int size ;
picture = av_frame_alloc ( ) ;
if ( ! picture )
return NULL ;
size = av_image_get_buffer_size ( pix_fmt , width , height , 1 ) ;
picture_buf = ( uint8_t * ) ( av_malloc ( size ) ) ;
if ( ! picture_buf )
{
av_free ( picture ) ;
return NULL ;
}
av_image_fill_arrays ( picture - > data , picture - > linesize , picture_buf , pix_fmt , width , height , 1 /*fixme: align*/ ) ;
picture - > width = width ;
picture - > height = height ;
return picture ;
2012-11-27 03:23:19 +00:00
}
2013-05-04 10:40:05 +00:00
AVStream * add_video_stream ( struct encctx * ctx , AVCodec * codec , int fps , int width , int height )
{
AVCodecContext * c ;
AVStream * st ;
char prof [ 128 ] ;
int bitrate = ( int ) pCvar_GetFloat ( " avplug_videobitrate " ) ;
int forcewidth = ( int ) pCvar_GetFloat ( " avplug_videoforcewidth " ) ;
int forceheight = ( int ) pCvar_GetFloat ( " avplug_videoforceheight " ) ;
st = avformat_new_stream ( ctx - > fc , codec ) ;
if ( ! st )
2016-07-12 00:40:13 +00:00
return NULL ;
2013-05-04 10:40:05 +00:00
c = st - > codec ;
c - > codec_id = codec - > id ;
c - > codec_type = codec - > type ;
/* put sample parameters */
c - > bit_rate = bitrate ;
/* resolution must be a multiple of two */
c - > width = forcewidth ? forcewidth : width ;
c - > height = forceheight ? forceheight : height ;
/* frames per second */
c - > time_base . num = 1 ;
c - > time_base . den = fps ;
2016-07-12 00:40:13 +00:00
//c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c - > pix_fmt = AV_PIX_FMT_YUV420P ;
if ( c - > codec_id = = AV_CODEC_ID_MPEG2VIDEO )
2013-05-04 10:40:05 +00:00
{
/* just for testing, we also add B frames */
c - > max_b_frames = 2 ;
}
2016-07-12 00:40:13 +00:00
if ( c - > codec_id = = AV_CODEC_ID_MPEG1VIDEO )
2013-05-04 10:40:05 +00:00
{
/* needed to avoid using macroblocks in which some coeffs overflow
this doesnt happen with normal video , it just happens here as the
motion of the chroma plane doesnt match the luma plane */
// c->mb_decision=2;
}
// some formats want stream headers to be seperate
if ( ctx - > fc - > oformat - > flags & AVFMT_GLOBALHEADER )
c - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
2016-07-12 00:40:13 +00:00
pCvar_GetString ( " avplug_videopreset " , prof , sizeof ( prof ) ) ;
if ( * prof )
av_opt_set ( c - > priv_data , " preset " , prof , AV_OPT_SEARCH_CHILDREN ) ;
pCvar_GetString ( " avplug_video_crf " , prof , sizeof ( prof ) ) ;
if ( * prof )
av_opt_set ( c - > priv_data , " crf " , prof , AV_OPT_SEARCH_CHILDREN ) ;
2013-05-04 10:40:05 +00:00
return st ;
2012-11-27 03:23:19 +00:00
}
2013-05-04 10:40:05 +00:00
void close_video ( struct encctx * ctx )
{
if ( ! ctx - > video_st )
return ;
avcodec_close ( ctx - > video_st - > codec ) ;
if ( ctx - > picture )
{
av_free ( ctx - > picture - > data [ 0 ] ) ;
av_free ( ctx - > picture ) ;
}
av_free ( ctx - > video_outbuf ) ;
2012-11-27 03:23:19 +00:00
}
2015-08-14 02:46:38 +00:00
static void AVEnc_Video ( void * vctx , void * data , int frame , int width , int height , enum uploadfmt qpfmt )
2013-05-04 10:40:05 +00:00
{
struct encctx * ctx = vctx ;
2015-08-14 02:46:38 +00:00
const uint8_t * srcslices [ 2 ] ;
int srcstride [ 2 ] ;
2013-05-04 10:40:05 +00:00
int success ;
AVPacket pkt ;
2015-08-14 02:46:38 +00:00
int avpfmt ;
int inbpp ;
2016-07-12 00:40:13 +00:00
int err ;
2013-05-04 10:40:05 +00:00
if ( ! ctx - > video_st )
return ;
2015-08-14 02:46:38 +00:00
switch ( qpfmt )
{
case TF_BGRA32 : avpfmt = AV_PIX_FMT_BGRA ; inbpp = 4 ; break ;
case TF_RGBA32 : avpfmt = AV_PIX_FMT_RGBA ; inbpp = 4 ; break ;
case TF_BGR24 : avpfmt = AV_PIX_FMT_BGR24 ; inbpp = 3 ; break ;
case TF_RGB24 : avpfmt = AV_PIX_FMT_RGB24 ; inbpp = 3 ; break ;
default :
return ;
}
//weird maths to flip it.
srcslices [ 0 ] = ( uint8_t * ) data + ( height - 1 ) * width * inbpp ;
srcstride [ 0 ] = - width * inbpp ;
srcslices [ 1 ] = NULL ;
srcstride [ 1 ] = 0 ;
2013-05-04 10:40:05 +00:00
//convert RGB to whatever the codec needs (ie: yuv...).
2015-08-14 02:46:38 +00:00
//also rescales, but only if the user resizes the video while recording. which is a stupid thing to do.
2016-07-12 00:40:13 +00:00
ctx - > scale_ctx = sws_getCachedContext ( ctx - > scale_ctx , width , height , avpfmt , ctx - > picture - > width , ctx - > picture - > height , ctx - > video_st - > codec - > pix_fmt , SWS_POINT , 0 , 0 , 0 ) ;
2013-05-04 10:40:05 +00:00
sws_scale ( ctx - > scale_ctx , srcslices , srcstride , 0 , height , ctx - > picture - > data , ctx - > picture - > linesize ) ;
av_init_packet ( & pkt ) ;
2016-07-12 00:40:13 +00:00
ctx - > picture - > pts = frame ;
2013-05-04 10:40:05 +00:00
success = 0 ;
2012-11-27 03:23:19 +00:00
pkt . data = ctx - > video_outbuf ;
2013-05-04 10:40:05 +00:00
pkt . size = ctx - > video_outbuf_size ;
2016-07-12 00:40:13 +00:00
ctx - > picture - > format = ctx - > video_st - > codec - > pix_fmt ;
err = avcodec_encode_video2 ( ctx - > video_st - > codec , & pkt , ctx - > picture , & success ) ;
if ( err )
2012-11-27 03:23:19 +00:00
{
2016-07-12 00:40:13 +00:00
char buf [ 512 ] ;
Con_Printf ( " avcodec_encode_video2: error: %s \n " , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
}
else if ( err = = 0 & & success )
{
// pkt.pts = ctx->video_st->codec->coded_frame->pts;
// if(ctx->video_st->codec->coded_frame->key_frame)
// pkt.flags |= AV_PKT_FLAG_KEY;
av_packet_rescale_ts ( & pkt , ctx - > video_st - > codec - > time_base , ctx - > video_st - > time_base ) ;
2012-11-27 03:23:19 +00:00
pkt . stream_index = ctx - > video_st - > index ;
2016-07-12 00:40:13 +00:00
err = av_interleaved_write_frame ( ctx - > fc , & pkt ) ;
if ( err )
{
char buf [ 512 ] ;
Con_Printf ( " av_interleaved_write_frame: error: %s \n " , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
}
2013-05-04 10:40:05 +00:00
}
2012-11-27 03:23:19 +00:00
}
2016-07-12 00:40:13 +00:00
AVStream * add_audio_stream ( struct encctx * ctx , AVCodec * codec , int samplerate , int * bits , int channels )
2013-05-04 10:40:05 +00:00
{
AVCodecContext * c ;
AVStream * st ;
int bitrate = ( int ) pCvar_GetFloat ( " avplug_audiobitrate " ) ;
st = avformat_new_stream ( ctx - > fc , codec ) ;
if ( ! st )
2016-07-12 00:40:13 +00:00
return NULL ;
2013-05-04 10:40:05 +00:00
2016-07-12 00:40:13 +00:00
st - > id = ctx - > fc - > nb_streams - 1 ;
2013-05-04 10:40:05 +00:00
c = st - > codec ;
c - > codec_id = codec - > id ;
c - > codec_type = codec - > type ;
/* put sample parameters */
c - > bit_rate = bitrate ;
/* frames per second */
2016-07-12 00:40:13 +00:00
c - > time_base . num = 1 ;
c - > time_base . den = samplerate ;
2013-05-04 10:40:05 +00:00
c - > sample_rate = samplerate ;
c - > channels = channels ;
2016-07-12 00:40:13 +00:00
#if 0
2013-05-04 10:40:05 +00:00
switch ( channels )
{
case 1 :
2016-07-12 00:40:13 +00:00
c - > channel_layout = AV_CH_LAYOUT_MONO ;
2013-05-04 10:40:05 +00:00
break ;
case 2 :
2016-07-12 00:40:13 +00:00
c - > channel_layout = AV_CH_LAYOUT_STEREO ;
2013-05-04 10:40:05 +00:00
break ;
default :
break ;
}
2016-07-12 00:40:13 +00:00
# else
c - > channel_layout = av_get_default_channel_layout ( c - > channels ) ;
# endif
c - > channel_layout = av_get_default_channel_layout ( c - > channels ) ;
c - > sample_fmt = codec - > sample_fmts [ 0 ] ;
// if (c->sample_fmt == AV_SAMPLE_FMT_FLTP || c->sample_fmt == AV_SAMPLE_FMT_FLT)
// *bits = 32; //get the engine to mix 32bit audio instead of whatever its currently set to.
// else if (c->sample_fmt == AV_SAMPLE_FMT_U8P || c->sample_fmt == AV_SAMPLE_FMT_U8)
// *bits = 8; //get the engine to mix 32bit audio instead of whatever its currently set to.
// else if (c->sample_fmt == AV_SAMPLE_FMT_S16P || c->sample_fmt == AV_SAMPLE_FMT_S16)
// *bits = 16;
// else
* bits = 32 ;
c - > strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL ;
2013-05-04 10:40:05 +00:00
// some formats want stream headers to be seperate
if ( ctx - > fc - > oformat - > flags & AVFMT_GLOBALHEADER )
c - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
2016-07-12 00:40:13 +00:00
// avcodec_parameters_from_context(st->codecpar, c);
2013-05-04 10:40:05 +00:00
return st ;
2012-11-27 03:23:19 +00:00
}
void close_audio ( struct encctx * ctx )
{
2013-05-04 10:40:05 +00:00
if ( ! ctx - > audio_st )
return ;
2012-11-27 03:23:19 +00:00
avcodec_close ( ctx - > audio_st - > codec ) ;
}
2013-05-04 10:40:05 +00:00
static void AVEnc_Audio ( void * vctx , void * data , int bytes )
{
struct encctx * ctx = vctx ;
int success ;
AVPacket pkt ;
2016-07-12 00:40:13 +00:00
int err ;
2013-05-04 10:40:05 +00:00
2016-07-12 00:40:13 +00:00
if ( ! ctx - > audio_st )
2013-05-04 10:40:05 +00:00
return ;
2016-07-12 00:40:13 +00:00
while ( bytes )
2012-11-27 03:23:19 +00:00
{
2016-07-12 00:40:13 +00:00
int i , p , chans = ctx - > audio_st - > codec - > channels ;
int blocksize = sizeof ( float ) * chans ;
int count = bytes / blocksize ;
int planesize = ctx - > audio_st - > codec - > frame_size ;
float * in ;
int offset ;
if ( ! planesize ) //variable-sized frames. yay
{
planesize = count ;
if ( count > VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx - > audio_outcount )
count = VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx - > audio_outcount ;
}
else if ( count > ctx - > audio_st - > codec - > frame_size - ctx - > audio_outcount )
count = ctx - > audio_st - > codec - > frame_size - ctx - > audio_outcount ;
in = ( float * ) data ;
offset = ctx - > audio_outcount ;
ctx - > audio_outcount + = count ;
data = ( qbyte * ) data + count * blocksize ;
bytes - = count * blocksize ;
//input is always float audio, because I'm lazy.
//output is whatever the codec needs (may be packed or planar, gah).
//the engine's mixer will do all rate scaling for us, as well as channel selection
switch ( ctx - > audio_st - > codec - > sample_fmt )
{
case AV_SAMPLE_FMT_DBL :
offset * = chans ;
count * = chans ;
planesize * = chans ;
chans = 1 ;
case AV_SAMPLE_FMT_DBLP :
for ( p = 0 ; p < chans ; p + + )
{
double * f = ( double * ) ctx - > audio_outbuf + p * planesize + offset ;
for ( i = 0 ; i < count * chans ; i + = chans )
* f + + = in [ i ] ;
in + + ;
}
break ;
case AV_SAMPLE_FMT_FLT :
offset * = chans ;
count * = chans ;
planesize * = chans ;
chans = 1 ;
case AV_SAMPLE_FMT_FLTP :
for ( p = 0 ; p < chans ; p + + )
{
float * f = ( float * ) ctx - > audio_outbuf + p * planesize + offset ;
for ( i = 0 ; i < count * chans ; i + = chans )
* f + + = in [ i ] ;
in + + ;
}
break ;
case AV_SAMPLE_FMT_S32 :
offset * = chans ;
count * = chans ;
planesize * = chans ;
chans = 1 ;
case AV_SAMPLE_FMT_S32P :
for ( p = 0 ; p < chans ; p + + )
{
int32_t * f = ( int32_t * ) ctx - > audio_outbuf + p * planesize + offset ;
for ( i = 0 ; i < count * chans ; i + = chans )
* f + + = bound ( 0x80000000 , ( in [ i ] * 0x7fffffff ) , 0x7fffffff ) ;
in + + ;
}
break ;
case AV_SAMPLE_FMT_S16 :
offset * = chans ;
count * = chans ;
planesize * = chans ;
chans = 1 ;
case AV_SAMPLE_FMT_S16P :
for ( p = 0 ; p < chans ; p + + )
{
int16_t * f = ( int16_t * ) ctx - > audio_outbuf + p * planesize + offset ;
for ( i = 0 ; i < count * chans ; i + = chans )
* f + + = bound ( - 32768 , ( int ) ( in [ i ] * 32767 ) , 32767 ) ;
//sin((ctx->audio_pts+ctx->audio_outcount-count+i/chans)*0.1) * 32767;//
in + + ;
}
break ;
case AV_SAMPLE_FMT_U8 :
offset * = chans ;
count * = chans ;
planesize * = chans ;
chans = 1 ;
case AV_SAMPLE_FMT_U8P :
for ( p = 0 ; p < chans ; p + + )
{
uint8_t * f = ( uint8_t * ) ctx - > audio_outbuf + p * planesize + offset ;
for ( i = 0 ; i < count * chans ; i + = chans )
* f + + = bound ( 0 , 128 + ( int ) ( in [ i ] * 127 ) , 255 ) ;
in + + ;
}
break ;
default :
return ;
}
if ( ctx - > audio_st - > codec - > frame_size )
{
if ( ctx - > audio_outcount < ctx - > audio_st - > codec - > frame_size )
break ; //not enough data yet.
}
else
{
if ( ctx - > audio_outcount < VARIABLE_AUDIO_FRAME_MIN_SIZE )
break ; //not enough data yet.
}
ctx - > audio - > nb_samples = ctx - > audio_outcount ;
avcodec_fill_audio_frame ( ctx - > audio , ctx - > audio_st - > codec - > channels , ctx - > audio_st - > codec - > sample_fmt , ctx - > audio_outbuf , av_get_bytes_per_sample ( ctx - > audio_st - > codec - > sample_fmt ) * ctx - > audio_outcount * ctx - > audio_st - > codec - > channels , 1 ) ;
av_init_packet ( & pkt ) ;
pkt . data = NULL ;
pkt . size = 0 ;
success = 0 ;
ctx - > audio - > pts = ctx - > audio_pts ;
ctx - > audio_pts + = ctx - > audio_outcount ;
ctx - > audio_outcount = 0 ;
err = avcodec_encode_audio2 ( ctx - > audio_st - > codec , & pkt , ctx - > audio , & success ) ;
if ( err )
{
char buf [ 512 ] ;
Con_Printf ( " avcodec_encode_audio2: error: %s \n " , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
}
else if ( success )
{
// pkt.pts = ctx->audio_st->codec->coded_frame->pts;
// if(ctx->audio_st->codec->coded_frame->key_frame)
// pkt.flags |= AV_PKT_FLAG_KEY;
av_packet_rescale_ts ( & pkt , ctx - > audio_st - > codec - > time_base , ctx - > audio_st - > time_base ) ;
pkt . stream_index = ctx - > audio_st - > index ;
err = av_interleaved_write_frame ( ctx - > fc , & pkt ) ;
if ( err )
{
char buf [ 512 ] ;
Con_Printf ( " av_interleaved_write_frame: error: %s \n " , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
}
}
2013-05-04 10:40:05 +00:00
}
2012-11-27 03:23:19 +00:00
}
2013-05-04 10:40:05 +00:00
static void * AVEnc_Begin ( char * streamname , int videorate , int width , int height , int * sndkhz , int * sndchannels , int * sndbits )
{
struct encctx * ctx ;
AVOutputFormat * fmt = NULL ;
AVCodec * videocodec = NULL ;
AVCodec * audiocodec = NULL ;
char formatname [ 64 ] ;
2016-07-12 00:40:13 +00:00
int err ;
2013-05-04 10:40:05 +00:00
formatname [ 0 ] = 0 ;
2016-07-12 00:40:13 +00:00
pCvar_GetString ( " avplug_format_force " , formatname , sizeof ( formatname ) ) ;
2013-05-04 10:40:05 +00:00
if ( * formatname )
{
fmt = av_guess_format ( formatname , NULL , NULL ) ;
if ( ! fmt )
{
Con_Printf ( " Unknown format specified. \n " ) ;
return NULL ;
}
}
if ( ! fmt )
fmt = av_guess_format ( NULL , streamname , NULL ) ;
if ( ! fmt )
{
Con_DPrintf ( " Could not deduce output format from file extension: using MPEG. \n " ) ;
fmt = av_guess_format ( " mpeg " , NULL , NULL ) ;
}
if ( ! fmt )
{
Con_Printf ( " Format not known \n " ) ;
return NULL ;
}
if ( videorate )
{
char codecname [ 64 ] ;
codecname [ 0 ] = 0 ;
pCvar_GetString ( " avplug_videocodec " , codecname , sizeof ( codecname ) ) ;
if ( strcmp ( codecname , " none " ) )
{
if ( codecname [ 0 ] )
{
videocodec = avcodec_find_encoder_by_name ( codecname ) ;
if ( ! videocodec )
{
2013-10-08 14:28:11 +00:00
Con_Printf ( " Unsupported avplug_videocodec \" %s \" \n " , codecname ) ;
2013-05-04 10:40:05 +00:00
return NULL ;
}
}
if ( ! videocodec & & fmt - > video_codec ! = AV_CODEC_ID_NONE )
videocodec = avcodec_find_encoder ( fmt - > video_codec ) ;
}
}
if ( * sndkhz )
{
char codecname [ 64 ] ;
codecname [ 0 ] = 0 ;
pCvar_GetString ( " avplug_audiocodec " , codecname , sizeof ( codecname ) ) ;
if ( strcmp ( codecname , " none " ) )
{
if ( codecname [ 0 ] )
{
audiocodec = avcodec_find_encoder_by_name ( codecname ) ;
if ( ! audiocodec )
{
2013-10-08 14:28:11 +00:00
Con_Printf ( " avplug: Unsupported avplug_audiocodec \" %s \" \n " , codecname ) ;
2013-05-04 10:40:05 +00:00
return NULL ;
}
}
if ( ! audiocodec & & fmt - > audio_codec ! = AV_CODEC_ID_NONE )
audiocodec = avcodec_find_encoder ( fmt - > audio_codec ) ;
}
}
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Using format \" %s \" \n " , fmt - > name ) ;
2013-05-04 10:40:05 +00:00
if ( videocodec )
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Using Video Codec \" %s \" \n " , videocodec - > name ) ;
2013-05-04 10:40:05 +00:00
else
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Not encoding video \n " ) ;
2013-05-04 10:40:05 +00:00
if ( audiocodec )
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Using Audio Codec \" %s \" \n " , audiocodec - > name ) ;
2013-05-04 10:40:05 +00:00
else
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Not encoding audio \n " ) ;
2016-12-19 13:31:05 +00:00
2013-05-04 10:40:05 +00:00
if ( ! videocodec & & ! audiocodec )
{
2013-07-26 17:19:06 +00:00
Con_DPrintf ( " avplug: Nothing to encode! \n " ) ;
2013-05-04 10:40:05 +00:00
return NULL ;
}
if ( ! audiocodec )
* sndkhz = 0 ;
ctx = malloc ( sizeof ( * ctx ) ) ;
if ( ! ctx )
return NULL ;
memset ( ctx , 0 , sizeof ( * ctx ) ) ;
ctx - > fc = avformat_alloc_context ( ) ;
ctx - > fc - > oformat = fmt ;
2013-05-11 14:02:55 +00:00
Q_snprintf ( ctx - > fc - > filename , sizeof ( ctx - > fc - > filename ) , " %s " , streamname ) ;
2013-05-04 10:40:05 +00:00
//pick default codecs
ctx - > video_st = NULL ;
if ( videocodec )
ctx - > video_st = add_video_stream ( ctx , videocodec , videorate , width , height ) ;
2016-07-12 00:40:13 +00:00
if ( audiocodec )
ctx - > audio_st = add_audio_stream ( ctx , audiocodec , * sndkhz , sndbits , * sndchannels ) ;
2013-05-04 10:40:05 +00:00
2016-07-12 00:40:13 +00:00
if ( ctx - > video_st )
{
AVCodecContext * c = ctx - > video_st - > codec ;
err = avcodec_open2 ( c , videocodec , NULL ) ;
if ( err < 0 )
2013-05-04 10:40:05 +00:00
{
2016-07-12 00:40:13 +00:00
char buf [ 512 ] ;
Con_Printf ( " avplug: Could not init codec instance \" %s \" - %s \n Maybe try a different framerate/resolution/bitrate \n " , videocodec - > name , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
AVEnc_End ( ctx ) ;
return NULL ;
2013-05-04 10:40:05 +00:00
}
2016-07-12 00:40:13 +00:00
ctx - > picture = alloc_frame ( c - > pix_fmt , c - > width , c - > height ) ;
ctx - > video_outbuf_size = 200000 ;
ctx - > video_outbuf = av_malloc ( ctx - > video_outbuf_size ) ;
if ( ! ctx - > video_outbuf )
ctx - > video_outbuf_size = 0 ;
2013-05-04 10:40:05 +00:00
}
2016-07-12 00:40:13 +00:00
if ( ctx - > audio_st )
2013-05-04 10:40:05 +00:00
{
2016-07-12 00:40:13 +00:00
int sz ;
AVCodecContext * c = ctx - > audio_st - > codec ;
err = avcodec_open2 ( c , audiocodec , NULL ) ;
if ( err < 0 )
2013-05-04 10:40:05 +00:00
{
2016-07-12 00:40:13 +00:00
char buf [ 512 ] ;
Con_Printf ( " avplug: Could not init codec instance \" %s \" - %s \n " , audiocodec - > name , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
AVEnc_End ( ctx ) ;
return NULL ;
2013-05-04 10:40:05 +00:00
}
2016-07-12 00:40:13 +00:00
ctx - > audio = av_frame_alloc ( ) ;
sz = ctx - > audio_st - > codec - > frame_size ;
if ( ! sz )
sz = VARIABLE_AUDIO_FRAME_MAX_SIZE ;
sz * = av_get_bytes_per_sample ( ctx - > audio_st - > codec - > sample_fmt ) * ctx - > audio_st - > codec - > channels ;
ctx - > audio_outbuf = av_malloc ( sz ) ;
2013-05-04 10:40:05 +00:00
}
av_dump_format ( ctx - > fc , 0 , streamname , 1 ) ;
if ( ! ( fmt - > flags & AVFMT_NOFILE ) )
{
2016-07-12 00:40:13 +00:00
//okay, this is annoying, but I'm too lazy to figure out the issue I was having with avio stuff.
if ( ! pFS_NativePath ( streamname , FS_GAMEONLY , ctx - > abspath , sizeof ( ctx - > abspath ) ) | | avio_open ( & ctx - > fc - > pb , ctx - > abspath , AVIO_FLAG_WRITE ) < 0 )
2013-05-04 10:40:05 +00:00
{
Con_Printf ( " Could not open '%s' \n " , streamname ) ;
AVEnc_End ( ctx ) ;
return NULL ;
}
}
//nearly complete, can make the file dirty now.
2016-07-12 00:40:13 +00:00
err = avformat_write_header ( ctx - > fc , NULL ) ;
if ( err < 0 )
{
char buf [ 512 ] ;
Con_Printf ( " avformat_write_header: failed %s \n " , av_make_error_string ( buf , sizeof ( buf ) , err ) ) ;
AVEnc_End ( ctx ) ;
return NULL ;
}
2013-05-04 10:40:05 +00:00
ctx - > doneheaders = true ;
return ctx ;
}
2012-11-27 03:23:19 +00:00
static void AVEnc_End ( void * vctx )
{
struct encctx * ctx = vctx ;
unsigned int i ;
close_video ( ctx ) ;
//don't write trailers if this is an error case and we never even wrote the headers.
if ( ctx - > doneheaders )
2016-07-12 00:40:13 +00:00
{
2012-11-27 03:23:19 +00:00
av_write_trailer ( ctx - > fc ) ;
2016-07-12 00:40:13 +00:00
if ( * ctx - > abspath )
Con_Printf ( " Finished writing %s \n " , ctx - > abspath ) ;
}
2012-11-27 03:23:19 +00:00
2013-05-04 10:40:05 +00:00
for ( i = 0 ; i < ctx - > fc - > nb_streams ; i + + )
av_freep ( & ctx - > fc - > streams [ i ] ) ;
// if (!(fmt->flags & AVFMT_NOFILE))
avio_close ( ctx - > fc - > pb ) ;
2016-07-12 00:40:13 +00:00
av_free ( ctx - > audio_outbuf ) ;
2012-11-27 03:23:19 +00:00
av_free ( ctx - > fc ) ;
free ( ctx ) ;
}
2013-05-04 10:40:05 +00:00
static media_encoder_funcs_t encoderfuncs =
{
2016-07-12 00:40:13 +00:00
sizeof ( media_encoder_funcs_t ) ,
2013-07-26 17:19:06 +00:00
" avplug " ,
2016-07-12 00:40:13 +00:00
" Use ffmpeg's various codecs. Various settings are configured with the avplug_* cvars. " ,
" .mp4 " ,
2013-05-04 10:40:05 +00:00
AVEnc_Begin ,
AVEnc_Video ,
AVEnc_Audio ,
AVEnc_End
2012-11-27 03:23:19 +00:00
} ;
2013-06-23 02:33:52 +00:00
/*
qintptr_t AVEnc_ExecuteCommand ( qintptr_t * args )
{
char cmd [ 256 ] ;
Cmd_Argv ( 0 , cmd , sizeof ( cmd ) ) ;
if ( ! strcmp ( cmd , " avcapture " ) )
{
menuclear
menualias menucallback
menubox 0 0 320 8
menutext 0 0 " GO GO GO!!! " " radio21 "
menutext 0 8 " Fall back " " radio22 "
menutext 0 8 " Stick together " " radio23 "
menutext 0 16 " Get in position " " radio24 "
menutext 0 24 " Storm the front " " radio25 "
menutext 0 24 " Report in " " radio26 "
menutext 0 24 " Cancel "
return true ;
}
return false ;
}
*/
2013-05-04 10:40:05 +00:00
qboolean AVEnc_Init ( void )
{
2016-07-12 00:40:13 +00:00
CHECKBUILTIN ( FS_NativePath ) ;
if ( ! BUILTINISVALID ( FS_NativePath ) )
{
Con_Printf ( " avplug: Engine too old \n " ) ;
return false ;
}
2013-05-04 10:40:05 +00:00
if ( ! pPlug_ExportNative ( " Media_VideoEncoder " , & encoderfuncs ) )
{
Con_Printf ( " avplug: Engine doesn't support media encoder plugins \n " ) ;
return false ;
}
2012-11-27 03:23:19 +00:00
2016-07-12 00:40:13 +00:00
pCvar_Register ( " avplug_format_force " , " " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_videocodec " , " " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_videobitrate " , " 4000000 " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_videoforcewidth " , " " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_videoforceheight " , " " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_videopreset " , " veryfast " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_audiocodec " , " " , 0 , " avplug " ) ;
pCvar_Register ( " avplug_audiobitrate " , " 64000 " , 0 , " avplug " ) ;
2013-06-23 02:33:52 +00:00
// if (Plug_Export("ExecuteCommand", AVEnc_ExecuteCommand))
// Cmd_AddCommand("avcapture");
2013-05-04 10:40:05 +00:00
return true ;
}
2012-11-27 03:23:19 +00:00