diff --git a/common/H264NalParser.cpp b/common/H264NalParser.cpp index 69a3f11cc97ed18530ae6ea2ca7cd344a39e001a..d2949c50d9c464999ce615f427cfbbf015f50c1f 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 0499e709dce8a69f7763ee5a4f2d0d4be3412d57..89c67f7e06ba50b9a80e5ffb07cbe00a8d0fba96 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 075411d27609a813ac2c8bd93c34e91a6c6b6c1b..125efd83268890678c1305b88229a8143ab24f1f 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 567115c5197394a0c0e334b4e3e8107c0e635239..0f4574a2616a80600835b511ffebd5d702bf45ca 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 ad85a5f7e53ae195bf987858a40342307c3aeffb..50032d3a8846a8a100dc1d67ea510926df84a04b 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 a852d933e5565f5c67fd2961976d400fd200e0c4..e10a1efa8b14a7f986957c45cafb2ed9510a300f 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 fcfeb103c67f8d8f03c11454661190b866ec132e..839574f291afbbde3b5621f132ddc85cea618429 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.