+
+AVFrameWithDeleter FFmpegCapture::decode_frame(AVFormatContext *format_ctx, AVCodecContext *codec_ctx, const std::string &pathname, int video_stream_index, bool *error)
+{
+ *error = false;
+
+ // Read packets until we have a frame or there are none left.
+ bool frame_finished = false;
+ AVFrameWithDeleter frame = av_frame_alloc_unique();
+ bool eof = false;
+ do {
+ AVPacket pkt;
+ unique_ptr<AVPacket, decltype(av_packet_unref)*> pkt_cleanup(
+ &pkt, av_packet_unref);
+ av_init_packet(&pkt);
+ pkt.data = nullptr;
+ pkt.size = 0;
+ if (av_read_frame(format_ctx, &pkt) == 0) {
+ if (pkt.stream_index != video_stream_index) {
+ // Ignore audio for now.
+ continue;
+ }
+ if (avcodec_send_packet(codec_ctx, &pkt) < 0) {
+ fprintf(stderr, "%s: Cannot send packet to codec.\n", pathname.c_str());
+ *error = true;
+ return AVFrameWithDeleter(nullptr);
+ }
+ } else {
+ eof = true; // Or error, but ignore that for the time being.
+ }
+
+ int err = avcodec_receive_frame(codec_ctx, frame.get());
+ if (err == 0) {
+ frame_finished = true;
+ break;
+ } else if (err != AVERROR(EAGAIN)) {
+ fprintf(stderr, "%s: Cannot receive frame from codec.\n", pathname.c_str());
+ *error = true;
+ return AVFrameWithDeleter(nullptr);
+ }
+ } while (!eof);
+
+ if (frame_finished)
+ return frame;
+ else
+ return AVFrameWithDeleter(nullptr);
+}