- render video using double buffers
this is to deliver frames more accurately to the client
This commit is contained in:
parent
448d287240
commit
a4f597696a
|
@ -419,7 +419,8 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
|
||||||
do_streaming(true),
|
do_streaming(true),
|
||||||
buffer_copy(NULL),
|
buffer_copy(NULL),
|
||||||
buffer_copy_lock(new pthread_mutex_t),
|
buffer_copy_lock(new pthread_mutex_t),
|
||||||
buffer_copy_used(0)
|
buffer_copy_used(0),
|
||||||
|
packet_index(0)
|
||||||
{
|
{
|
||||||
if ( !initialised )
|
if ( !initialised )
|
||||||
{
|
{
|
||||||
|
@ -443,6 +444,12 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
|
||||||
SetupFormat( );
|
SetupFormat( );
|
||||||
SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate );
|
SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate );
|
||||||
SetParameters( );
|
SetParameters( );
|
||||||
|
|
||||||
|
// Allocate buffered packets.
|
||||||
|
packet_buffers = new AVPacket*[2];
|
||||||
|
packet_buffers[0] = new AVPacket();
|
||||||
|
packet_buffers[1] = new AVPacket();
|
||||||
|
packet_index = 0;
|
||||||
|
|
||||||
// Initialize mutex used by streaming thread.
|
// Initialize mutex used by streaming thread.
|
||||||
if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 )
|
if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 )
|
||||||
|
@ -471,6 +478,7 @@ VideoStream::~VideoStream( )
|
||||||
{
|
{
|
||||||
av_free( buffer_copy );
|
av_free( buffer_copy );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( buffer_copy_lock )
|
if ( buffer_copy_lock )
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 )
|
if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 )
|
||||||
|
@ -479,6 +487,12 @@ VideoStream::~VideoStream( )
|
||||||
}
|
}
|
||||||
delete buffer_copy_lock;
|
delete buffer_copy_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packet_buffers) {
|
||||||
|
delete packet_buffers[0];
|
||||||
|
delete packet_buffers[1];
|
||||||
|
delete packet_buffers;
|
||||||
|
}
|
||||||
|
|
||||||
/* close each codec */
|
/* close each codec */
|
||||||
if ( ost )
|
if ( ost )
|
||||||
|
@ -601,19 +615,19 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
}
|
}
|
||||||
AVFrame *opicture_ptr = opicture;
|
AVFrame *opicture_ptr = opicture;
|
||||||
|
|
||||||
AVPacket pkt = { 0 };
|
AVPacket *pkt = packet_buffers[packet_index];
|
||||||
av_init_packet( &pkt );
|
av_init_packet( pkt );
|
||||||
int got_packet = 0;
|
int got_packet = 0;
|
||||||
if ( of->flags & AVFMT_RAWPICTURE )
|
if ( of->flags & AVFMT_RAWPICTURE )
|
||||||
{
|
{
|
||||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 2, 1)
|
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 2, 1)
|
||||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
#else
|
#else
|
||||||
pkt.flags |= PKT_FLAG_KEY;
|
pkt->flags |= PKT_FLAG_KEY;
|
||||||
#endif
|
#endif
|
||||||
pkt.stream_index = ost->index;
|
pkt->stream_index = ost->index;
|
||||||
pkt.data = (uint8_t *)opicture_ptr;
|
pkt->data = (uint8_t *)opicture_ptr;
|
||||||
pkt.size = sizeof (AVPicture);
|
pkt->size = sizeof (AVPicture);
|
||||||
got_packet = 1;
|
got_packet = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -622,7 +636,7 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
opicture_ptr->quality = c->global_quality;
|
opicture_ptr->quality = c->global_quality;
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 0, 0)
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 0, 0)
|
||||||
int ret = avcodec_encode_video2( c, &pkt, opicture_ptr, &got_packet );
|
int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet );
|
||||||
if ( ret != 0 )
|
if ( ret != 0 )
|
||||||
{
|
{
|
||||||
Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) );
|
Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) );
|
||||||
|
@ -630,46 +644,47 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
#else
|
#else
|
||||||
int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr );
|
int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr );
|
||||||
got_packet = out_size > 0 ? 1 : 0;
|
got_packet = out_size > 0 ? 1 : 0;
|
||||||
pkt.data = got_packet ? video_outbuf : NULL;
|
pkt->data = got_packet ? video_outbuf : NULL;
|
||||||
pkt.size = got_packet ? out_size : 0;
|
pkt->size = got_packet ? out_size : 0;
|
||||||
#endif
|
#endif
|
||||||
if ( got_packet )
|
if ( got_packet )
|
||||||
{
|
{
|
||||||
if ( c->coded_frame->key_frame )
|
if ( c->coded_frame->key_frame )
|
||||||
{
|
{
|
||||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1)
|
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1)
|
||||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
#else
|
#else
|
||||||
pkt.flags |= PKT_FLAG_KEY;
|
pkt->flags |= PKT_FLAG_KEY;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pkt.pts != (int64_t)AV_NOPTS_VALUE )
|
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE )
|
||||||
{
|
{
|
||||||
pkt.pts = av_rescale_q( pkt.pts, c->time_base, ost->time_base );
|
pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base );
|
||||||
}
|
}
|
||||||
if ( pkt.dts != (int64_t)AV_NOPTS_VALUE )
|
if ( pkt->dts != (int64_t)AV_NOPTS_VALUE )
|
||||||
{
|
{
|
||||||
pkt.dts = av_rescale_q( pkt.dts, c->time_base, ost->time_base );
|
pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base );
|
||||||
}
|
}
|
||||||
pkt.duration = av_rescale_q( pkt.duration, c->time_base, ost->time_base );
|
pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base );
|
||||||
pkt.stream_index = ost->index;
|
pkt->stream_index = ost->index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( got_packet )
|
|
||||||
{
|
|
||||||
int ret = av_write_frame( ofc, &pkt );
|
|
||||||
if ( ret != 0 )
|
|
||||||
{
|
|
||||||
Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
av_free_packet(&pkt);
|
|
||||||
return ( opicture_ptr->pts);
|
return ( opicture_ptr->pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VideoStream::SendPacket(AVPacket *packet) {
|
||||||
|
|
||||||
|
int ret = av_write_frame( ofc, packet );
|
||||||
|
if ( ret != 0 )
|
||||||
|
{
|
||||||
|
Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) );
|
||||||
|
}
|
||||||
|
av_free_packet(packet);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void *VideoStream::StreamingThreadCallback(void *ctx){
|
void *VideoStream::StreamingThreadCallback(void *ctx){
|
||||||
|
|
||||||
Debug( 1, "StreamingThreadCallback started" );
|
Debug( 1, "StreamingThreadCallback started" );
|
||||||
|
@ -697,7 +712,21 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
|
||||||
// It's not time to render a frame yet.
|
// It's not time to render a frame yet.
|
||||||
usleep( (target_ns - current_time_ns) * 0.001 );
|
usleep( (target_ns - current_time_ns) * 0.001 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// By sending the last rendered frame we deliver frames to the client more accurate.
|
||||||
|
// If we're encoding the frame before sending it there will be lag.
|
||||||
|
// Since this lag is not constant the client may skip frames.
|
||||||
|
|
||||||
|
// Get the last rendered packet.
|
||||||
|
AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index];
|
||||||
|
if (packet->size) {
|
||||||
|
videoStream->SendPacket(packet);
|
||||||
|
}
|
||||||
|
av_free_packet(packet);
|
||||||
|
videoStream->packet_index = videoStream->packet_index ? 0 : 1;
|
||||||
|
|
||||||
|
// Lock buffer and render next frame.
|
||||||
|
|
||||||
if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 )
|
if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 )
|
||||||
{
|
{
|
||||||
Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." );
|
Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." );
|
||||||
|
@ -705,7 +734,7 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
|
||||||
|
|
||||||
if ( videoStream->buffer_copy )
|
if ( videoStream->buffer_copy )
|
||||||
{
|
{
|
||||||
// Encode and transmit frame.
|
// Encode next frame.
|
||||||
videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp );
|
videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,9 @@ protected:
|
||||||
pthread_mutex_t *buffer_copy_lock;
|
pthread_mutex_t *buffer_copy_lock;
|
||||||
int buffer_copy_size;
|
int buffer_copy_size;
|
||||||
int buffer_copy_used;
|
int buffer_copy_used;
|
||||||
|
AVPacket** packet_buffers;
|
||||||
|
int packet_index;
|
||||||
|
int SendPacket(AVPacket *packet);
|
||||||
static void* StreamingThreadCallback(void *ctx);
|
static void* StreamingThreadCallback(void *ctx);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Reference in New Issue