@ -3,6 +3,7 @@
* Simple movie writer for vnc ; based on Libavformat API example from FFMPEG
*
* Copyright ( c ) 2003 Fabrice Bellard , 2004 Johannes E . Schindelin
* Updates copyright ( c ) 2017 Tyrel M . McQueen
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
@ -25,412 +26,451 @@
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <signal.h>
# include <math.h>
# ifndef M_PI
# define M_PI 3.1415926535897931
# endif
# include "avformat.h"
# include <signal.h>
# include <sys/time.h>
# include <libavformat/avformat.h>
# include <libswscale/swscale.h>
# include <rfb/rfbclient.h>
# define STREAM_FRAME_RATE 25 /* 25 images/s */
# define VNC_PIX_FMT AV_PIX_FMT_RGB565 /* pixel format generated by VNC client */
# define OUTPUT_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
/**************************************************************/
/* video output */
static int write_packet ( AVFormatContext * oc , const AVRational * time_base , AVStream * st , AVPacket * pkt )
{
/* rescale output packet timestamp values from codec to stream timebase */
av_packet_rescale_ts ( pkt , * time_base , st - > time_base ) ;
pkt - > stream_index = st - > index ;
/* Write the compressed frame to the media file. */
return av_interleaved_write_frame ( oc , pkt ) ;
}
AVFrame * picture , * tmp_picture ;
uint8_t * video_outbuf ;
int frame_count , video_outbuf_size ;
/*************************************************/
/* video functions */
/* add a video output stream */
AVStream * add_video_stream ( AVFormatContext * oc , int codec_id , int w , int h )
{
AVCodecContext * c ;
/* a wrapper around a single output video stream */
typedef struct {
AVStream * st ;
AVCodec * codec ;
AVCodecContext * enc ;
int64_t pts ;
AVFrame * frame ;
AVFrame * tmp_frame ;
struct SwsContext * sws ;
} VideoOutputStream ;
/* Add an output video stream. */
int add_video_stream ( VideoOutputStream * ost , AVFormatContext * oc ,
enum AVCodecID codec_id , int64_t br , int sr , int w , int h )
{
int i ;
st = av_new_stream ( oc , 0 ) ;
if ( ! st ) {
fprintf ( stderr , " Could not alloc stream \n " ) ;
exit ( 1 ) ;
}
# if LIBAVFORMAT_BUILD<4629
c = & st - > codec ;
# else
c = st - > codec ;
# endif
c - > codec_id = codec_id ;
c - > codec_type = CODEC_TYPE_VIDEO ;
/* put sample parameters */
c - > bit_rate = 800000 ;
/* resolution must be a multiple of two */
c - > width = w ;
c - > height = h ;
/* frames per second */
# if LIBAVCODEC_BUILD<4754
c - > frame_rate = STREAM_FRAME_RATE ;
c - > frame_rate_base = 1 ;
# else
c - > time_base . den = STREAM_FRAME_RATE ;
c - > time_base . num = 1 ;
c - > pix_fmt = PIX_FMT_YUV420P ;
# endif
c - > gop_size = 12 ; /* emit one intra frame every twelve frames at most */
if ( c - > codec_id = = CODEC_ID_MPEG2VIDEO ) {
/* just for testing, we also add B frames */
c - > max_b_frames = 2 ;
/* find the encoder */
ost - > codec = avcodec_find_encoder ( codec_id ) ;
if ( ! ( ost - > codec ) ) {
fprintf ( stderr , " Could not find encoder for '%s' \n " ,
avcodec_get_name ( codec_id ) ) ;
return - 1 ;
} // no extra memory allocation from this call
if ( ost - > codec - > type ! = AVMEDIA_TYPE_VIDEO ) {
fprintf ( stderr , " Encoder for '%s' does not seem to be for video. \n " ,
avcodec_get_name ( codec_id ) ) ;
return - 2 ;
}
if ( c - > codec_id = = CODEC_ID_MPEG1VIDEO ) {
/* needed to avoid using macroblocks in which some coeffs overflow
this doesn ' t happen with normal video , it just happens here as the
motion of the chroma plane doesn ' t match the luma plane */
c - > mb_decision = 2 ;
ost - > enc = avcodec_alloc_context3 ( ost - > codec ) ;
if ( ! ( ost - > enc ) ) {
fprintf ( stderr , " Could not alloc an encoding context \n " ) ;
return - 3 ;
} // from now on need to call avcodec_free_context(&(ost->enc)) on error
/* Set codec parameters */
ost - > enc - > codec_id = codec_id ;
ost - > enc - > bit_rate = br ;
/* Resolution must be a multiple of two (round up to avoid buffer overflow). */
ost - > enc - > width = w + ( w % 2 ) ;
ost - > enc - > height = h + ( h % 2 ) ;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented . For fixed - fps content ,
* timebase should be 1 / framerate and timestamp increments should be
* identical to 1. */
ost - > enc - > time_base = ( AVRational ) { 1 , sr } ;
ost - > enc - > gop_size = 12 ; /* emit one intra frame every twelve frames at most */
ost - > enc - > pix_fmt = OUTPUT_PIX_FMT ;
if ( ost - > enc - > codec_id = = AV_CODEC_ID_MPEG1VIDEO ) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video , it just happens here as
* the motion of the chroma plane does not match the luma plane . */
ost - > enc - > mb_decision = 2 ;
}
/* some formats want stream headers to be separate */
if ( ! strcmp ( oc - > oformat - > name , " mp4 " ) | | ! strcmp ( oc - > oformat - > name , " mov " ) | | ! strcmp ( oc - > oformat - > name , " 3gp " ) )
c - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
return st ;
ost - > st = avformat_new_stream ( oc , ost - > codec ) ;
if ( ! ost - > st ) {
fprintf ( stderr , " Could not allocate stream \n " ) ;
avcodec_free_context ( & ( ost - > enc ) ) ;
return - 4 ;
} // stream memory cleared up when oc is freed, so no need to do so later in this function on error
ost - > st - > id = oc - > nb_streams - 1 ;
ost - > st - > time_base = ost - > enc - > time_base ;
ost - > pts = 0 ;
/* Some formats want stream headers to be separate. */
if ( oc - > oformat - > flags & AVFMT_GLOBALHEADER )
ost - > enc - > flags | = AV_CODEC_FLAG_GLOBAL_HEADER ;
// must wait to allocate frame buffers until codec is opened (in case codec changes the PIX_FMT)
return 0 ;
}
AVFrame * alloc_picture ( int pix_fmt , int width , int height )
AVFrame * alloc_picture ( enum AVPixelForma t pix_fmt , int width , int height )
{
AVFrame * picture ;
uint8_t * picture_buf ;
int size ;
picture = avcodec_alloc_frame ( ) ;
int ret ;
picture = av_frame_alloc ( ) ;
if ( ! picture )
return NULL ;
size = avpicture_get_size ( pix_fmt , width , height ) ;
picture_buf = malloc ( size ) ;
if ( ! picture_buf ) {
av_free ( picture ) ;
// from now on need to call av_frame_free(&picture) on error
picture - > format = pix_fmt ;
picture - > width = width ;
picture - > height = height ;
/* allocate the buffers for the frame data */
ret = av_frame_get_buffer ( picture , 64 ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Could not allocate frame data. \n " ) ;
av_frame_free ( & picture ) ;
return NULL ;
}
avpicture_fill ( ( AVPicture * ) picture , picture_buf ,
pix_fmt , width , height ) ;
return picture ;
}
void open_video ( AVFormatContext * oc , AVStream * st )
{
AVCodec * codec ;
AVCodecContext * c ;
# if LIBAVFORMAT_BUILD<4629
c = & st - > codec ;
# else
c = st - > codec ;
# endif
/* find the video encoder */
codec = avcodec_find_encoder ( c - > codec_id ) ;
if ( ! codec ) {
fprintf ( stderr , " codec not found \n " ) ;
exit ( 1 ) ;
}
} // use av_frame_free(&picture) to free memory from this call
int open_video ( AVFormatContext * oc , VideoOutputStream * ost )
{
int ret ;
/* open the codec */
if ( avcodec_open ( c , codec ) < 0 ) {
fprintf ( stderr , " could not open codec \n " ) ;
exit ( 1 ) ;
ret = avcodec_open2 ( ost - > enc , ost - > codec , NULL ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Could not open video codec: %s \n " , av_err2str ( ret ) ) ;
return ret ;
} // memory from this call freed when oc is freed, no need to do it on error in this call
/* copy the stream parameters to the muxer */
ret = avcodec_parameters_from_context ( ost - > st - > codecpar , ost - > enc ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Could not copy the stream parameters. \n " ) ;
return ret ;
} // memory from this call is freed when oc (parent of ost->st) is freed, no need to do it on error in this call
/* allocate and init a re-usable frame */
ost - > frame = alloc_picture ( ost - > enc - > pix_fmt , ost - > enc - > width , ost - > enc - > height ) ;
if ( ! ( ost - > frame ) ) {
fprintf ( stderr , " Could not allocate video frame \n " ) ;
return - 1 ;
} // from now on need to call av_frame_free(&(ost->frame)) on error
/* If the output format is not the same as the VNC format, then a temporary VNC format
* picture is needed too . It is then converted to the required
* output format . */
ost - > tmp_frame = NULL ;
ost - > sws = NULL ;
if ( ost - > enc - > pix_fmt ! = VNC_PIX_FMT ) {
ost - > tmp_frame = alloc_picture ( VNC_PIX_FMT , ost - > enc - > width , ost - > enc - > height ) ;
if ( ! ( ost - > tmp_frame ) ) {
fprintf ( stderr , " Could not allocate temporary picture \n " ) ;
av_frame_free ( & ( ost - > frame ) ) ;
return - 2 ;
} // from now on need to call av_frame_free(&(ost->tmp_frame)) on error
ost - > sws = sws_getCachedContext ( ost - > sws , ost - > enc - > width , ost - > enc - > height , VNC_PIX_FMT , ost - > enc - > width , ost - > enc - > height , ost - > enc - > pix_fmt , 0 , NULL , NULL , NULL ) ;
if ( ! ( ost - > sws ) ) {
fprintf ( stderr , " Could not get sws context \n " ) ;
av_frame_free ( & ( ost - > frame ) ) ;
av_frame_free ( & ( ost - > tmp_frame ) ) ;
return - 3 ;
} // from now on need to call sws_freeContext(ost->sws); ost->sws = NULL; on error
}
video_outbuf = NULL ;
if ( ! ( oc - > oformat - > flags & AVFMT_RAWPICTURE ) ) {
/* allocate output buffer */
/* XXX: API change will be done */
video_outbuf_size = 200000 ;
video_outbuf = malloc ( video_outbuf_size ) ;
}
return 0 ;
}
/* allocate the encoded raw picture */
picture = alloc_picture ( c - > pix_fmt , c - > width , c - > height ) ;
if ( ! picture ) {
fprintf ( stderr , " Could not allocate picture \n " ) ;
exit ( 1 ) ;
/*
* encode current video frame and send it to the muxer
* return 0 on success , negative on error
*/
int write_video_frame ( AVFormatContext * oc , VideoOutputStream * ost , int64_t pts )
{
int ret , ret2 ;
AVPacket pkt = { 0 } ;
if ( pts < = ost - > pts ) return 0 ; // nothing to do
/* convert format if needed */
if ( ost - > tmp_frame ) {
sws_scale ( ost - > sws , ( const uint8_t * const * ) ost - > tmp_frame - > data ,
ost - > tmp_frame - > linesize , 0 , ost - > enc - > height , ost - > frame - > data , ost - > frame - > linesize ) ;
}
/* if the output format is not RGB565, then a temporary RGB565
picture is needed too . It is then converted to the required
output format */
tmp_picture = NULL ;
if ( c - > pix_fmt ! = PIX_FMT_RGB565 ) {
tmp_picture = alloc_picture ( PIX_FMT_RGB565 , c - > width , c - > height ) ;
if ( ! tmp_picture ) {
fprintf ( stderr , " Could not allocate temporary picture \n " ) ;
exit ( 1 ) ;
/* send the imager to encoder */
ost - > pts = pts ;
ost - > frame - > pts = ost - > pts ;
ret = avcodec_send_frame ( ost - > enc , ost - > frame ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error sending video frame to encoder: %s \n " , av_err2str ( ret ) ) ;
return ret ;
}
/* read all available packets */
ret2 = 0 ;
for ( ret = avcodec_receive_packet ( ost - > enc , & pkt ) ; ret = = 0 ; ret = avcodec_receive_packet ( ost - > enc , & pkt ) ) {
ret2 = write_packet ( oc , & ( ost - > enc - > time_base ) , ost - > st , & pkt ) ;
if ( ret2 < 0 ) {
fprintf ( stderr , " Error while writing video frame: %s \n " , av_err2str ( ret2 ) ) ;
/* continue on this error to not gum up encoder */
}
}
if ( ret2 < 0 ) return ret2 ;
if ( ! ( ret = = AVERROR ( EAGAIN ) ) ) return ret ; // if AVERROR(EAGAIN), means all available packets output, need more frames (i.e. success)
return 0 ;
}
void write_video_frame ( AVFormatContext * oc , AVStream * st )
/*
* Write final video frame ( i . e . drain codec ) .
*/
int write_final_video_frame ( AVFormatContext * oc , VideoOutputStream * ost )
{
int out_size , ret ;
AVCodecContext * c ;
AVFrame * picture_ptr ;
# if LIBAVFORMAT_BUILD<4629
c = & st - > codec ;
# else
c = st - > codec ;
# endif
if ( c - > pix_fmt ! = PIX_FMT_RGB565 ) {
/* as we only generate a RGB565 picture, we must convert it
to the codec pixel format if needed */
img_convert ( ( AVPicture * ) picture , c - > pix_fmt ,
( AVPicture * ) tmp_picture , PIX_FMT_RGB565 ,
c - > width , c - > height ) ;
}
picture_ptr = picture ;
if ( oc - > oformat - > flags & AVFMT_RAWPICTURE ) {
/* raw video case. The API will change slightly in the near
futur for that */
AVPacket pkt ;
av_init_packet ( & pkt ) ;
pkt . flags | = PKT_FLAG_KEY ;
pkt . stream_index = st - > index ;
pkt . data = ( uint8_t * ) picture_ptr ;
pkt . size = sizeof ( AVPicture ) ;
ret = av_write_frame ( oc , & pkt ) ;
} else {
/* encode the image */
out_size = avcodec_encode_video ( c , video_outbuf , video_outbuf_size , picture_ptr ) ;
/* if zero size, it means the image was buffered */
if ( out_size ! = 0 ) {
AVPacket pkt ;
av_init_packet ( & pkt ) ;
pkt . pts = c - > coded_frame - > pts ;
if ( c - > coded_frame - > key_frame )
pkt . flags | = PKT_FLAG_KEY ;
pkt . stream_index = st - > index ;
pkt . data = video_outbuf ;
pkt . size = out_size ;
/* write the compressed frame in the media file */
ret = av_write_frame ( oc , & pkt ) ;
} else {
ret = 0 ;
}
int ret , ret2 ;
AVPacket pkt = { 0 } ;
/* send NULL image to encoder */
ret = avcodec_send_frame ( ost - > enc , NULL ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error sending final video frame to encoder: %s \n " , av_err2str ( ret ) ) ;
return ret ;
}
if ( ret ! = 0 ) {
fprintf ( stderr , " Error while writing video frame \n " ) ;
exit ( 1 ) ;
/* read all available packets */
ret2 = 0 ;
for ( ret = avcodec_receive_packet ( ost - > enc , & pkt ) ; ret = = 0 ; ret = avcodec_receive_packet ( ost - > enc , & pkt ) ) {
ret2 = write_packet ( oc , & ( ost - > enc - > time_base ) , ost - > st , & pkt ) ;
if ( ret2 < 0 ) {
fprintf ( stderr , " Error while writing final video frame: %s \n " , av_err2str ( ret2 ) ) ;
/* continue on this error to not gum up encoder */
}
}
frame_count + + ;
if ( ret2 < 0 ) return ret2 ;
if ( ! ( ret = = AVERROR ( EOF ) ) ) return ret ;
return 0 ;
}
void close_video ( AVFormatContext * oc , AVStream * st )
void close_video_stream ( VideoOutputStream * ost )
{
avcodec_close ( st - > codec ) ;
av_free ( picture - > data [ 0 ] ) ;
av_free ( picture ) ;
if ( tmp_picture ) {
av_free ( tmp_picture - > data [ 0 ] ) ;
av_free ( tmp_picture ) ;
}
av_free ( video_outbuf ) ;
avcodec_free_context ( & ( ost - > enc ) ) ;
av_frame_free ( & ( ost - > frame ) ) ;
av_frame_free ( & ( ost - > tmp_frame ) ) ;
sws_freeContext ( ost - > sws ) ; ost - > sws = NULL ;
ost - > codec = NULL ; /* codec not an allocated item */
ost - > st = NULL ; /* freeing parent oc will free this memory */
}
static const char * filename ;
static AVOutputFormat * fmt ;
static AVFormatContext * oc ;
static AVStream * video_st ;
static double video_pts ;
static int movie_open ( int w , int h ) {
if ( fmt - > video_codec ! = CODEC_ID_NONE ) {
video_st = add_video_stream ( oc , fmt - > video_codec , w , h ) ;
} else
return 1 ;
/* set the output parameters (must be done even if no
parameters ) . */
if ( av_set_parameters ( oc , NULL ) < 0 ) {
fprintf ( stderr , " Invalid output format parameters \n " ) ;
return 2 ;
/**************************************************************/
/* Output movie handling */
AVFormatContext * movie_open ( char * filename , VideoOutputStream * video_st , int br , int fr , int w , int h ) {
int ret ;
AVFormatContext * oc ;
/* allocate the output media context. */
ret = avformat_alloc_output_context2 ( & oc , NULL , NULL , filename ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Warning: Could not deduce output format from file extension: using MP4. \n " ) ;
ret = avformat_alloc_output_context2 ( & oc , NULL , " mp4 " , filename ) ;
}
if ( ret < 0 ) {
fprintf ( stderr , " Error: Could not allocate media context: %s. \n " , av_err2str ( ret ) ) ;
return NULL ;
} // from now on, need to call avformat_free_context(oc); oc=NULL; to free memory on error
dump_format ( oc , 0 , filename , 1 ) ;
/* now that all the parameters are set, we can open the audio and
video codecs and allocate the necessary encode buffers */
if ( video_st )
open_video ( oc , video_st ) ;
/* Add the video stream using the default format codec and initialize the codec. */
if ( oc - > oformat - > video_codec ! = AV_CODEC_ID_NONE ) {
ret = add_video_stream ( video_st , oc , oc - > oformat - > video_codec , br , fr , w , h ) ;
} else {
ret = - 1 ;
}
if ( ret < 0 ) {
fprintf ( stderr , " Error: chosen output format does not have a video codec, or error %i \n " , ret ) ;
avformat_free_context ( oc ) ; oc = NULL ;
return NULL ;
} // from now on, need to call close_video_stream(video_st) to free memory on error
/* Now that all the parameters are set, we can open the codecs and allocate the necessary encode buffers. */
ret = open_video ( oc , video_st ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error: error opening video codec, error %i \n " , ret ) ;
close_video_stream ( video_st ) ;
avformat_free_context ( oc ) ; oc = NULL ;
return NULL ;
} // no additional calls required to free memory, as close_video_stream(video_st) will do it
/* open the output file, if needed */
if ( ! ( fmt - > flags & AVFMT_NOFILE ) ) {
if ( url_fopen ( & oc - > pb , filename , URL_WRONLY ) < 0 ) {
fprintf ( stderr , " Could not open '%s' \n " , filename ) ;
return 3 ;
if ( ! ( oc - > oformat - > flags & AVFMT_NOFILE ) ) {
ret = avio_open ( & oc - > pb , filename , AVIO_FLAG_WRITE ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Could not open '%s': %s \n " , filename ,
av_err2str ( ret ) ) ;
close_video_stream ( video_st ) ;
avformat_free_context ( oc ) ; oc = NULL ;
return NULL ;
}
}
/* write the stream header, if any */
av_write_header ( oc ) ;
return 0 ;
} // will need to call avio_closep(&oc->pb) to free file handle on error
/* Write the stream header, if any. */
ret = avformat_write_header ( oc , NULL ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error occurred when writing to output file: %s \n " ,
av_err2str ( ret ) ) ;
if ( ! ( oc - > oformat - > flags & AVFMT_NOFILE ) )
avio_closep ( & oc - > pb ) ;
close_video_stream ( video_st ) ;
avformat_free_context ( oc ) ; oc = NULL ;
} // no additional items to free
return oc ;
}
static int movie_close ( ) {
int i ;
void movie_close ( AVFormatContext * * ocp , VideoOutputStream * video_st ) {
AVFormatContext * oc = * ocp ;
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header ; otherwise
* av_write_trailer ( ) may try to use memory that was freed on
* av_codec_close ( ) . */
if ( oc ) {
if ( video_st )
write_final_video_frame ( oc , video_st ) ;
/* close each codec */
close_video ( oc , video_st ) ;
av_write_trailer ( oc ) ;
/* write the trailer, if any */
av_write_trailer ( oc ) ;
/* free the streams */
for ( i = 0 ; i < oc - > nb_streams ; i + + ) {
av_freep ( & oc - > streams [ i ] ) ;
}
/* Close the video codec. */
close_video_stream ( video_st ) ;
if ( ! ( fmt - > flags & AVFMT_NOFILE ) ) {
/* close the output file */
url_fclose ( & oc - > pb ) ;
}
if ( ! ( oc - > oformat - > flags & AVFMT_NOFILE ) )
/* Close the output file. */
avio_closep ( & oc - > pb ) ;
/* free the stream */
av_free ( oc ) ;
/* free the stream */
avformat_free_context ( oc ) ;
ocp = NULL ;
}
}
/**************************************************************/
/* VNC globals */
VideoOutputStream video_st = { 0 } ;
rfbClient * client = NULL ;
rfbBool quit = FALSE ;
char * filename = NULL ;
AVFormatContext * oc = NULL ;
int bitrate = 1000000 ;
int framerate = 5 ;
long max_time = 0 ;
struct timespec start_time , cur_time ;
/* Signal handling */
void signal_handler ( int signal ) {
quit = TRUE ;
}
static rfbBool quit = FALSE ;
static void signal_handler ( int signal ) {
fprintf ( stderr , " Cleaning up. \n " ) ;
quit = TRUE ;
/* returns time since start in pts units */
int64_t time_to_pts ( int framerate , struct timespec * start_time , struct timespec * cur_time ) {
time_t ds = cur_time - > tv_sec - start_time - > tv_sec ;
long dns = cur_time - > tv_nsec - start_time - > tv_nsec ;
/* use usecs */
int64_t dt = ( int64_t ) ds * ( int64_t ) 1000000 + ( int64_t ) dns / ( int64_t ) 1000 ;
/* compute rv in units of frame number (rounding to nearest, not truncating) */
int64_t rv = ( ( ( int64_t ) framerate ) * dt + ( int64_t ) 500000 ) / ( int64_t ) ( 1000000 ) ;
return rv ;
}
/**************************************************************/
/* VNC callback functions */
static rfbBool resize ( rfbClient * client ) {
static rfbBool first = TRUE ;
if ( ! first ) {
movie_close ( ) ;
perror ( " I don't know yet how to change resolutions! \n " ) ;
}
movie_open ( client - > width , client - > height ) ;
signal ( SIGINT , signal_handler ) ;
if ( tmp_picture )
client - > frameBuffer = tmp_picture - > data [ 0 ] ;
else
client - > frameBuffer = picture - > data [ 0 ] ;
return TRUE ;
rfbBool vnc_malloc_fb ( rfbClient * client ) {
movie_close ( & oc , & video_st ) ;
oc = movie_open ( filename , & video_st , bitrate , framerate , client - > width , client - > height ) ;
if ( ! oc )
return FALSE ;
signal ( SIGINT , signal_handler ) ;
signal ( SIGTERM , signal_handler ) ;
signal ( SIGQUIT , signal_handler ) ;
signal ( SIGABRT , signal_handler ) ;
/* These assignments assumes the AVFrame buffer is contigous. This is true in current ffmpeg versions for
* most non - HW accelerated bits , but may not be true globally . */
if ( video_st . tmp_frame )
client - > frameBuffer = video_st . tmp_frame - > data [ 0 ] ;
else
client - > frameBuffer = video_st . frame - > data [ 0 ] ;
return TRUE ;
}
static void update ( rfbClient * client , int x , int y , int w , int h ) {
void vnc_ update( rfbClient * client , int x , int y , int w , int h ) {
}
/**************************************************************/
/* media file output */
int main ( int argc , char * * argv )
{
time_t stop = 0 ;
rfbClient * client ;
int i , j ;
/* get a vnc client structure (don't connect yet). */
/* Initialize vnc client structure (don't connect yet). */
client = rfbGetClient ( 5 , 3 , 2 ) ;
client - > format . redShift = 11 ; client - > format . redMax = 31 ;
client - > format . greenShift = 5 ; client - > format . greenMax = 63 ;
client - > format . blueShift = 0 ; client - > format . blueMax = 31 ;
/* i nitialize libavcodec, and register all codecs and formats */
/* I nitialize libavcodec, and register all codecs and formats. */
av_register_all ( ) ;
if ( ! strncmp ( argv [ argc - 1 ] , " : " , 1 ) | |
! strncmp ( argv [ argc - 1 ] , " 127.0.0.1 " , 9 ) | |
! strncmp ( argv [ argc - 1 ] , " localhost " , 9 ) )
client - > appData . encodingsString = " raw " ;
filename = 0 ;
/* Parse command line. */
for ( i = 1 ; i < argc ; i + + ) {
j = i ;
if ( argc > i + 1 & & ! strcmp ( " -o " , argv [ i ] ) ) {
filename = argv [ 2 ] ;
j + = 2 ;
} else if ( argc > i + 1 & & ! strcmp ( " -t " , argv [ i ] ) ) {
stop = time ( 0 ) + atoi ( argv [ i + 1 ] ) ;
j + = 2 ;
}
if ( j > i ) {
argc - = j - i ;
memmove ( argv + i , argv + j , ( argc - i ) * sizeof ( char * ) ) ;
i - - ;
}
j = i ;
if ( argc > i + 1 & & ! strcmp ( " -o " , argv [ i ] ) ) {
filename = argv [ i + 1 ] ;
j + = 2 ;
} else if ( argc > i + 1 & & ! strcmp ( " -t " , argv [ i ] ) ) {
max_time = atol ( argv [ i + 1 ] ) ;
if ( max_time < 10 | | max_time > 100000000 ) {
fprintf ( stderr , " Warning: Nonsensical time-per-file %li, resetting to default. \n " , max_time ) ;
max_time = 0 ;
}
j + = 2 ;
}
/* This is so that argc/argv are ready for passing to rfbInitClient */
if ( j > i ) {
argc - = j - i ;
memmove ( argv + i , argv + j , ( argc - i ) * sizeof ( char * ) ) ;
i - - ;
}
}
/* auto detect the output format from the name. default is
mpeg . */
fmt = filename ? guess_format ( NULL , filename , NULL ) : 0 ;
if ( ! fmt ) {
printf ( " Could not deduce output format from file extension: using MPEG. \n " ) ;
fmt = guess_format ( " mpeg " , NULL , NULL ) ;
}
if ( ! fmt ) {
fprintf ( stderr , " Could not find suitable output format \n " ) ;
exit ( 1 ) ;
/* default filename. */
if ( ! filename ) {
fprintf ( stderr , " Warning: No filename specified. Using output.mp4 \n " ) ;
filename = " output.mp4 " ;
}
/* allocate the output media context */
oc = av_alloc_format_context ( ) ;
if ( ! oc ) {
fprintf ( stderr , " Memory error \n " ) ;
exit ( 1 ) ;
}
oc - > oformat = fmt ;
snprintf ( oc - > filename , sizeof ( oc - > filename ) , " %s " , filename ) ;
/* add the audio and video streams using the default format codecs
and initialize the codecs */
video_st = NULL ;
/* open VNC connection */
client - > MallocFrameBuffer = resize ;
client - > GotFrameBufferUpdate = update;
/* open VNC connection. */
client - > MallocFrameBuffer = vnc_malloc_fb ;
client - > GotFrameBufferUpdate = vnc_update ;
if ( ! rfbInitClient ( client , & argc , argv ) ) {
printf ( " usage: %s [-o output_file] [-t seconds] server:port \n "
" Shoot a movie from a VNC server. \n " , argv [ 0 ] ) ;
exit ( 1 ) ;
printf ( " usage: %s [-o output_file] [-t seconds-per-file] server:port \n " , argv [ 0 ] ) ;
return 1 ;
}
if ( client - > serverPort = = - 1 )
client - > vncRec - > doNotSleep = TRUE ; /* vncrec playback */
/* main loop */
/* main loop */
clock_gettime ( CLOCK_MONOTONIC , & start_time ) ;
while ( ! quit ) {
int i = WaitForMessage ( client , 1000000 / STREAM_FRAME_RATE ) ;
if ( i < 0 ) {
movie_close ( ) ;
return 0 ;
int i = WaitForMessage ( client , 10000 / framerate ) ; /* useful for timeout to be no more than 10 msec per second (=10000/framerate usec) */
if ( i > 0 ) {
if ( ! HandleRFBServerMessage ( client ) )
quit = TRUE ;
} else if ( i < 0 ) {
quit = TRUE ;
}
if ( i )
if ( ! HandleRFBServerMessage ( client ) )
quit = TRUE ;
else {
/* compute current audio and video time */
video_pts = ( double ) video_st - > pts . val * video_st - > time_base . num / video_st - > time_base . den ;
/* write interleaved audio and video frames */
write_video_frame ( oc , video_st ) ;
}
if ( stop ! = 0 & & stop < time ( 0 ) )
quit = TRUE ;
if ( ! quit ) {
clock_gettime ( CLOCK_MONOTONIC , & cur_time ) ;
write_video_frame ( oc , & video_st , update_time_for_next ( framerate , & start_time , & cur_time ) ) ;
if ( ( cur_time . tv_sec - start_time . tv_sec ) > max_time & & max_time > 0 ) {
quit = TRUE ;
}
}
}
movie_close ( ) ;
movie_close ( & oc , & video_st ) ;
return 0 ;
}