/* EOF can happen if we've already sent a flush packet */
throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::flush"), r);
}
- r = avcodec_receive_frame (context, _frame);
+ r = avcodec_receive_frame (context, audio_frame(i));
if (r >= 0) {
process_audio_frame (i);
did_something = true;
auto const f = full_length.frames_round (vfr);
auto v = video->position(film()).get_value_or(ContentTime()).frames_round(vfr) + 1;
while (v < f) {
- video->emit (film(), shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)), v);
+ video->emit (film(), make_shared<const RawImageProxy>(_black_image), v);
++v;
}
}
for (auto& i: _next_time) {
i.second = boost::optional<dcpomatic::ContentTime>();
}
-
- /* We find that we get some errors from av_send_packet after a seek. Perhaps we should ignore
- * all of them (which seems risky), or perhaps we should have some proper fix. But instead
- * let's ignore the next 2 errors.
- */
- _errors_to_ignore = 2;
}
void
FFmpegDecoder::process_audio_frame (shared_ptr<FFmpegAudioStream> stream)
{
- auto data = deinterleave_audio (_frame);
+ auto frame = audio_frame (stream);
+ auto data = deinterleave_audio (frame);
ContentTime ct;
- if (_frame->pts == AV_NOPTS_VALUE) {
+ if (frame->pts == AV_NOPTS_VALUE) {
/* In some streams we see not every frame coming through with a timestamp; for those
that have AV_NOPTS_VALUE we need to work out the timestamp ourselves. This is
particularly noticeable with TrueHD streams (see #1111).
}
} else {
ct = ContentTime::from_seconds (
- _frame->best_effort_timestamp *
+ frame->best_effort_timestamp *
av_q2d (stream->stream(_format_context)->time_base))
+ _pts_offset;
}
to_string(ct),
data->frames(),
stream->id(),
- _frame->best_effort_timestamp,
+ frame->best_effort_timestamp,
av_q2d(stream->stream(_format_context)->time_base),
to_string(_pts_offset)
);
}
auto context = _codec_context[stream->index(_format_context)];
+ auto frame = audio_frame (stream);
int r = avcodec_send_packet (context, packet);
if (r < 0) {
- /* We could cope with AVERROR(EAGAIN) and re-send the packet but I think it should never happen.
- * Likewise I think AVERROR_EOF should not happen.
- */
- if (_errors_to_ignore > 0) {
- /* We see errors here after a seek, which is hopefully to be nothing to worry about */
- --_errors_to_ignore;
- LOG_GENERAL("Ignoring error %1 avcodec_send_packet after seek; will ignore %2 more", r, _errors_to_ignore);
- return;
- }
- throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::decode_and_process_audio_packet"), r);
+ LOG_WARNING("avcodec_send_packet returned %1 for an audio packet", r);
}
-
while (r >= 0) {
- r = avcodec_receive_frame (context, _frame);
+ r = avcodec_receive_frame (context, frame);
if (r == AVERROR(EAGAIN)) {
/* More input is required */
return;
auto context = video_codec_context();
- int r = avcodec_send_packet (context, packet);
- if (r < 0 && !(r == AVERROR_EOF && !packet)) {
- /* We could cope with AVERROR(EAGAIN) and re-send the packet but I think it should never happen.
- * AVERROR_EOF can happen during flush if we've already sent a flush packet.
- */
- throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::decode_and_process_video_packet"), r);
- }
+ bool pending = false;
+ do {
+ int r = avcodec_send_packet (context, packet);
+ if (r < 0) {
+ LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
+ }
- r = avcodec_receive_frame (context, _frame);
- if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
- /* More input is required, no more frames are coming, or we are flushing and there was
- * some error which we just want to ignore.
- */
- return false;
- } else if (r < 0) {
- throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
- }
+ /* EAGAIN means we should call avcodec_receive_frame and then re-send the same packet */
+ pending = r == AVERROR(EAGAIN);
+
+ while (true) {
+ r = avcodec_receive_frame (context, _video_frame);
+ if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
+ /* More input is required, no more frames are coming, or we are flushing and there was
+ * some error which we just want to ignore.
+ */
+ return false;
+ } else if (r < 0) {
+ throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
+ }
+
+ process_video_frame ();
+ }
+ } while (pending);
- /* We assume we'll only get one frame here, which I think is safe */
+ return true;
+}
+
+void
+FFmpegDecoder::process_video_frame ()
+{
boost::mutex::scoped_lock lm (_filter_graphs_mutex);
shared_ptr<VideoFilterGraph> graph;
auto i = _filter_graphs.begin();
- while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+ while (i != _filter_graphs.end() && !(*i)->can_process(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format)) {
++i;
}
if (i == _filter_graphs.end ()) {
dcp::Fraction vfr (lrint(_ffmpeg_content->video_frame_rate().get() * 1000), 1000);
- graph = make_shared<VideoFilterGraph>(dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format, vfr);
+ graph = make_shared<VideoFilterGraph>(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format, vfr);
graph->setup (_ffmpeg_content->filters ());
_filter_graphs.push_back (graph);
- LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
+ LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _video_frame->width, _video_frame->height, _video_frame->format);
} else {
graph = *i;
}
- auto images = graph->process (_frame);
+ auto images = graph->process (_video_frame);
for (auto const& i: images) {
LOG_WARNING_NC ("Dropping frame without PTS");
}
}
-
- return true;
}