From 40700d27f9a242c7854d5f30bedb4d0a4682d54b Mon Sep 17 00:00:00 2001 From: Zyta Szpak <zyta@google.com> Date: Thu, 18 May 2023 15:14:03 +0000 Subject: [PATCH] Reland: "V4L2Decoder: Check if waiting for DRC needed"" This reverts commit c69b69e501cc928a7bcb1867f26ae9423744c709. Reason for revert: this original commit revealed another bug that got fixed in Chrome and verified by security tests. It can now be safely relanded. Bug: 280853786 Bug: 274864105 Test: android.media.cts.DecoderTest#testEOSBehaviorVP9 Change-Id: I6de423c5b8e6b9d11208d71d279b8ae31cf73f2d (cherry picked from commit ea9b7f077241a75f461f73e292298a6797e72276) --- common/H264NalParser.cpp | 10 +++++ common/HEVCNalParser.cpp | 10 +++++ .../v4l2_codec2/common/H264NalParser.h | 1 + .../v4l2_codec2/common/HEVCNalParser.h | 2 + common/include/v4l2_codec2/common/NalParser.h | 1 + v4l2/V4L2Decoder.cpp | 44 +++++++++++++++++-- v4l2/include/v4l2_codec2/v4l2/V4L2Decoder.h | 3 ++ 7 files changed, 68 insertions(+), 3 deletions(-) diff --git a/common/H264NalParser.cpp b/common/H264NalParser.cpp index 69a3f11..d2949c5 100644 --- a/common/H264NalParser.cpp +++ b/common/H264NalParser.cpp @@ -81,6 +81,16 @@ bool H264NalParser::locateSPS() { return false; } +bool H264NalParser::locateIDR() { + while (locateNextNal()) { + if (length() == 0) continue; + if (type() != kIDRType) continue; + return true; + } + + return false; +} + uint8_t H264NalParser::type() const { // First byte is forbidden_zero_bit (1) + nal_ref_idc (2) + nal_unit_type (5) constexpr uint8_t kNALTypeMask = 0x1f; diff --git a/common/HEVCNalParser.cpp b/common/HEVCNalParser.cpp index 0499e70..89c67f7 100644 --- a/common/HEVCNalParser.cpp +++ b/common/HEVCNalParser.cpp @@ -202,6 +202,16 @@ bool HEVCNalParser::locateSPS() { return false; } +bool HEVCNalParser::locateIDR() { + while (locateNextNal()) { + if (length() == 0) continue; + if (type() != kIDRType) continue; + return true; + } + + return false; +} + uint8_t HEVCNalParser::type() const { // First bit is forbidden_zero_bit, next 6 are nal_unit_type constexpr uint8_t kNALTypeMask = 0x7e; diff --git a/common/include/v4l2_codec2/common/H264NalParser.h b/common/include/v4l2_codec2/common/H264NalParser.h index 075411d..125efd8 100644 --- a/common/include/v4l2_codec2/common/H264NalParser.h +++ b/common/include/v4l2_codec2/common/H264NalParser.h @@ -26,6 +26,7 @@ public: // Locate the sequence parameter set (SPS). bool locateSPS() override; + bool locateIDR() override; // Get the type of the current NAL unit. uint8_t type() const; diff --git a/common/include/v4l2_codec2/common/HEVCNalParser.h b/common/include/v4l2_codec2/common/HEVCNalParser.h index 567115c..0f4574a 100644 --- a/common/include/v4l2_codec2/common/HEVCNalParser.h +++ b/common/include/v4l2_codec2/common/HEVCNalParser.h @@ -15,6 +15,7 @@ namespace android { class HEVCNalParser : public NalParser { public: // Type of a SPS NAL unit. + static constexpr uint8_t kIDRType = 19; //IDR_W_RADL static constexpr uint8_t kSPSType = 33; HEVCNalParser(const uint8_t* data, size_t length); @@ -22,6 +23,7 @@ public: // Locate the sequence parameter set (SPS). bool locateSPS() override; + bool locateIDR() override; // Get the type of the current NAL unit. uint8_t type() const override; diff --git a/common/include/v4l2_codec2/common/NalParser.h b/common/include/v4l2_codec2/common/NalParser.h index ad85a5f..50032d3 100644 --- a/common/include/v4l2_codec2/common/NalParser.h +++ b/common/include/v4l2_codec2/common/NalParser.h @@ -35,6 +35,7 @@ public: // Locate the sequence parameter set (SPS). virtual bool locateSPS() = 0; + virtual bool locateIDR() = 0; // Gets current NAL data (start code is not included). const uint8_t* data() const; diff --git a/v4l2/V4L2Decoder.cpp b/v4l2/V4L2Decoder.cpp index a852d93..e10a1ef 100644 --- a/v4l2/V4L2Decoder.cpp +++ b/v4l2/V4L2Decoder.cpp @@ -21,6 +21,8 @@ #include <v4l2_codec2/common/Common.h> #include <v4l2_codec2/common/Fourcc.h> +#include <v4l2_codec2/common/H264NalParser.h> +#include <v4l2_codec2/common/HEVCNalParser.h> #include <v4l2_codec2/plugin_store/DmabufHelpers.h> namespace android { @@ -29,6 +31,36 @@ namespace { // Extra buffers for transmitting in the whole video pipeline. constexpr size_t kNumExtraOutputBuffers = 4; +bool waitForDRC(const C2ConstLinearBlock& input, std::optional<VideoCodec> codec) { + C2ReadView view = input.map().get(); + const uint8_t* pos = view.data(); + // frame type takes the (2) position in first byte of VP9 uncompressed header + const uint8_t kVP9FrameTypeMask = 0x4; + // frame type takes the (0) position in first byte of VP8 uncompressed header + const uint8_t kVP8FrameTypeMask = 0x1; + + switch (*codec) { + case VideoCodec::H264: { + H264NalParser parser(view.data(), view.capacity()); + return parser.locateIDR(); + } + case VideoCodec::HEVC: { + HEVCNalParser parser(view.data(), view.capacity()); + return parser.locateIDR(); + } + // For VP8 and VP9 it is assumed that the input buffer contains a single + // frame that is not fragmented. + case VideoCodec::VP9: + // 0 - key frame; 1 - interframe + return ((pos[0] & kVP9FrameTypeMask) == 0); + case VideoCodec::VP8: + // 0 - key frame; 1 - interframe; + return ((pos[0] & kVP8FrameTypeMask) == 0); + } + + return false; +} + } // namespace // static @@ -91,6 +123,7 @@ bool V4L2Decoder::start(const VideoCodec& codec, const size_t inputBufferSize, mGetPoolCb = std::move(getPoolCb); mOutputCb = std::move(outputCb); mErrorCb = std::move(errorCb); + mCodec = codec; if (mState == State::Error) { ALOGE("Ignore due to error state."); @@ -326,6 +359,8 @@ void V4L2Decoder::decode(std::unique_ptr<ConstBitstreamBuffer> buffer, DecodeCB setState(State::Decoding); } + if (mInitialEosBuffer && !mPendingDRC) mPendingDRC = waitForDRC(buffer->dmabuf, mCodec); + mDecodeRequests.push(DecodeRequest(std::move(buffer), std::move(decodeCb))); pumpDecodeRequest(); } @@ -397,9 +432,12 @@ void V4L2Decoder::pumpDecodeRequest() { // There is one more case that EOS frame cannot be dequeued because // the first resolution change event wasn't dequeued before - output // queues on the host are not streaming but ARCVM has no knowledge about - // it. Check if first resolution change event was received and finish - // drain now if it wasn't. - if (mInitialEosBuffer) { + // it. Check if first resolution change event was received and if there + // was no previously sent non-empty frame (other than SPS/PPS/EOS) that + // may trigger config from host side. + // Drain can only be finished if we are sure there was no stream = no + // single frame in the stack. + if (mInitialEosBuffer && !mPendingDRC) { ALOGV("Terminate drain, because there was no stream"); mTaskRunner->PostTask(FROM_HERE, ::base::BindOnce(std::move(request.decodeCb), VideoDecoder::DecodeStatus::kOk)); diff --git a/v4l2/include/v4l2_codec2/v4l2/V4L2Decoder.h b/v4l2/include/v4l2_codec2/v4l2/V4L2Decoder.h index fcfeb10..839574f 100644 --- a/v4l2/include/v4l2_codec2/v4l2/V4L2Decoder.h +++ b/v4l2/include/v4l2_codec2/v4l2/V4L2Decoder.h @@ -113,6 +113,9 @@ private: std::queue<DecodeRequest> mDecodeRequests; std::map<int32_t, DecodeCB> mPendingDecodeCbs; + // Marks that we need to wait for DRC before drain can complete. + bool mPendingDRC = false; + VideoCodec mCodec; // Tracks the last DMA buffer ID which was used for a given V4L2 input // buffer ID. Used to try to avoid re-importing buffers. -- GitLab