From ae88eb5c074afc02b3a8507ce3054112145c854e Mon Sep 17 00:00:00 2001 From: Linda Yu Date: Mon, 26 Dec 2016 14:13:17 +0800 Subject: [PATCH 1/3] unittest: pass Non IDR frame to decode after flush this is used to verify yami's robustness Signed-off-by: Linda Yu --- decoder/DecoderApi_unittest.cpp | 117 ++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/decoder/DecoderApi_unittest.cpp b/decoder/DecoderApi_unittest.cpp index 10bb2376..35e27bc2 100644 --- a/decoder/DecoderApi_unittest.cpp +++ b/decoder/DecoderApi_unittest.cpp @@ -264,6 +264,42 @@ TEST_P(DecodeApiTest, Format_Change) EXPECT_EQ(inFrames, outFrames); } +void decodeAndOutput(SharedPtr decoder, DecodeSurfaceAllocator *allocator, TestDecodeFrames& frames, + int begin, int end, int64_t& timeBeforeFlush, int64_t& timeCurrent, bool isFlushed, int& inFrames, int& outFrames) +{ + VideoDecodeBuffer buffer; + FrameInfo info; + SharedPtr output; + + for (int j = begin; j < end; j++) { + if (!frames.getFrame(buffer, info)) + break; + + buffer.timeStamp = timeCurrent; + timeCurrent++; + YamiStatus status = decoder->decode(&buffer); + while ((output = decoder->getOutput())) { + outFrames ++; + if (isFlushed) { + EXPECT_TRUE(output->timeStamp > timeBeforeFlush); + } + } + + if (status == YAMI_DECODE_FORMAT_CHANGE) { + //check format + const VideoFormatInfo* format = decoder->getFormatInfo(); + allocator->onFormatChange(format); + + //send buffer again + EXPECT_EQ(YAMI_SUCCESS, decoder->decode(&buffer)); + } else { + EXPECT_TRUE((YAMI_SUCCESS == status) || (YAMI_DECODE_INVALID_DATA == status)); + } + inFrames++; + } + return; +} + TEST_P(DecodeApiTest, Flush) { NativeDisplay nativeDisplay; @@ -282,43 +318,18 @@ TEST_P(DecodeApiTest, Flush) memset(&config, 0, sizeof(config)); ASSERT_EQ(YAMI_SUCCESS, decoder->start(&config)); - VideoDecodeBuffer buffer; - FrameInfo info; int64_t timeBeforeFlush = 0; int64_t timeCurrent = 0; - SharedPtr output; int32_t size = frames.getFrameCount(); + int32_t inFrames = 0; int32_t outFrames = 0; for (int i = 0; i < size; i++) { + inFrames = 0; outFrames = 0; frames.seekToStart(); - for (int j = 0; j <= i; j++) { - if (!frames.getFrame(buffer, info)) - break; - - buffer.timeStamp = timeCurrent; - timeCurrent++; - YamiStatus status = decoder->decode(&buffer); - while ((output = decoder->getOutput())) { - EXPECT_TRUE(output->timeStamp > timeBeforeFlush); - outFrames++; - } - - if (status == YAMI_DECODE_FORMAT_CHANGE) { - //check format - const VideoFormatInfo* format = decoder->getFormatInfo(); - allocator->onFormatChange(format); - - //send buffer again - EXPECT_EQ(YAMI_SUCCESS, decoder->decode(&buffer)); - } - else { - EXPECT_EQ(YAMI_SUCCESS, status); - } - } - timeBeforeFlush = buffer.timeStamp; - + decodeAndOutput(decoder, allocator, frames, 0, i, timeBeforeFlush, timeCurrent, false, inFrames, outFrames); + timeBeforeFlush = timeCurrent - 1; // No flush at last round, it has EOS at the end if (i < (size - 1)) decoder->flush(); @@ -328,7 +339,53 @@ TEST_P(DecodeApiTest, Flush) while (decoder->getOutput()) { outFrames++; } - EXPECT_EQ(outFrames, size); + EXPECT_EQ(outFrames, inFrames); +} + + +TEST_P(DecodeApiTest, Flush_NonIDR) +{ + NativeDisplay nativeDisplay; + memset(&nativeDisplay, 0, sizeof(nativeDisplay)); + DisplayPtr display = VaapiDisplay::create(nativeDisplay); + DecodeSurfaceAllocator* allocator = new DecodeSurfaceAllocator(display); + + SharedPtr decoder; + TestDecodeFrames frames = *GetParam(); + decoder.reset(createVideoDecoder(frames.getMime()), releaseVideoDecoder); + ASSERT_TRUE(bool(decoder)); + + decoder->setAllocator(allocator); + + VideoConfigBuffer config; + memset(&config, 0, sizeof(config)); + ASSERT_EQ(YAMI_SUCCESS, decoder->start(&config)); + + int64_t timeBeforeFlush = 0; + int64_t timeCurrent = 0; + int32_t size = frames.getFrameCount(); + int inFrames = 0; + int outFrames = 0; + + for (int i = 1; i < size; i++) { + frames.seekToStart(); + // decode 0 -> i frames, and get possible output + decodeAndOutput(decoder, allocator, frames, 0, i, timeBeforeFlush, timeCurrent, false, inFrames, outFrames); + timeBeforeFlush = timeCurrent - 1; + // flush + EXPECT_TRUE(inFrames > outFrames); + decoder->flush(); + inFrames = 0; + outFrames = 0; + // decode i -> size frames, and get possible output + decodeAndOutput(decoder, allocator, frames, i, size, timeBeforeFlush, timeCurrent, true, inFrames, outFrames); + } + //drain the decoder + EXPECT_EQ(YAMI_SUCCESS, decoder->decode(NULL)); + while (decoder->getOutput()) { + outFrames ++; + } + EXPECT_TRUE(inFrames > outFrames); } /** Teach Google Test how to print a TestDecodeFrames::Shared object */ From 2c535b3a2fdea404494674f5e05942fe71f7023e Mon Sep 17 00:00:00 2001 From: Linda Yu Date: Fri, 17 Feb 2017 15:30:35 +0800 Subject: [PATCH 2/3] vaapidecoder_h265: return invalid if 1st frame is not idr if the frist frame of a new stream is not idr, return invalid data directly but not process it. Signed-off-by: Linda Yu --- decoder/vaapidecoder_h265.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decoder/vaapidecoder_h265.cpp b/decoder/vaapidecoder_h265.cpp index 674f835e..7304fbea 100644 --- a/decoder/vaapidecoder_h265.cpp +++ b/decoder/vaapidecoder_h265.cpp @@ -1077,7 +1077,7 @@ YamiStatus VaapiDecoderH265::decodeSlice(NalUnit* nalu) return status; if (m_noRaslOutputFlag && isRasl(nalu)) return YAMI_SUCCESS; - if (!m_current || !m_dpb.init(m_current, slice, nalu, m_newStream)) + if (!m_current || (!isIdr(nalu) && m_newStream) || !m_dpb.init(m_current, slice, nalu, m_newStream)) return YAMI_DECODE_INVALID_DATA; if (!fillPicture(m_current, slice) || !fillIqMatrix(m_current, slice)) return YAMI_FAIL; From 04310beb5c655d578dd5349f9979d49c6bf031ca Mon Sep 17 00:00:00 2001 From: Linda Yu Date: Thu, 29 Dec 2016 15:52:39 +0800 Subject: [PATCH 3/3] unittest: add mpeg2 test frames Signed-off-by: Linda Yu --- decoder/DecoderApi_unittest.cpp | 12 +++++++++++ decoder/FrameData.h | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/decoder/DecoderApi_unittest.cpp b/decoder/DecoderApi_unittest.cpp index 35e27bc2..8836175b 100644 --- a/decoder/DecoderApi_unittest.cpp +++ b/decoder/DecoderApi_unittest.cpp @@ -85,6 +85,17 @@ static FrameData g_vp8data[] = { g_EOF, }; + +static FrameData g_mpeg2data[] = { + g_mpeg2_8x8I1, + g_mpeg2_8x8P, + g_mpeg2_8x8I2, + g_mpeg2_8x8I1, + g_mpeg2_8x8P, + g_mpeg2_8x8I2, + g_EOF, +}; + class TestDecodeFrames { public: typedef SharedPtr Shared; @@ -398,6 +409,7 @@ INSTANTIATE_TEST_CASE_P( VaapiDecoder, DecodeApiTest, ::testing::Values( TestDecodeFrames::create(g_h264data, YAMI_MIME_H264), + TestDecodeFrames::create(g_mpeg2data, YAMI_MIME_MPEG2), TestDecodeFrames::create(g_h265data, YAMI_MIME_H265), TestDecodeFrames::create(g_vp8data, YAMI_MIME_VP8))); } diff --git a/decoder/FrameData.h b/decoder/FrameData.h index 9b3d584c..0bee3ada 100644 --- a/decoder/FrameData.h +++ b/decoder/FrameData.h @@ -316,6 +316,38 @@ static const uint8_t s_hevc16x16[] = { 0x3e, 0xfe, 0xc3, 0x05, 0xd2, 0x1a, 0xc9, 0x78 }; + +static const uint8_t s_mpeg2_8x8I1[] = { + 0x00, 0x00, 0x01, 0xb3, 0x00, 0x80, 0x08, 0x13, 0xff, 0xff, 0xe0, 0x18, + 0x00, 0x00, 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xb8, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, + 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5, 0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, + 0x00, 0x01, 0x01, 0x13, 0xfb, 0x55, 0x29, 0x49, 0x4a +}; + +static const uint8_t s_mpeg2_8x8P[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x57, 0xff, 0xfb, 0x80, 0x00, 0x00, 0x01, + 0xb5, 0x81, 0x1f, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x12, 0x88, + 0x02, 0x07, 0xe4, 0x00, 0x6a, 0x08, 0x60, 0x7a, 0x00, 0xb8, 0x11, 0xff, + 0xb5, 0x20, 0x97, 0xff, 0xa0, 0x0d, 0x60, 0x40, 0xfc, 0x80, 0x0d, 0x41, + 0x0c, 0x0f, 0x40, 0x17, 0x02, 0x3f, 0xf6, 0xa4, 0x12, 0xff, 0xf4, 0x01, + 0xac, 0x02, 0x70, 0x2a, 0x08, 0x80, 0x0c, 0x08, 0xe0, 0x10, 0x18, 0x45, + 0x80, 0x14, 0x00, 0x2e, 0x09, 0x04, 0x1f, 0xeb, 0x03, 0xc3, 0x00, 0x15, + 0x80, 0x5e, 0x80, 0x2c, 0xa7, 0x59, 0x49, 0x74, 0x8f, 0x48, 0x4b, 0x38, + 0x12, 0xdb, 0x19, 0x00 +}; + +static const uint8_t s_mpeg2_8x8I2[] = { + 0x00, 0x00, 0x01, 0xb3, 0x00, 0x80, 0x08, 0x13, 0xff, 0xff, 0xe0, 0x18, + 0x00, 0x00, 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xb8, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, + 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5, 0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, + 0x00, 0x01, 0x01, 0x13, 0xfb, 0x30, 0x21, 0x00, 0x40, 0x13, 0x4a, 0x28, + 0xc0, 0x3a, 0x04, 0x5f, 0xfc, 0x2c, 0x9f, 0x5c, 0x98, 0xdc, 0xa0, 0x83, + 0xfd, 0x65, 0x1a, 0x00, 0xf8, 0x01, 0xf7, 0xe3, 0xc0, 0xc1, 0x0d, 0x04, + 0xe2, 0x80, 0xaa, 0x10, 0x4e, 0xad, 0xa0 +}; + struct FrameInfo { uint32_t m_width; uint32_t m_height; @@ -363,6 +395,10 @@ const static FrameData g_vp8_8x8P1(s_vp8_8x8P1, N_ELEMENTS(s_vp8_8x8P1), 8, 8); const static FrameData g_vp8_8x8P2(s_vp8_8x8P2, N_ELEMENTS(s_vp8_8x8P2), 8, 8); const static FrameData g_vp8_16x16(s_vp8_16x16, N_ELEMENTS(s_vp8_16x16), 16, 16); +const static FrameData g_mpeg2_8x8I1(s_mpeg2_8x8I1, N_ELEMENTS(s_mpeg2_8x8I1), 8, 8); +const static FrameData g_mpeg2_8x8P(s_mpeg2_8x8P, N_ELEMENTS(s_mpeg2_8x8P), 8, 8); +const static FrameData g_mpeg2_8x8I2(s_mpeg2_8x8I2, N_ELEMENTS(s_mpeg2_8x8I2), 8, 8); + const static FrameData g_EOF(NULL, 0, 0, 0); }; #endif