-
Notifications
You must be signed in to change notification settings - Fork 665
Limit CPU video decoder codec support #6352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 99d65ba2e8f1e30fffc6978cda805aa630a2255c | ||
| 6e59c3ea6333fed7948f7146b1ffbb5a7db5e536 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 21dd6148f0cf4557531d54b810379c681c898e91 | ||
| 7079ceff2aa2ca2e5a6a55d8ea47b459bf41f9b1 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -50,6 +50,10 @@ void FramesDecoderCpu::Flush() { | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| bool FramesDecoderCpu::ReadNextFrame(uint8_t *data) { | ||||||||||||||||||||||||||||||
| LOG_LINE << "FramesDecoderCpu::ReadNextFrame: next_frame_idx_=" << next_frame_idx_ << std::endl; | ||||||||||||||||||||||||||||||
| if (next_frame_idx_ == 0) { | ||||||||||||||||||||||||||||||
| // call NumFrames() to populate the index before any frames are read | ||||||||||||||||||||||||||||||
| NumFrames(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| // No more frames in the file | ||||||||||||||||||||||||||||||
| if (next_frame_idx_ == -1) { | ||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||
|
|
@@ -154,19 +158,21 @@ bool FramesDecoderCpu::ReadRegularFrame(uint8_t *data) { | |||||||||||||||||||||||||||||
| LOG_LINE << (copy_to_output ? "Read" : "Skip") << " frame (ReadRegularFrame), index " | ||||||||||||||||||||||||||||||
| << next_frame_idx_ << ", timestamp " << std::setw(5) << frame_->pts | ||||||||||||||||||||||||||||||
| << std::endl; | ||||||||||||||||||||||||||||||
| if (!copy_to_output) { | ||||||||||||||||||||||||||||||
| ++next_frame_idx_; | ||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||
| if (copy_to_output) { | ||||||||||||||||||||||||||||||
| CopyToOutput(data); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| CopyToOutput(data); | ||||||||||||||||||||||||||||||
| ++next_frame_idx_; | ||||||||||||||||||||||||||||||
| if (next_frame_idx_ >= NumFrames()) { | ||||||||||||||||||||||||||||||
|
jantonguirao marked this conversation as resolved.
|
||||||||||||||||||||||||||||||
| next_frame_idx_ = -1; | ||||||||||||||||||||||||||||||
| LOG_LINE << "Next frame index out of bounds (regular), setting to -1" << std::endl; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||
|
Comment on lines
164
to
169
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ret = avcodec_send_packet(codec_ctx_, nullptr); | ||||||||||||||||||||||||||||||
| DALI_ENFORCE(ret >= 0, | ||||||||||||||||||||||||||||||
| make_string("Failed to send packet to decoder: ", av_error_string(ret))); | ||||||||||||||||||||||||||||||
| // the decoder has already drained — no more packets to send | ||||||||||||||||||||||||||||||
| DALI_ENFORCE(ret >= 0 || ret == AVERROR_EOF, | ||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Nit/Wording] Now that More importantly: dropping into
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added comment |
||||||||||||||||||||||||||||||
| make_string("avcodec_send_packet failed: ", av_error_string(ret))); | ||||||||||||||||||||||||||||||
| flush_state_ = true; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||
|
|
@@ -176,6 +182,7 @@ bool FramesDecoderCpu::ReadFlushFrame(uint8_t *data) { | |||||||||||||||||||||||||||||
| bool copy_to_output = data != nullptr; | ||||||||||||||||||||||||||||||
| if (avcodec_receive_frame(codec_ctx_, frame_) < 0) { | ||||||||||||||||||||||||||||||
| flush_state_ = false; | ||||||||||||||||||||||||||||||
| next_frame_idx_ = -1; | ||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -189,7 +196,7 @@ bool FramesDecoderCpu::ReadFlushFrame(uint8_t *data) { | |||||||||||||||||||||||||||||
| ++next_frame_idx_; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // TODO(awolant): Figure out how to handle this during index building | ||||||||||||||||||||||||||||||
| // Or when NumFrames in unavailible | ||||||||||||||||||||||||||||||
| // Or when NumFrames in unavailable | ||||||||||||||||||||||||||||||
| if (next_frame_idx_ >= NumFrames()) { | ||||||||||||||||||||||||||||||
| next_frame_idx_ = -1; | ||||||||||||||||||||||||||||||
| LOG_LINE << "Next frame index out of bounds, setting to -1" << std::endl; | ||||||||||||||||||||||||||||||
|
|
@@ -213,15 +220,15 @@ bool FramesDecoderCpu::SelectVideoStream(int stream_id) { | |||||||||||||||||||||||||||||
| assert(codec_params_); | ||||||||||||||||||||||||||||||
| AVCodecID codec_id = codec_params_->codec_id; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| static constexpr std::array<AVCodecID, 7> codecs = { | ||||||||||||||||||||||||||||||
| AVCodecID::AV_CODEC_ID_H264, | ||||||||||||||||||||||||||||||
| AVCodecID::AV_CODEC_ID_HEVC, | ||||||||||||||||||||||||||||||
| static constexpr std::array<AVCodecID, 3> codecs = { | ||||||||||||||||||||||||||||||
| AVCodecID::AV_CODEC_ID_VP8, | ||||||||||||||||||||||||||||||
| AVCodecID::AV_CODEC_ID_VP9, | ||||||||||||||||||||||||||||||
| AVCodecID::AV_CODEC_ID_MJPEG, | ||||||||||||||||||||||||||||||
| // Those are not supported by our compiled version of libavcodec, | ||||||||||||||||||||||||||||||
| // AVCodecID::AV_CODEC_ID_AV1, | ||||||||||||||||||||||||||||||
| // AVCodecID::AV_CODEC_ID_MPEG4, | ||||||||||||||||||||||||||||||
| // AVCodecID::AV_CODEC_ID_H264, | ||||||||||||||||||||||||||||||
| // AVCodecID::AV_CODEC_ID_HEVC, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (std::find(codecs.begin(), codecs.end(), codec_id) == codecs.end()) { | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,24 +81,33 @@ void SaveFrame(uint8_t *frame, int frame_id, int sample_id, int batch_id, | |
|
|
||
| void TestVideo::CompareFrame(int frame_id, const uint8_t *frame, int eps) { | ||
| auto &ground_truth = frames_[frame_id]; | ||
| bool frames_match = true; | ||
| // Tolerate a tiny number of isolated subpixel deviations exceeding eps. The CPU VP9 | ||
| // decode path occasionally produces a single byte that differs by ~32 (suspected SIMD | ||
| // glitch in libavcodec/sws_scale that Valgrind cannot instrument). A genuine regression | ||
| // produces orders of magnitude more bad subpixels, so this budget still catches real | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Question/Design] Tolerating 16 deviating subpixels per frame to mask a suspected libavcodec/sws_scale SIMD glitch is pragmatic, but it does mean a regression that introduces ≤16 wrong subpixels per frame will be silently absorbed. Given the suspected upstream cause, is there any plan to file an upstream bug and/or pin a known-good FFmpeg version, or to scope this tolerance only to the VP9 paths it actually affects (e.g. an extra arg to Not blocking — the rationale in the comment is clear and the 16/2.7M ratio is tiny — just want to make sure we don't lose the thread on the underlying glitch.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. The threshold is set empirically based on the failures in tests observed. I don't know if there is a good FFmpeg we can pin. I still believe that 16 off pixels is still something we can tolerate, if something is seriously off the number if bigger. |
||
| // breakage while suppressing the flake. | ||
| static constexpr int max_bad_subpixels = 16; | ||
| std::vector<int> bad_per_thread(detail::ThreadCount(), 0); | ||
|
|
||
| detail::parallel_for(FrameSize(), detail::ThreadCount(), [&](int start, int end, int id){ | ||
| int count = 0; | ||
| for (int j = start; j < end; ++j) { | ||
| if (std::abs(frame[j] - ground_truth.data[j]) > eps) { | ||
| frames_match = false; | ||
| break; | ||
| ++count; | ||
| } | ||
| } | ||
| bad_per_thread[id] = count; | ||
| }); | ||
| int total_bad = std::accumulate(bad_per_thread.begin(), bad_per_thread.end(), 0); | ||
|
|
||
| if (!frames_match) { | ||
| if (total_bad > max_bad_subpixels) { | ||
| SaveFrame(const_cast<uint8_t *>(frame), frame_id, 0, 0, "test_frame", Width(), | ||
| Height()); | ||
| SaveFrame(ground_truth.data, frame_id, 0, 0, "ground_truth", Width(), | ||
| Height()); | ||
|
|
||
| FAIL() << "Frames do not match (eps=" << eps | ||
| FAIL() << "Frames do not match (eps=" << eps << ", " << total_bad | ||
| << " subpixels exceed threshold, budget=" << max_bad_subpixels | ||
| << "). Debug frames saved to test_frame_*.png and ground_truth_*.png"; | ||
| } | ||
| } | ||
|
|
@@ -125,24 +134,24 @@ void CompareFrameAvgError(int frame_id, size_t frame_size, size_t width, size_t | |
| } | ||
|
|
||
| std::vector<std::string> VideoTestBase::cfr_videos_frames_paths_{ | ||
| testing::dali_extra_path() + "/db/video/cfr/frames_1/", | ||
| testing::dali_extra_path() + "/db/video/cfr/frames_2/"}; | ||
| testing::dali_extra_path() + "/db/video/cfr/frames_1_vp9/", | ||
| testing::dali_extra_path() + "/db/video/cfr/frames_2_vp9/"}; | ||
|
|
||
| std::vector<std::string> VideoTestBase::vfr_videos_frames_paths_{ | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_1/", | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_2/"}; | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_1_vp9/", | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_2_vp9/"}; | ||
|
|
||
| std::vector<std::string> VideoTestBase::vfr_hevc_videos_frames_paths_{ | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_1_hevc/", | ||
| testing::dali_extra_path() + "/db/video/vfr/frames_2_hevc/"}; | ||
|
|
||
| std::vector<std::string> VideoTestBase::cfr_videos_paths_{ | ||
| testing::dali_extra_path() + "/db/video/cfr/test_1.mp4", | ||
| testing::dali_extra_path() + "/db/video/cfr/test_2.mp4"}; | ||
| testing::dali_extra_path() + "/db/video/cfr/test_1_vp9.mp4", | ||
| testing::dali_extra_path() + "/db/video/cfr/test_2_vp9.mp4"}; | ||
|
|
||
| std::vector<std::string> VideoTestBase::vfr_videos_paths_{ | ||
| testing::dali_extra_path() + "/db/video/vfr/test_1.mp4", | ||
| testing::dali_extra_path() + "/db/video/vfr/test_2.mp4"}; | ||
| testing::dali_extra_path() + "/db/video/vfr/test_1_vp9.mp4", | ||
| testing::dali_extra_path() + "/db/video/vfr/test_2_vp9.mp4"}; | ||
|
|
||
| std::vector<std::string> VideoTestBase::cfr_hevc_videos_paths_{ | ||
| testing::dali_extra_path() + "/db/video/cfr/test_1_hevc.mp4", | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.