Handle case where time_base is not set in the codec. Fixes h265 not playing right. Don't eek until a significant difference in time. Lots more debug output. Pass codec when initialising codec context. Mostly this fixes playback of h265

pull/3609/head
Isaac Connor 2022-08-16 15:16:22 -04:00
parent 5f435cf0ba
commit 866edc2990
1 changed files with 57 additions and 36 deletions

View File

@ -31,8 +31,7 @@ int FFmpeg_Input::Open(
const AVStream * audio_in_stream,
const AVCodecContext * audio_in_ctx
) {
video_stream_id = video_in_stream->index;
int max_stream_index = video_in_stream->index;
int max_stream_index = video_stream_id = video_in_stream->index;
if (audio_in_stream) {
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
@ -88,14 +87,8 @@ int FFmpeg_Input::Open(const char *filepath) {
}
streams[i].frame_count = 0;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
streams[i].context = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(streams[i].context, input_format_context->streams[i]->codecpar);
#else
streams[i].context = input_format_context->streams[i]->codec;
#endif
if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) {
if (!(streams[i].codec = avcodec_find_decoder(input_format_context->streams[i]->codecpar->codec_id))) {
Error("Could not find input codec");
avformat_close_input(&input_format_context);
return AVERROR_EXIT;
@ -103,6 +96,18 @@ int FFmpeg_Input::Open(const char *filepath) {
Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i);
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
streams[i].context = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(streams[i].context, input_format_context->streams[i]->codecpar);
#else
streams[i].context = input_format_context->streams[i]->codec;
#endif
avcodec_parameters_to_context(streams[i].context, input_format_context->streams[i]->codecpar);
// Some codecs will change the time base others might not. h265 seems to not. So let's set a sane value
streams[i].context->time_base.num = 1;
streams[i].context->time_base.den = 90000;
zm_dump_codec(streams[i].context);
error = avcodec_open2(streams[i].context, streams[i].codec, nullptr);
if (error < 0) {
Error("Could not open input codec (error '%s')",
@ -114,6 +119,12 @@ int FFmpeg_Input::Open(const char *filepath) {
input_format_context = nullptr;
return error;
}
zm_dump_codec(streams[i].context);
if (!(streams[i].context->time_base.num && streams[i].context->time_base.den)) {
Warning("Setting to default time base 1/90000");
streams[i].context->time_base.num = 1;
streams[i].context->time_base.den = 90000;
}
} // end foreach stream
if (video_stream_id == -1)
@ -206,13 +217,22 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
}
}
frameComplete = 1;
frameComplete = true;
if (context->time_base.num && context->time_base.den) {
// Convert timestamps to stream timebase instead of codec timebase
frame->pts = av_rescale_q(frame->pts,
context->time_base,
input_format_context->streams[stream_id]->time_base
);
} else {
Warning("No timebase set in context!");
}
if (is_video_stream(input_format_context->streams[packet.stream_index])) {
zm_dump_video_frame(frame, "resulting video frame");
} else {
zm_dump_frame(frame, "resulting frame");
}
zm_av_packet_unref(&packet);
} // end while !frameComplete
@ -274,7 +294,8 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
last_seek_request = seek_target;
if (frame->pts + frame->pkt_duration < seek_target) {
// Normally it is likely just the next packet. Need a heuristic for seeking, something like duration * keyframe interval
if (frame->pts + 10*frame->pkt_duration < seek_target) {
Debug(1, "Jumping ahead");
if (( ret = av_seek_frame(input_format_context, stream_id, seek_target,
AVSEEK_FLAG_FRAME
@ -287,12 +308,12 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
}
// Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want.
if (frame->pts <= seek_target) {
while (frame && (frame->pts + frame->pkt_duration < seek_target)) {
if (is_video_stream(input_format_context->streams[stream_id])) {
zm_dump_video_frame(frame, "pts <= seek_target");
} else {
zm_dump_frame(frame, "pts <= seek_target");
}
while ( frame && (frame->pts < seek_target) ) {
if (!get_frame(stream_id)) {
Warning("Got no frame. returning nothing");
return frame;