/* * Copyright 2017 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 "webrtc/test/gtest.h" #include "webrtc/test/gmock.h" #include "webrtc/base/bytebuffer.h" #include "webrtc/base/logging.h" #include "webrtc/common_video/h264/h264_common.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/modules/pacing/packet_router.h" #include "webrtc/modules/video_coding/include/video_coding_defines.h" #include "webrtc/modules/video_coding/frame_object.h" #include "webrtc/modules/video_coding/packet.h" #include "webrtc/modules/video_coding/rtp_frame_reference_finder.h" #include "webrtc/modules/video_coding/timing.h" #include "webrtc/modules/utility/include/process_thread.h" #include "webrtc/system_wrappers/include/clock.h" #include "webrtc/system_wrappers/include/field_trial_default.h" #include "webrtc/test/field_trial.h" #include "webrtc/video/rtp_stream_receiver.h" using testing::_; namespace webrtc { namespace { const char kNewJitterBufferFieldTrialEnabled[] = "WebRTC-NewVideoJitterBuffer/Enabled/"; const uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01}; class MockTransport : public Transport { public: MOCK_METHOD3(SendRtp, bool(const uint8_t* packet, size_t length, const PacketOptions& options)); MOCK_METHOD2(SendRtcp, bool(const uint8_t* packet, size_t length)); }; class MockNackSender : public NackSender { public: MOCK_METHOD1(SendNack, void(const std::vector& sequence_numbers)); }; class MockKeyFrameRequestSender : public KeyFrameRequestSender { public: MOCK_METHOD0(RequestKeyFrame, void()); }; class MockOnCompleteFrameCallback : public video_coding::OnCompleteFrameCallback { public: MockOnCompleteFrameCallback() : buffer_(rtc::ByteBuffer::ORDER_NETWORK) {} MOCK_METHOD1(DoOnCompleteFrame, void(video_coding::FrameObject* frame)); MOCK_METHOD1(DoOnCompleteFrameFailNullptr, void(video_coding::FrameObject* frame)); MOCK_METHOD1(DoOnCompleteFrameFailLength, void(video_coding::FrameObject* frame)); MOCK_METHOD1(DoOnCompleteFrameFailBitstream, void(video_coding::FrameObject* frame)); void OnCompleteFrame(std::unique_ptr frame) { if (!frame) { DoOnCompleteFrameFailNullptr(nullptr); return; } EXPECT_EQ(buffer_.Length(), frame->size()); if (buffer_.Length() != frame->size()) { DoOnCompleteFrameFailLength(frame.get()); return; } std::vector actual_data(frame->size()); frame->GetBitstream(actual_data.data()); if (memcmp(buffer_.Data(), actual_data.data(), buffer_.Length()) != 0) { DoOnCompleteFrameFailBitstream(frame.get()); return; } DoOnCompleteFrame(frame.get()); } void AppendExpectedBitstream(const uint8_t data[], size_t size_in_bytes) { // TODO(Johan): Let rtc::ByteBuffer handle uint8_t* instead of char*. buffer_.WriteBytes(reinterpret_cast(data), size_in_bytes); } rtc::ByteBufferWriter buffer_; }; } // namespace class RtpStreamReceiverTest : public testing::Test { public: RtpStreamReceiverTest() : config_(CreateConfig()), timing_(Clock::GetRealTimeClock()), process_thread_(ProcessThread::Create("TestThread")) {} void SetUp() { rtp_stream_receiver_.reset(new RtpStreamReceiver( &mock_transport_, nullptr, &packet_router_, nullptr, &config_, nullptr, process_thread_.get(), &mock_nack_sender_, &mock_key_frame_request_sender_, &mock_on_complete_frame_callback_, &timing_)); } WebRtcRTPHeader GetDefaultPacket() { WebRtcRTPHeader packet; memset(&packet, 0, sizeof(packet)); packet.type.Video.codec = kRtpVideoH264; return packet; } // TODO(Johan): refactor h264_sps_pps_tracker_unittests.cc to avoid duplicate // code. void AddSps(WebRtcRTPHeader* packet, int sps_id, std::vector* data) { NaluInfo info; info.type = H264::NaluType::kSps; info.sps_id = sps_id; info.pps_id = -1; info.offset = data->size(); info.size = 2; data->push_back(H264::NaluType::kSps); data->push_back(sps_id); packet->type.Video.codecHeader.H264 .nalus[packet->type.Video.codecHeader.H264.nalus_length++] = info; } void AddPps(WebRtcRTPHeader* packet, int sps_id, int pps_id, std::vector* data) { NaluInfo info; info.type = H264::NaluType::kPps; info.sps_id = sps_id; info.pps_id = pps_id; info.offset = data->size(); info.size = 2; data->push_back(H264::NaluType::kPps); data->push_back(pps_id); packet->type.Video.codecHeader.H264 .nalus[packet->type.Video.codecHeader.H264.nalus_length++] = info; } void AddIdr(WebRtcRTPHeader* packet, int pps_id) { NaluInfo info; info.type = H264::NaluType::kIdr; info.sps_id = -1; info.pps_id = pps_id; packet->type.Video.codecHeader.H264 .nalus[packet->type.Video.codecHeader.H264.nalus_length++] = info; } protected: static VideoReceiveStream::Config CreateConfig() { VideoReceiveStream::Config config(nullptr); config.rtp.remote_ssrc = 1111; config.rtp.local_ssrc = 2222; return config; } webrtc::test::ScopedFieldTrials override_field_trials_{ kNewJitterBufferFieldTrialEnabled}; VideoReceiveStream::Config config_; MockNackSender mock_nack_sender_; MockKeyFrameRequestSender mock_key_frame_request_sender_; MockTransport mock_transport_; MockOnCompleteFrameCallback mock_on_complete_frame_callback_; PacketRouter packet_router_; VCMTiming timing_; std::unique_ptr process_thread_; std::unique_ptr rtp_stream_receiver_; }; TEST_F(RtpStreamReceiverTest, GenericKeyFrame) { WebRtcRTPHeader rtp_header; const std::vector data({1, 2, 3, 4}); memset(&rtp_header, 0, sizeof(rtp_header)); rtp_header.header.sequenceNumber = 1; rtp_header.header.markerBit = 1; rtp_header.type.Video.is_first_packet_in_frame = true; rtp_header.frameType = kVideoFrameKey; rtp_header.type.Video.codec = kRtpVideoGeneric; mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), data.size()); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &rtp_header); } TEST_F(RtpStreamReceiverTest, GenericKeyFrameBitstreamError) { WebRtcRTPHeader rtp_header; const std::vector data({1, 2, 3, 4}); memset(&rtp_header, 0, sizeof(rtp_header)); rtp_header.header.sequenceNumber = 1; rtp_header.header.markerBit = 1; rtp_header.type.Video.is_first_packet_in_frame = true; rtp_header.frameType = kVideoFrameKey; rtp_header.type.Video.codec = kRtpVideoGeneric; constexpr uint8_t expected_bitsteam[] = {1, 2, 3, 0xff}; mock_on_complete_frame_callback_.AppendExpectedBitstream( expected_bitsteam, sizeof(expected_bitsteam)); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrameFailBitstream(_)); rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &rtp_header); } TEST_F(RtpStreamReceiverTest, InBandSpsPps) { std::vector sps_data; WebRtcRTPHeader sps_packet = GetDefaultPacket(); AddSps(&sps_packet, 0, &sps_data); sps_packet.header.sequenceNumber = 0; mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data.data(), sps_data.size()); rtp_stream_receiver_->OnReceivedPayloadData(sps_data.data(), sps_data.size(), &sps_packet); std::vector pps_data; WebRtcRTPHeader pps_packet = GetDefaultPacket(); AddPps(&pps_packet, 0, 1, &pps_data); pps_packet.header.sequenceNumber = 1; mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data.data(), pps_data.size()); rtp_stream_receiver_->OnReceivedPayloadData(pps_data.data(), pps_data.size(), &pps_packet); std::vector idr_data; WebRtcRTPHeader idr_packet = GetDefaultPacket(); AddIdr(&idr_packet, 1); idr_packet.type.Video.is_first_packet_in_frame = true; idr_packet.header.sequenceNumber = 2; idr_packet.header.markerBit = 1; idr_packet.type.Video.is_first_packet_in_frame = true; idr_packet.frameType = kVideoFrameKey; idr_packet.type.Video.codec = kRtpVideoH264; idr_data.insert(idr_data.end(), {0x65, 1, 2, 3}); mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data.data(), idr_data.size()); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); rtp_stream_receiver_->OnReceivedPayloadData(idr_data.data(), idr_data.size(), &idr_packet); } TEST_F(RtpStreamReceiverTest, OutOfBandFmtpSpsPps) { constexpr int kPayloadType = 99; VideoCodec codec; codec.plType = kPayloadType; std::map codec_params; // Example parameter sets from https://tools.ietf.org/html/rfc3984#section-8.2 // . codec_params.insert( {cricket::kH264FmtpSpropParameterSets, "Z0IACpZTBYmI,aMljiA=="}); rtp_stream_receiver_->AddReceiveCodec(codec, codec_params); const uint8_t binary_sps[] = {0x67, 0x42, 0x00, 0x0a, 0x96, 0x53, 0x05, 0x89, 0x88}; mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_sps, sizeof(binary_sps)); const uint8_t binary_pps[] = {0x68, 0xc9, 0x63, 0x88}; mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_pps, sizeof(binary_pps)); std::vector data; WebRtcRTPHeader idr_packet = GetDefaultPacket(); AddIdr(&idr_packet, 0); idr_packet.header.payloadType = kPayloadType; idr_packet.type.Video.is_first_packet_in_frame = true; idr_packet.header.sequenceNumber = 2; idr_packet.header.markerBit = 1; idr_packet.type.Video.is_first_packet_in_frame = true; idr_packet.frameType = kVideoFrameKey; idr_packet.type.Video.codec = kRtpVideoH264; data.insert(data.end(), {1, 2, 3}); mock_on_complete_frame_callback_.AppendExpectedBitstream( kH264StartCode, sizeof(kH264StartCode)); mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), data.size()); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &idr_packet); } TEST_F(RtpStreamReceiverTest, PaddingInMediaStream) { WebRtcRTPHeader header = GetDefaultPacket(); std::vector data; data.insert(data.end(), {1, 2, 3}); header.header.payloadType = 99; header.type.Video.is_first_packet_in_frame = true; header.header.sequenceNumber = 2; header.header.markerBit = true; header.frameType = kVideoFrameKey; header.type.Video.codec = kRtpVideoGeneric; mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), data.size()); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &header); header.header.sequenceNumber = 3; rtp_stream_receiver_->OnReceivedPayloadData(nullptr, 0, &header); header.frameType = kVideoFrameDelta; header.header.sequenceNumber = 4; EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &header); header.header.sequenceNumber = 6; rtp_stream_receiver_->OnReceivedPayloadData(data.data(), data.size(), &header); EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); header.header.sequenceNumber = 5; rtp_stream_receiver_->OnReceivedPayloadData(nullptr, 0, &header); } } // namespace webrtc