Check the working queue, and move finished frames to the finished queue
| 2726 | |
| 2727 | // Check the working queue, and move finished frames to the finished queue |
| 2728 | void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) { |
| 2729 | |
| 2730 | // Prevent async calls to the following code |
| 2731 | const std::lock_guard<std::recursive_mutex> lock(getFrameMutex); |
| 2732 | |
| 2733 | // Get a list of current working queue frames in the cache (in-progress frames) |
| 2734 | std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.GetFrames(); |
| 2735 | std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr; |
| 2736 | |
| 2737 | // Loop through all working queue frames (sorted by frame #) |
| 2738 | for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr) |
| 2739 | { |
| 2740 | // Get working frame |
| 2741 | std::shared_ptr<Frame> f = *working_itr; |
| 2742 | |
| 2743 | // Was a frame found? Is frame requested yet? |
| 2744 | if (!f || f->number > requested_frame) { |
| 2745 | // If not, skip to next one |
| 2746 | continue; |
| 2747 | } |
| 2748 | |
| 2749 | // Calculate PTS in seconds (of working frame), and the most recent processed pts value |
| 2750 | double frame_pts_seconds = (double(f->number - 1) / info.fps.ToDouble()) + pts_offset_seconds; |
| 2751 | double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds); |
| 2752 | |
| 2753 | // Determine if video and audio are ready (based on timestamps) |
| 2754 | bool is_video_ready = false; |
| 2755 | bool is_audio_ready = false; |
| 2756 | double recent_pts_diff = recent_pts_seconds - frame_pts_seconds; |
| 2757 | if ((frame_pts_seconds <= video_pts_seconds) |
| 2758 | || (recent_pts_diff > 1.5) |
| 2759 | || packet_status.video_eof || packet_status.end_of_file) { |
| 2760 | // Video stream is past this frame (so it must be done) |
| 2761 | // OR video stream is too far behind, missing, or end-of-file |
| 2762 | is_video_ready = true; |
| 2763 | ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (video ready)", |
| 2764 | "frame_number", f->number, |
| 2765 | "frame_pts_seconds", frame_pts_seconds, |
| 2766 | "video_pts_seconds", video_pts_seconds, |
| 2767 | "recent_pts_diff", recent_pts_diff); |
| 2768 | if (info.has_video && !f->has_image_data && |
| 2769 | (packet_status.video_eof || packet_status.end_of_file)) { |
| 2770 | // Frame has no image data. Prefer timeline-previous frames to preserve |
| 2771 | // visual order, especially when decode/prefetch is out-of-order. |
| 2772 | std::shared_ptr<Frame> previous_frame_instance = final_cache.GetFrame(f->number - 1); |
| 2773 | if (previous_frame_instance && previous_frame_instance->has_image_data) { |
| 2774 | f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy())); |
| 2775 | } |
| 2776 | |
| 2777 | // Fall back to last finalized timeline image (survives cache churn). |
| 2778 | if (!f->has_image_data |
| 2779 | && last_final_video_frame |
| 2780 | && last_final_video_frame->has_image_data |
| 2781 | && last_final_video_frame->number <= f->number) { |
| 2782 | f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy())); |
| 2783 | } |
| 2784 | |
| 2785 | // Fall back to the last decoded image only when it is not from the future. |
no test coverage detected