/* * Copyright (c) 2004 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/media/base/testutils.h" #include #include #include #include "webrtc/base/bytebuffer.h" #include "webrtc/base/fileutils.h" #include "webrtc/base/gunit.h" #include "webrtc/base/pathutils.h" #include "webrtc/base/stream.h" #include "webrtc/base/stringutils.h" #include "webrtc/base/testutils.h" #include "webrtc/media/base/rtpdump.h" #include "webrtc/media/base/videocapturer.h" #include "webrtc/media/base/videoframe.h" namespace cricket { ///////////////////////////////////////////////////////////////////////// // Implementation of RawRtpPacket ///////////////////////////////////////////////////////////////////////// void RawRtpPacket::WriteToByteBuffer(uint32_t in_ssrc, rtc::ByteBufferWriter* buf) const { if (!buf) return; buf->WriteUInt8(ver_to_cc); buf->WriteUInt8(m_to_pt); buf->WriteUInt16(sequence_number); buf->WriteUInt32(timestamp); buf->WriteUInt32(in_ssrc); buf->WriteBytes(payload, sizeof(payload)); } bool RawRtpPacket::ReadFromByteBuffer(rtc::ByteBufferReader* buf) { if (!buf) return false; bool ret = true; ret &= buf->ReadUInt8(&ver_to_cc); ret &= buf->ReadUInt8(&m_to_pt); ret &= buf->ReadUInt16(&sequence_number); ret &= buf->ReadUInt32(×tamp); ret &= buf->ReadUInt32(&ssrc); ret &= buf->ReadBytes(payload, sizeof(payload)); return ret; } bool RawRtpPacket::SameExceptSeqNumTimestampSsrc(const RawRtpPacket& packet, uint16_t seq, uint32_t ts, uint32_t ssc) const { return sequence_number == seq && timestamp == ts && ver_to_cc == packet.ver_to_cc && m_to_pt == packet.m_to_pt && ssrc == ssc && 0 == memcmp(payload, packet.payload, sizeof(payload)); } ///////////////////////////////////////////////////////////////////////// // Implementation of RawRtcpPacket ///////////////////////////////////////////////////////////////////////// void RawRtcpPacket::WriteToByteBuffer(rtc::ByteBufferWriter *buf) const { if (!buf) return; buf->WriteUInt8(ver_to_count); buf->WriteUInt8(type); buf->WriteUInt16(length); buf->WriteBytes(payload, sizeof(payload)); } bool RawRtcpPacket::ReadFromByteBuffer(rtc::ByteBufferReader* buf) { if (!buf) return false; bool ret = true; ret &= buf->ReadUInt8(&ver_to_count); ret &= buf->ReadUInt8(&type); ret &= buf->ReadUInt16(&length); ret &= buf->ReadBytes(payload, sizeof(payload)); return ret; } bool RawRtcpPacket::EqualsTo(const RawRtcpPacket& packet) const { return ver_to_count == packet.ver_to_count && type == packet.type && length == packet.length && 0 == memcmp(payload, packet.payload, sizeof(payload)); } ///////////////////////////////////////////////////////////////////////// // Implementation of class RtpTestUtility ///////////////////////////////////////////////////////////////////////// const RawRtpPacket RtpTestUtility::kTestRawRtpPackets[] = { {0x80, 0, 0, 0, RtpTestUtility::kDefaultSsrc, "RTP frame 0"}, {0x80, 0, 1, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, {0x80, 0, 2, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, {0x80, 0, 3, 60, RtpTestUtility::kDefaultSsrc, "RTP frame 2"} }; const RawRtcpPacket RtpTestUtility::kTestRawRtcpPackets[] = { // The Version is 2, the Length is 2, and the payload has 8 bytes. {0x80, 0, 2, "RTCP0000"}, {0x80, 0, 2, "RTCP0001"}, {0x80, 0, 2, "RTCP0002"}, {0x80, 0, 2, "RTCP0003"}, }; size_t RtpTestUtility::GetTestPacketCount() { return std::min(arraysize(kTestRawRtpPackets), arraysize(kTestRawRtcpPackets)); } bool RtpTestUtility::WriteTestPackets(size_t count, bool rtcp, uint32_t rtp_ssrc, RtpDumpWriter* writer) { if (!writer || count > GetTestPacketCount()) return false; bool result = true; uint32_t elapsed_time_ms = 0; for (size_t i = 0; i < count && result; ++i) { rtc::ByteBufferWriter buf; if (rtcp) { kTestRawRtcpPackets[i].WriteToByteBuffer(&buf); } else { kTestRawRtpPackets[i].WriteToByteBuffer(rtp_ssrc, &buf); } RtpDumpPacket dump_packet(buf.Data(), buf.Length(), elapsed_time_ms, rtcp); elapsed_time_ms += kElapsedTimeInterval; result &= (rtc::SR_SUCCESS == writer->WritePacket(dump_packet)); } return result; } bool RtpTestUtility::VerifyTestPacketsFromStream(size_t count, rtc::StreamInterface* stream, uint32_t ssrc) { if (!stream) return false; uint32_t prev_elapsed_time = 0; bool result = true; stream->Rewind(); RtpDumpLoopReader reader(stream); for (size_t i = 0; i < count && result; ++i) { // Which loop and which index in the loop are we reading now. size_t loop = i / GetTestPacketCount(); size_t index = i % GetTestPacketCount(); RtpDumpPacket packet; result &= (rtc::SR_SUCCESS == reader.ReadPacket(&packet)); // Check the elapsed time of the dump packet. result &= (packet.elapsed_time >= prev_elapsed_time); prev_elapsed_time = packet.elapsed_time; // Check the RTP or RTCP packet. rtc::ByteBufferReader buf(reinterpret_cast(&packet.data[0]), packet.data.size()); if (packet.is_rtcp()) { // RTCP packet. RawRtcpPacket rtcp_packet; result &= rtcp_packet.ReadFromByteBuffer(&buf); result &= rtcp_packet.EqualsTo(kTestRawRtcpPackets[index]); } else { // RTP packet. RawRtpPacket rtp_packet; result &= rtp_packet.ReadFromByteBuffer(&buf); result &= rtp_packet.SameExceptSeqNumTimestampSsrc( kTestRawRtpPackets[index], static_cast(kTestRawRtpPackets[index].sequence_number + loop * GetTestPacketCount()), static_cast(kTestRawRtpPackets[index].timestamp + loop * kRtpTimestampIncrease), ssrc); } } stream->Rewind(); return result; } bool RtpTestUtility::VerifyPacket(const RtpDumpPacket* dump, const RawRtpPacket* raw, bool header_only) { if (!dump || !raw) return false; rtc::ByteBufferWriter buf; raw->WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf); if (header_only) { size_t header_len = 0; dump->GetRtpHeaderLen(&header_len); return header_len == dump->data.size() && buf.Length() > dump->data.size() && 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); } else { return buf.Length() == dump->data.size() && 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); } } // Implementation of VideoCaptureListener. VideoCapturerListener::VideoCapturerListener(VideoCapturer* capturer) : last_capture_state_(CS_STARTING), frame_count_(0), frame_fourcc_(0), frame_width_(0), frame_height_(0), frame_size_(0), resolution_changed_(false) { capturer->SignalStateChange.connect(this, &VideoCapturerListener::OnStateChange); capturer->SignalFrameCaptured.connect(this, &VideoCapturerListener::OnFrameCaptured); } void VideoCapturerListener::OnStateChange(VideoCapturer* capturer, CaptureState result) { last_capture_state_ = result; } void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer, const CapturedFrame* frame) { ++frame_count_; if (1 == frame_count_) { frame_fourcc_ = frame->fourcc; frame_width_ = frame->width; frame_height_ = frame->height; frame_size_ = frame->data_size; } else if (frame_width_ != frame->width || frame_height_ != frame->height) { resolution_changed_ = true; } } cricket::StreamParams CreateSimStreamParams( const std::string& cname, const std::vector& ssrcs) { cricket::StreamParams sp; cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs); sp.ssrcs = ssrcs; sp.ssrc_groups.push_back(sg); sp.cname = cname; return sp; } // There should be an rtx_ssrc per ssrc. cricket::StreamParams CreateSimWithRtxStreamParams( const std::string& cname, const std::vector& ssrcs, const std::vector& rtx_ssrcs) { cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs); for (size_t i = 0; i < ssrcs.size(); ++i) { sp.ssrcs.push_back(rtx_ssrcs[i]); std::vector fid_ssrcs; fid_ssrcs.push_back(ssrcs[i]); fid_ssrcs.push_back(rtx_ssrcs[i]); cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs); sp.ssrc_groups.push_back(fid_group); } return sp; } } // namespace cricket