/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include "modules/video_coding/main/source/decoding_state.h" #include "modules/video_coding/main/source/frame_buffer.h" #include "gtest/gtest.h" #include "modules/video_coding/main/source/jitter_buffer_common.h" #include "modules/interface/module_common_types.h" #include "modules/video_coding/main/source/packet.h" namespace webrtc { TEST(TestDecodingState, Sanity) { VCMDecodingState dec_state; dec_state.Reset(); EXPECT_TRUE(dec_state.init()); EXPECT_TRUE(dec_state.full_sync()); } TEST(TestDecodingState, FrameContinuity) { VCMDecodingState dec_state; // Check that makes decision based on correct method. VCMFrameBuffer frame; frame.SetState(kStateEmpty); VCMPacket* packet = new VCMPacket(); packet->isFirstPacket = 1; packet->timestamp = 1; packet->seqNum = 0xffff; packet->frameType = kVideoFrameDelta; packet->codecSpecificHeader.codec = kRTPVideoVP8; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0x007F; frame.InsertPacket(*packet, 0, false, 0); // Should return true on init. dec_state.Reset(); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); frame.Reset(); // Use pictureId packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0x0002; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0; packet->seqNum = 10; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); // Use sequence numbers. packet->codecSpecificHeader.codecHeader.VP8.pictureId = kNoPictureId; frame.Reset(); frame.SetState(kStateEmpty); packet->seqNum = dec_state.sequence_num() - 1u; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); frame.Reset(); frame.SetState(kStateEmpty); packet->seqNum = dec_state.sequence_num() + 1u; frame.InsertPacket(*packet, 0, false, 0); // Insert another packet to this frame packet->seqNum++; frame.InsertPacket(*packet, 0, false, 0); // Verify wrap. EXPECT_EQ(dec_state.sequence_num(), 0xffff); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); // Insert packet with temporal info. dec_state.Reset(); frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0; packet->seqNum = 1; packet->timestamp = 1; EXPECT_TRUE(dec_state.full_sync()); frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); frame.Reset(); frame.SetState(kStateEmpty); // 1 layer up - still good. packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 1; packet->seqNum = 2; packet->timestamp = 2; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); frame.Reset(); frame.SetState(kStateEmpty); // Lost non-base layer packet => should update sync parameter. packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 3; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 3; packet->seqNum = 4; packet->timestamp = 4; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); // Now insert the next non-base layer (belonging to a next tl0PicId). frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 2; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 4; packet->seqNum = 5; packet->timestamp = 5; frame.InsertPacket(*packet, 0, false, 0); // Checking continuity and not updating the state - this should not trigger // an update of sync state. EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); EXPECT_TRUE(dec_state.full_sync()); // Next base layer (dropped interim non-base layers) - should update sync. frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 5; packet->seqNum = 6; packet->timestamp = 6; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_FALSE(dec_state.full_sync()); // Check wrap for temporal layers. frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0x00FF; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 6; packet->seqNum = 7; packet->timestamp = 7; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); EXPECT_FALSE(dec_state.full_sync()); frame.Reset(); frame.SetState(kStateEmpty); packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0x0000; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 7; packet->seqNum = 8; packet->timestamp = 8; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); // The current frame is not continuous dec_state.SetState(&frame); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); delete packet; } TEST(TestDecodingState, SetStateOneBack) { VCMDecodingState dec_state; VCMFrameBuffer frame; frame.SetState(kStateEmpty); VCMPacket* packet = new VCMPacket(); // Based on PictureId. packet->frameType = kVideoFrameDelta; packet->codecSpecificHeader.codec = kRTPVideoVP8; packet->timestamp = 0; packet->seqNum = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0; packet->frameType = kVideoFrameDelta; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetStateOneBack(&frame); EXPECT_EQ(dec_state.sequence_num(), 0xFFFF); // Check continuity. EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); // Based on Temporal layers. packet->timestamp = 0; packet->seqNum = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = kNoPictureId; packet->frameType = kVideoFrameDelta; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetStateOneBack(&frame); // Check continuity EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); delete packet; } TEST(TestDecodingState, UpdateOldPacket) { VCMDecodingState dec_state; // Update only if zero size and newer than previous. // Should only update if the timeStamp match. VCMFrameBuffer frame; frame.SetState(kStateEmpty); VCMPacket* packet = new VCMPacket(); packet->timestamp = 1; packet->seqNum = 1; packet->frameType = kVideoFrameDelta; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); EXPECT_EQ(dec_state.sequence_num(), 1); // Insert an empty packet that does not belong to the same frame. // => Sequence num should be the same. packet->timestamp = 2; dec_state.UpdateOldPacket(packet); EXPECT_EQ(dec_state.sequence_num(), 1); // Now insert empty packet belonging to the same frame. packet->timestamp = 1; packet->seqNum = 2; packet->frameType = kFrameEmpty; packet->sizeBytes = 0; dec_state.UpdateOldPacket(packet); EXPECT_EQ(dec_state.sequence_num(), 2); // Now insert delta packet belonging to the same frame. packet->timestamp = 1; packet->seqNum = 3; packet->frameType = kVideoFrameDelta; packet->sizeBytes = 1400; dec_state.UpdateOldPacket(packet); EXPECT_EQ(dec_state.sequence_num(), 3); // Insert a packet belonging to an older timestamp - should not update the // sequence number. packet->timestamp = 0; packet->seqNum = 4; packet->frameType = kFrameEmpty; packet->sizeBytes = 0; dec_state.UpdateOldPacket(packet); EXPECT_EQ(dec_state.sequence_num(), 3); delete packet; } TEST(TestDecodingState, MultiLayerBehavior) { // Identify sync/non-sync when more than one layer. VCMDecodingState dec_state; // Identify packets belonging to old frames/packets. // Set state for current frames. // tl0PicIdx 0, temporal id 0. VCMFrameBuffer frame; VCMPacket* packet = new VCMPacket(); packet->frameType = kVideoFrameDelta; packet->codecSpecificHeader.codec = kRTPVideoVP8; frame.SetState(kStateEmpty); packet->timestamp = 0; packet->seqNum = 0; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); // tl0PicIdx 0, temporal id 1. frame.Reset(); frame.SetState(kStateEmpty); packet->timestamp = 1; packet->seqNum = 1; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 1; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); // Lost tl0PicIdx 0, temporal id 2. // Insert tl0PicIdx 0, temporal id 3. frame.Reset(); frame.SetState(kStateEmpty); packet->timestamp = 3; packet->seqNum = 3; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 3; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 3; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_FALSE(dec_state.full_sync()); // Insert next base layer frame.Reset(); frame.SetState(kStateEmpty); packet->timestamp = 4; packet->seqNum = 4; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 4; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_FALSE(dec_state.full_sync()); // Insert key frame - should update sync value. // A key frame is always a base layer. frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameKey; packet->isFirstPacket = 1; packet->timestamp = 5; packet->seqNum = 5; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 2; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 5; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); // After sync, a continuous PictureId is required // (continuous base layer is not enough ) frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->timestamp = 6; packet->seqNum = 6; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 3; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 6; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); EXPECT_TRUE(dec_state.full_sync()); frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->isFirstPacket = 1; packet->timestamp = 8; packet->seqNum = 8; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 4; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 8; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); EXPECT_TRUE(dec_state.full_sync()); dec_state.SetState(&frame); EXPECT_FALSE(dec_state.full_sync()); // Insert a non-ref frame - should update sync value. frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->isFirstPacket = 1; packet->timestamp = 9; packet->seqNum = 9; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 4; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 2; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 9; packet->codecSpecificHeader.codecHeader.VP8.layerSync = true; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); // The following test will verify the sync flag behavior after a loss. // Create the following pattern: // Update base layer, lose packet 1 (sync flag on, layer 2), insert packet 3 // (sync flag on, layer 2) check continuity and sync flag after inserting // packet 2 (sync flag on, layer 1). // Base layer. frame.Reset(); dec_state.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->isFirstPacket = 1; packet->markerBit = 1; packet->timestamp = 0; packet->seqNum = 0; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 0; packet->codecSpecificHeader.codecHeader.VP8.layerSync = false; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); EXPECT_TRUE(dec_state.full_sync()); // Layer 2 - 2 packets (insert one, lose one). frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->isFirstPacket = 1; packet->markerBit = 0; packet->timestamp = 1; packet->seqNum = 1; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 2; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 1; packet->codecSpecificHeader.codecHeader.VP8.layerSync = true; frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); // Layer 1 frame.Reset(); frame.SetState(kStateEmpty); packet->frameType = kVideoFrameDelta; packet->isFirstPacket = 1; packet->markerBit = 1; packet->timestamp = 2; packet->seqNum = 3; packet->codecSpecificHeader.codecHeader.VP8.tl0PicIdx = 0; packet->codecSpecificHeader.codecHeader.VP8.temporalIdx = 1; packet->codecSpecificHeader.codecHeader.VP8.pictureId = 2; packet->codecSpecificHeader.codecHeader.VP8.layerSync = true; frame.InsertPacket(*packet, 0, false, 0); EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); EXPECT_TRUE(dec_state.full_sync()); delete packet; } TEST(TestDecodingState, OldInput) { VCMDecodingState dec_state; // Identify packets belonging to old frames/packets. // Set state for current frames. VCMFrameBuffer frame; frame.SetState(kStateEmpty); VCMPacket* packet = new VCMPacket(); packet->timestamp = 10; packet->seqNum = 1; frame.InsertPacket(*packet, 0, false, 0); dec_state.SetState(&frame); packet->timestamp = 9; EXPECT_TRUE(dec_state.IsOldPacket(packet)); // Check for old frame frame.Reset(); frame.InsertPacket(*packet, 0, false, 0); EXPECT_TRUE(dec_state.IsOldFrame(&frame)); delete packet; } } // namespace webrtc