blob: d28fa0fc6df7fbaa42570810738b4060cea0b1bc [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
kjellandera96e2d72016-02-04 23:52:28 -080011#include "webrtc/media/base/testutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
13#include <math.h>
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000014#include <algorithm>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000015
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000016#include "webrtc/base/bytebuffer.h"
17#include "webrtc/base/fileutils.h"
18#include "webrtc/base/gunit.h"
19#include "webrtc/base/pathutils.h"
20#include "webrtc/base/stream.h"
21#include "webrtc/base/stringutils.h"
22#include "webrtc/base/testutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080023#include "webrtc/media/base/executablehelpers.h"
24#include "webrtc/media/base/rtpdump.h"
25#include "webrtc/media/base/videocapturer.h"
26#include "webrtc/media/base/videoframe.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000027
28namespace cricket {
29
30/////////////////////////////////////////////////////////////////////////
31// Implementation of RawRtpPacket
32/////////////////////////////////////////////////////////////////////////
Peter Boström0c4e06b2015-10-07 12:23:21 +020033void RawRtpPacket::WriteToByteBuffer(uint32_t in_ssrc,
34 rtc::ByteBuffer* buf) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035 if (!buf) return;
36
37 buf->WriteUInt8(ver_to_cc);
38 buf->WriteUInt8(m_to_pt);
39 buf->WriteUInt16(sequence_number);
40 buf->WriteUInt32(timestamp);
41 buf->WriteUInt32(in_ssrc);
42 buf->WriteBytes(payload, sizeof(payload));
43}
44
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000045bool RawRtpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046 if (!buf) return false;
47
48 bool ret = true;
49 ret &= buf->ReadUInt8(&ver_to_cc);
50 ret &= buf->ReadUInt8(&m_to_pt);
51 ret &= buf->ReadUInt16(&sequence_number);
52 ret &= buf->ReadUInt32(&timestamp);
53 ret &= buf->ReadUInt32(&ssrc);
54 ret &= buf->ReadBytes(payload, sizeof(payload));
55 return ret;
56}
57
Peter Boström0c4e06b2015-10-07 12:23:21 +020058bool RawRtpPacket::SameExceptSeqNumTimestampSsrc(const RawRtpPacket& packet,
59 uint16_t seq,
60 uint32_t ts,
61 uint32_t ssc) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062 return sequence_number == seq &&
63 timestamp == ts &&
64 ver_to_cc == packet.ver_to_cc &&
65 m_to_pt == packet.m_to_pt &&
66 ssrc == ssc &&
67 0 == memcmp(payload, packet.payload, sizeof(payload));
68}
69
70/////////////////////////////////////////////////////////////////////////
71// Implementation of RawRtcpPacket
72/////////////////////////////////////////////////////////////////////////
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000073void RawRtcpPacket::WriteToByteBuffer(rtc::ByteBuffer *buf) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000074 if (!buf) return;
75
76 buf->WriteUInt8(ver_to_count);
77 buf->WriteUInt8(type);
78 buf->WriteUInt16(length);
79 buf->WriteBytes(payload, sizeof(payload));
80}
81
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000082bool RawRtcpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000083 if (!buf) return false;
84
85 bool ret = true;
86 ret &= buf->ReadUInt8(&ver_to_count);
87 ret &= buf->ReadUInt8(&type);
88 ret &= buf->ReadUInt16(&length);
89 ret &= buf->ReadBytes(payload, sizeof(payload));
90 return ret;
91}
92
93bool RawRtcpPacket::EqualsTo(const RawRtcpPacket& packet) const {
94 return ver_to_count == packet.ver_to_count &&
95 type == packet.type &&
96 length == packet.length &&
97 0 == memcmp(payload, packet.payload, sizeof(payload));
98}
99
100/////////////////////////////////////////////////////////////////////////
101// Implementation of class RtpTestUtility
102/////////////////////////////////////////////////////////////////////////
103const RawRtpPacket RtpTestUtility::kTestRawRtpPackets[] = {
104 {0x80, 0, 0, 0, RtpTestUtility::kDefaultSsrc, "RTP frame 0"},
105 {0x80, 0, 1, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"},
106 {0x80, 0, 2, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"},
107 {0x80, 0, 3, 60, RtpTestUtility::kDefaultSsrc, "RTP frame 2"}
108};
109const RawRtcpPacket RtpTestUtility::kTestRawRtcpPackets[] = {
110 // The Version is 2, the Length is 2, and the payload has 8 bytes.
111 {0x80, 0, 2, "RTCP0000"},
112 {0x80, 0, 2, "RTCP0001"},
113 {0x80, 0, 2, "RTCP0002"},
114 {0x80, 0, 2, "RTCP0003"},
115};
116
117size_t RtpTestUtility::GetTestPacketCount() {
tfarina5237aaf2015-11-10 23:44:30 -0800118 return std::min(arraysize(kTestRawRtpPackets),
119 arraysize(kTestRawRtcpPackets));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120}
121
Peter Boström0c4e06b2015-10-07 12:23:21 +0200122bool RtpTestUtility::WriteTestPackets(size_t count,
123 bool rtcp,
124 uint32_t rtp_ssrc,
125 RtpDumpWriter* writer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126 if (!writer || count > GetTestPacketCount()) return false;
127
128 bool result = true;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200129 uint32_t elapsed_time_ms = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130 for (size_t i = 0; i < count && result; ++i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000131 rtc::ByteBuffer buf;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 if (rtcp) {
133 kTestRawRtcpPackets[i].WriteToByteBuffer(&buf);
134 } else {
135 kTestRawRtpPackets[i].WriteToByteBuffer(rtp_ssrc, &buf);
136 }
137
138 RtpDumpPacket dump_packet(buf.Data(), buf.Length(), elapsed_time_ms, rtcp);
139 elapsed_time_ms += kElapsedTimeInterval;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000140 result &= (rtc::SR_SUCCESS == writer->WritePacket(dump_packet));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141 }
142 return result;
143}
144
Peter Boström0c4e06b2015-10-07 12:23:21 +0200145bool RtpTestUtility::VerifyTestPacketsFromStream(size_t count,
146 rtc::StreamInterface* stream,
147 uint32_t ssrc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148 if (!stream) return false;
149
Peter Boström0c4e06b2015-10-07 12:23:21 +0200150 uint32_t prev_elapsed_time = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151 bool result = true;
152 stream->Rewind();
153 RtpDumpLoopReader reader(stream);
154 for (size_t i = 0; i < count && result; ++i) {
155 // Which loop and which index in the loop are we reading now.
156 size_t loop = i / GetTestPacketCount();
157 size_t index = i % GetTestPacketCount();
158
159 RtpDumpPacket packet;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000160 result &= (rtc::SR_SUCCESS == reader.ReadPacket(&packet));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000161 // Check the elapsed time of the dump packet.
162 result &= (packet.elapsed_time >= prev_elapsed_time);
163 prev_elapsed_time = packet.elapsed_time;
164
165 // Check the RTP or RTCP packet.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000166 rtc::ByteBuffer buf(reinterpret_cast<const char*>(&packet.data[0]),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000167 packet.data.size());
168 if (packet.is_rtcp()) {
169 // RTCP packet.
170 RawRtcpPacket rtcp_packet;
171 result &= rtcp_packet.ReadFromByteBuffer(&buf);
172 result &= rtcp_packet.EqualsTo(kTestRawRtcpPackets[index]);
173 } else {
174 // RTP packet.
175 RawRtpPacket rtp_packet;
176 result &= rtp_packet.ReadFromByteBuffer(&buf);
177 result &= rtp_packet.SameExceptSeqNumTimestampSsrc(
178 kTestRawRtpPackets[index],
Peter Boström0c4e06b2015-10-07 12:23:21 +0200179 static_cast<uint16_t>(kTestRawRtpPackets[index].sequence_number +
180 loop * GetTestPacketCount()),
181 static_cast<uint32_t>(kTestRawRtpPackets[index].timestamp +
182 loop * kRtpTimestampIncrease),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183 ssrc);
184 }
185 }
186
187 stream->Rewind();
188 return result;
189}
190
191bool RtpTestUtility::VerifyPacket(const RtpDumpPacket* dump,
192 const RawRtpPacket* raw,
193 bool header_only) {
194 if (!dump || !raw) return false;
195
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000196 rtc::ByteBuffer buf;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197 raw->WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf);
198
199 if (header_only) {
200 size_t header_len = 0;
201 dump->GetRtpHeaderLen(&header_len);
202 return header_len == dump->data.size() &&
203 buf.Length() > dump->data.size() &&
204 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size());
205 } else {
206 return buf.Length() == dump->data.size() &&
207 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size());
208 }
209}
210
211// Implementation of VideoCaptureListener.
212VideoCapturerListener::VideoCapturerListener(VideoCapturer* capturer)
213 : last_capture_state_(CS_STARTING),
214 frame_count_(0),
215 frame_fourcc_(0),
216 frame_width_(0),
217 frame_height_(0),
218 frame_size_(0),
219 resolution_changed_(false) {
220 capturer->SignalStateChange.connect(this,
221 &VideoCapturerListener::OnStateChange);
222 capturer->SignalFrameCaptured.connect(this,
223 &VideoCapturerListener::OnFrameCaptured);
224}
225
226void VideoCapturerListener::OnStateChange(VideoCapturer* capturer,
227 CaptureState result) {
228 last_capture_state_ = result;
229}
230
231void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer,
232 const CapturedFrame* frame) {
233 ++frame_count_;
234 if (1 == frame_count_) {
235 frame_fourcc_ = frame->fourcc;
236 frame_width_ = frame->width;
237 frame_height_ = frame->height;
238 frame_size_ = frame->data_size;
239 } else if (frame_width_ != frame->width || frame_height_ != frame->height) {
240 resolution_changed_ = true;
241 }
242}
243
kjellandera96e2d72016-02-04 23:52:28 -0800244// Returns the absolute path to a file in the resources/ directory.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000245std::string GetTestFilePath(const std::string& filename) {
246 // Locate test data directory.
thorcarpenter@google.coma3344cf2014-09-05 16:34:13 +0000247#ifdef ENABLE_WEBRTC
248 rtc::Pathname path = rtc::GetExecutablePath();
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000249 EXPECT_FALSE(path.empty());
kjellandera96e2d72016-02-04 23:52:28 -0800250 path.AppendPathname("../../resources/");
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000251#else
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000252 rtc::Pathname path = testing::GetTalkDirectory();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 EXPECT_FALSE(path.empty()); // must be run from inside "talk"
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000254#endif
kjellandera96e2d72016-02-04 23:52:28 -0800255 path.AppendFolder("media/");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256 path.SetFilename(filename);
257 return path.pathname();
258}
259
260// Loads the image with the specified prefix and size into |out|.
261bool LoadPlanarYuvTestImage(const std::string& prefix,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200262 int width,
263 int height,
264 uint8_t* out) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 std::stringstream ss;
266 ss << prefix << "." << width << "x" << height << "_P420.yuv";
267
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000268 rtc::scoped_ptr<rtc::FileStream> stream(
269 rtc::Filesystem::OpenFile(rtc::Pathname(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 GetTestFilePath(ss.str())), "rb"));
271 if (!stream) {
272 return false;
273 }
274
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000275 rtc::StreamResult res =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 stream->ReadAll(out, I420_SIZE(width, height), NULL, NULL);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000277 return (res == rtc::SR_SUCCESS);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278}
279
280// Dumps the YUV image out to a file, for visual inspection.
281// PYUV tool can be used to view dump files.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200282void DumpPlanarYuvTestImage(const std::string& prefix,
283 const uint8_t* img,
284 int w,
285 int h) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000286 rtc::FileStream fs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 char filename[256];
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000288 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 prefix.c_str(), w, h);
290 fs.Open(filename, "wb", NULL);
291 fs.Write(img, I420_SIZE(w, h), NULL, NULL);
292}
293
294// Dumps the ARGB image out to a file, for visual inspection.
295// ffplay tool can be used to view dump files.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200296void DumpPlanarArgbTestImage(const std::string& prefix,
297 const uint8_t* img,
298 int w,
299 int h) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000300 rtc::FileStream fs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301 char filename[256];
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000302 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_ARGB.raw",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 prefix.c_str(), w, h);
304 fs.Open(filename, "wb", NULL);
305 fs.Write(img, ARGB_SIZE(w, h), NULL, NULL);
306}
307
308bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200309 const uint8_t* y0 = frame0->GetYPlane();
310 const uint8_t* u0 = frame0->GetUPlane();
311 const uint8_t* v0 = frame0->GetVPlane();
312 const uint8_t* y1 = frame1->GetYPlane();
313 const uint8_t* u1 = frame1->GetUPlane();
314 const uint8_t* v1 = frame1->GetVPlane();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315
316 for (size_t i = 0; i < frame0->GetHeight(); ++i) {
317 if (0 != memcmp(y0, y1, frame0->GetWidth())) {
318 return false;
319 }
320 y0 += frame0->GetYPitch();
321 y1 += frame1->GetYPitch();
322 }
323
324 for (size_t i = 0; i < frame0->GetChromaHeight(); ++i) {
325 if (0 != memcmp(u0, u1, frame0->GetChromaWidth())) {
326 return false;
327 }
328 if (0 != memcmp(v0, v1, frame0->GetChromaWidth())) {
329 return false;
330 }
331 u0 += frame0->GetUPitch();
332 v0 += frame0->GetVPitch();
333 u1 += frame1->GetUPitch();
334 v1 += frame1->GetVPitch();
335 }
336
337 return true;
338}
339
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000340cricket::StreamParams CreateSimStreamParams(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200341 const std::string& cname,
342 const std::vector<uint32_t>& ssrcs) {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000343 cricket::StreamParams sp;
344 cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs);
345 sp.ssrcs = ssrcs;
346 sp.ssrc_groups.push_back(sg);
347 sp.cname = cname;
348 return sp;
349}
350
351// There should be an rtx_ssrc per ssrc.
352cricket::StreamParams CreateSimWithRtxStreamParams(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200353 const std::string& cname,
354 const std::vector<uint32_t>& ssrcs,
355 const std::vector<uint32_t>& rtx_ssrcs) {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000356 cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs);
357 for (size_t i = 0; i < ssrcs.size(); ++i) {
358 sp.ssrcs.push_back(rtx_ssrcs[i]);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200359 std::vector<uint32_t> fid_ssrcs;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000360 fid_ssrcs.push_back(ssrcs[i]);
361 fid_ssrcs.push_back(rtx_ssrcs[i]);
362 cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs);
363 sp.ssrc_groups.push_back(fid_group);
364 }
365 return sp;
366}
367
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000368} // namespace cricket