[Libav-user] achieving greater than 15 fps playback
Blake Senftner
bsenftner at earthlink.net
Sun Dec 18 19:22:33 EET 2016
Hello,
Working on video (no audio) playback library, I am only seeing frame rates of 15 fps or less. My code started based on the Dranger examples, but has since been updated to the latest APIs (such that I compile with no depreciated warnings.) I am using Visual Studio 2013 Community.
I have a 160x40 29.97 fps h264 clip with timecodes, and it plays back at exactly half speed, consuming very little cpu. An rtsp h264 stream from an IP camera plays back between 10 & 11 fps, while the latest full HD resolution Rogue One trailer plays back at 15 fps (both only consuming < 20% CPU.)
My code does not use SDL, so I am using neosmart cross platform pevents (https://github.com/neosmart/pevents).
I am using one thread to read packets, decode them to yuv & convert to RGB. OpenGL does the display in the main thread.
Does achieving higher frame rates require the packet reading and packet decoding in separate threads? I thought the 160x40 file would decompress fast enough that I’d need to add necessary timing logic to delay the frames, but that is not the case. It plays
Here’s the general logic of my playback setup, abbreviated for clarity, and the packet reading is below that:
------------------------------------------------------------------------------------------
avformat_open_input( &mp_format_context, [path to file or stream], NULL, NULL );
avformat_find_stream_info( mp_format_context, NULL );
m_video_stream = -1;
for (ce_uint i = 0; i < (ce_uint)mp_format_context->nb_streams; i++) {
if (mp_format_context->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
m_video_stream = i;
break;
}
}
if (m_video_stream == -1)
return false;
AVCodecParameters* codecPars = mp_format_context->streams[m_video_stream]->codecpar;
AVCodec* pCodec = avcodec_find_decoder( codecPars->codec_id );
if (!pCodec)
return false;
mp_codec_context = avcodec_alloc_context3( pCodec );
if (avcodec_parameters_to_context( mp_codec_context, codecPars ) != 0)
return false;
mp_codec_context->thread_count = 0; // tried 4, 8… makes no difference
AVDictionary* codec_options = NULL;
av_dict_set( &codec_options, "threads", "auto", 0 ); // makes no difference, tried 4 & 8 too
if (m_stream_type == 2)
av_dict_set( &codec_options, "rtsp_transport", "tcp", 0 ); // needed for ffplay to work with my ip cam, makes no difference here...
if (avcodec_open2( mp_codec_context, pCodec, &codec_options ) < 0)
return false;
mp_decompressed_frame = av_frame_alloc();
mp_display_frame = av_frame_alloc();
if (mp_decompressed_frame == NULL || mp_display_frame == NULL)
return false;
ce_int numBytes = av_image_get_buffer_size( AV_PIX_FMT_RGBA, mp_codec_context->width, mp_codec_context->height, 1 );
mp_display_buffer = (uint8_t *)av_malloc( numBytes*sizeof(uint8_t) );
av_image_fill_arrays( mp_display_frame->data, mp_display_frame->linesize,
mp_display_buffer, AV_PIX_FMT_RGBA,
mp_codec_context->width, mp_codec_context->height, 1 );
mp_sws_context = sws_getContext( mp_codec_context->width, mp_codec_context->height, mp_codec_context->pix_fmt,
mp_codec_context->width, mp_codec_context->height, AV_PIX_FMT_RGBA,
SWS_BILINEAR, NULL, NULL, NULL );
—————————————————————————————————————————
In my packet reading thread, the logic is simple:
——————————————————————————————————
uint64_t milliseconds = 1;
bool media_has_ended = false;
bool camera_has_terminated = false;
ce_uint packet_errors = 0;
while (true) {
// this basically allows the thread to idle (sleep) but it will
// wake up if the events are signaled
dwWaitRes = neosmart::WaitForEvent(m_stop_local_spin_event, milliseconds);
if (0 == dwWaitRes) // stop event
break;
else if (m_is_playing && !m_paused) {
// I’ve got 16 packet buffers so I can look at their data, but only use one at a time:
ce_uint curr_packet_index = m_total_video_packets % CE_LIBAV_NUMVIDPKTS;
AVPacket* curr_packet = &m_packet[ curr_packet_index ];
m_total_video_packets++;
int stream_status = av_read_frame(mp_format_context, curr_packet);
if (stream_status < 0) {
if (m_stream_type == 0) { // end of media file // stream_types: 0=Media, 1=USB, 2=IP
media_has_ended = true;
break;
}
else { // camera stream has terminated unexpectedly
camera_has_terminated = true;
break;
}
}
else { // stream delivered a packet fine
if (curr_packet->stream_index == m_video_stream) {
// Decode video frame(s):
int ret = avcodec_send_packet( mp_codec_context, curr_packet );
if (ret < 0) {
if (ret == AVERROR_EOF && m_stream_type == 0) {
media_has_ended = true;
break;
}
else packet_errors++;
}
else {
while (!ret)
{
ret = avcodec_receive_frame( mp_codec_context, mp_decompressed_frame );
if (!ret)
handle_new_frame( curr_packet, curr_packet_index );
}
}
av_packet_unref(curr_packet);
} // end stream delivered a packet
} // end if (m_is_playing && !m_paused)
} // end while (true)
if (media_has_ended) {
if (mp_stream_ended_callback)
(mp_stream_ended_callback)(m_frames_received, mp_stream_ended_object);
if (camera_has_terminated)
if (mp_term_callback)
(mp_term_callback)(mp_term_object);
——————————————————————————————————
Sincerely,
-Blake Senftner
Mad Computer Scientist
p.s.
My ffmpeg libav* 3.2 libs are, downloaded pre-built from Zeranoe early November, my versions are:
libavutil 55. 34.100 / 55. 34.100
libavcodec 57. 64.100 / 57. 64.100
libavformat 57. 56.100 / 57. 56.100
libavdevice 57. 1.100 / 57. 1.100
libavfilter 6. 65.100 / 6. 65.100
libswscale 4. 2.100 / 4. 2.100
libswresample 2. 3.100 / 2. 3.100
libpostproc 54. 1.100 / 54. 1.100
More information about the Libav-user
mailing list