blob: 33d0a9727dddc3b8ed8945802d9c58dd466b3cde [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>
kwiberg686a8ef2016-02-26 03:00:35 -080015#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000016
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000017#include "webrtc/base/bytebuffer.h"
18#include "webrtc/base/fileutils.h"
19#include "webrtc/base/gunit.h"
20#include "webrtc/base/pathutils.h"
21#include "webrtc/base/stream.h"
22#include "webrtc/base/stringutils.h"
23#include "webrtc/base/testutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080024#include "webrtc/media/base/executablehelpers.h"
25#include "webrtc/media/base/rtpdump.h"
26#include "webrtc/media/base/videocapturer.h"
27#include "webrtc/media/base/videoframe.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000028
29namespace cricket {
30
31/////////////////////////////////////////////////////////////////////////
32// Implementation of RawRtpPacket
33/////////////////////////////////////////////////////////////////////////
Peter Boström0c4e06b2015-10-07 12:23:21 +020034void RawRtpPacket::WriteToByteBuffer(uint32_t in_ssrc,
35 rtc::ByteBuffer* buf) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036 if (!buf) return;
37
38 buf->WriteUInt8(ver_to_cc);
39 buf->WriteUInt8(m_to_pt);
40 buf->WriteUInt16(sequence_number);
41 buf->WriteUInt32(timestamp);
42 buf->WriteUInt32(in_ssrc);
43 buf->WriteBytes(payload, sizeof(payload));
44}
45
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000046bool RawRtpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000047 if (!buf) return false;
48
49 bool ret = true;
50 ret &= buf->ReadUInt8(&ver_to_cc);
51 ret &= buf->ReadUInt8(&m_to_pt);
52 ret &= buf->ReadUInt16(&sequence_number);
53 ret &= buf->ReadUInt32(&timestamp);
54 ret &= buf->ReadUInt32(&ssrc);
55 ret &= buf->ReadBytes(payload, sizeof(payload));
56 return ret;
57}
58
Peter Boström0c4e06b2015-10-07 12:23:21 +020059bool RawRtpPacket::SameExceptSeqNumTimestampSsrc(const RawRtpPacket& packet,
60 uint16_t seq,
61 uint32_t ts,
62 uint32_t ssc) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063 return sequence_number == seq &&
64 timestamp == ts &&
65 ver_to_cc == packet.ver_to_cc &&
66 m_to_pt == packet.m_to_pt &&
67 ssrc == ssc &&
68 0 == memcmp(payload, packet.payload, sizeof(payload));
69}
70
71/////////////////////////////////////////////////////////////////////////
72// Implementation of RawRtcpPacket
73/////////////////////////////////////////////////////////////////////////
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000074void RawRtcpPacket::WriteToByteBuffer(rtc::ByteBuffer *buf) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000075 if (!buf) return;
76
77 buf->WriteUInt8(ver_to_count);
78 buf->WriteUInt8(type);
79 buf->WriteUInt16(length);
80 buf->WriteBytes(payload, sizeof(payload));
81}
82
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000083bool RawRtcpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000084 if (!buf) return false;
85
86 bool ret = true;
87 ret &= buf->ReadUInt8(&ver_to_count);
88 ret &= buf->ReadUInt8(&type);
89 ret &= buf->ReadUInt16(&length);
90 ret &= buf->ReadBytes(payload, sizeof(payload));
91 return ret;
92}
93
94bool RawRtcpPacket::EqualsTo(const RawRtcpPacket& packet) const {
95 return ver_to_count == packet.ver_to_count &&
96 type == packet.type &&
97 length == packet.length &&
98 0 == memcmp(payload, packet.payload, sizeof(payload));
99}
100
101/////////////////////////////////////////////////////////////////////////
102// Implementation of class RtpTestUtility
103/////////////////////////////////////////////////////////////////////////
104const RawRtpPacket RtpTestUtility::kTestRawRtpPackets[] = {
105 {0x80, 0, 0, 0, RtpTestUtility::kDefaultSsrc, "RTP frame 0"},
106 {0x80, 0, 1, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"},
107 {0x80, 0, 2, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"},
108 {0x80, 0, 3, 60, RtpTestUtility::kDefaultSsrc, "RTP frame 2"}
109};
110const RawRtcpPacket RtpTestUtility::kTestRawRtcpPackets[] = {
111 // The Version is 2, the Length is 2, and the payload has 8 bytes.
112 {0x80, 0, 2, "RTCP0000"},
113 {0x80, 0, 2, "RTCP0001"},
114 {0x80, 0, 2, "RTCP0002"},
115 {0x80, 0, 2, "RTCP0003"},
116};
117
118size_t RtpTestUtility::GetTestPacketCount() {
tfarina5237aaf2015-11-10 23:44:30 -0800119 return std::min(arraysize(kTestRawRtpPackets),
120 arraysize(kTestRawRtcpPackets));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121}
122
Peter Boström0c4e06b2015-10-07 12:23:21 +0200123bool RtpTestUtility::WriteTestPackets(size_t count,
124 bool rtcp,
125 uint32_t rtp_ssrc,
126 RtpDumpWriter* writer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 if (!writer || count > GetTestPacketCount()) return false;
128
129 bool result = true;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200130 uint32_t elapsed_time_ms = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000131 for (size_t i = 0; i < count && result; ++i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000132 rtc::ByteBuffer buf;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133 if (rtcp) {
134 kTestRawRtcpPackets[i].WriteToByteBuffer(&buf);
135 } else {
136 kTestRawRtpPackets[i].WriteToByteBuffer(rtp_ssrc, &buf);
137 }
138
139 RtpDumpPacket dump_packet(buf.Data(), buf.Length(), elapsed_time_ms, rtcp);
140 elapsed_time_ms += kElapsedTimeInterval;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000141 result &= (rtc::SR_SUCCESS == writer->WritePacket(dump_packet));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142 }
143 return result;
144}
145
Peter Boström0c4e06b2015-10-07 12:23:21 +0200146bool RtpTestUtility::VerifyTestPacketsFromStream(size_t count,
147 rtc::StreamInterface* stream,
148 uint32_t ssrc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149 if (!stream) return false;
150
Peter Boström0c4e06b2015-10-07 12:23:21 +0200151 uint32_t prev_elapsed_time = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000152 bool result = true;
153 stream->Rewind();
154 RtpDumpLoopReader reader(stream);
155 for (size_t i = 0; i < count && result; ++i) {
156 // Which loop and which index in the loop are we reading now.
157 size_t loop = i / GetTestPacketCount();
158 size_t index = i % GetTestPacketCount();
159
160 RtpDumpPacket packet;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000161 result &= (rtc::SR_SUCCESS == reader.ReadPacket(&packet));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162 // Check the elapsed time of the dump packet.
163 result &= (packet.elapsed_time >= prev_elapsed_time);
164 prev_elapsed_time = packet.elapsed_time;
165
166 // Check the RTP or RTCP packet.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000167 rtc::ByteBuffer buf(reinterpret_cast<const char*>(&packet.data[0]),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000168 packet.data.size());
169 if (packet.is_rtcp()) {
170 // RTCP packet.
171 RawRtcpPacket rtcp_packet;
172 result &= rtcp_packet.ReadFromByteBuffer(&buf);
173 result &= rtcp_packet.EqualsTo(kTestRawRtcpPackets[index]);
174 } else {
175 // RTP packet.
176 RawRtpPacket rtp_packet;
177 result &= rtp_packet.ReadFromByteBuffer(&buf);
178 result &= rtp_packet.SameExceptSeqNumTimestampSsrc(
179 kTestRawRtpPackets[index],
Peter Boström0c4e06b2015-10-07 12:23:21 +0200180 static_cast<uint16_t>(kTestRawRtpPackets[index].sequence_number +
181 loop * GetTestPacketCount()),
182 static_cast<uint32_t>(kTestRawRtpPackets[index].timestamp +
183 loop * kRtpTimestampIncrease),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000184 ssrc);
185 }
186 }
187
188 stream->Rewind();
189 return result;
190}
191
192bool RtpTestUtility::VerifyPacket(const RtpDumpPacket* dump,
193 const RawRtpPacket* raw,
194 bool header_only) {
195 if (!dump || !raw) return false;
196
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000197 rtc::ByteBuffer buf;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000198 raw->WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf);
199
200 if (header_only) {
201 size_t header_len = 0;
202 dump->GetRtpHeaderLen(&header_len);
203 return header_len == dump->data.size() &&
204 buf.Length() > dump->data.size() &&
205 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size());
206 } else {
207 return buf.Length() == dump->data.size() &&
208 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size());
209 }
210}
211
212// Implementation of VideoCaptureListener.
213VideoCapturerListener::VideoCapturerListener(VideoCapturer* capturer)
214 : last_capture_state_(CS_STARTING),
215 frame_count_(0),
216 frame_fourcc_(0),
217 frame_width_(0),
218 frame_height_(0),
219 frame_size_(0),
220 resolution_changed_(false) {
221 capturer->SignalStateChange.connect(this,
222 &VideoCapturerListener::OnStateChange);
223 capturer->SignalFrameCaptured.connect(this,
224 &VideoCapturerListener::OnFrameCaptured);
225}
226
227void VideoCapturerListener::OnStateChange(VideoCapturer* capturer,
228 CaptureState result) {
229 last_capture_state_ = result;
230}
231
232void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer,
233 const CapturedFrame* frame) {
234 ++frame_count_;
235 if (1 == frame_count_) {
236 frame_fourcc_ = frame->fourcc;
237 frame_width_ = frame->width;
238 frame_height_ = frame->height;
239 frame_size_ = frame->data_size;
240 } else if (frame_width_ != frame->width || frame_height_ != frame->height) {
241 resolution_changed_ = true;
242 }
243}
244
kjellandera96e2d72016-02-04 23:52:28 -0800245// Returns the absolute path to a file in the resources/ directory.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246std::string GetTestFilePath(const std::string& filename) {
247 // Locate test data directory.
thorcarpenter@google.coma3344cf2014-09-05 16:34:13 +0000248#ifdef ENABLE_WEBRTC
249 rtc::Pathname path = rtc::GetExecutablePath();
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000250 EXPECT_FALSE(path.empty());
kjellandera96e2d72016-02-04 23:52:28 -0800251 path.AppendPathname("../../resources/");
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000252#else
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000253 rtc::Pathname path = testing::GetTalkDirectory();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 EXPECT_FALSE(path.empty()); // must be run from inside "talk"
buildbot@webrtc.org72e44852014-09-03 00:43:48 +0000255#endif
kjellandera96e2d72016-02-04 23:52:28 -0800256 path.AppendFolder("media/");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 path.SetFilename(filename);
258 return path.pathname();
259}
260
261// Loads the image with the specified prefix and size into |out|.
262bool LoadPlanarYuvTestImage(const std::string& prefix,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200263 int width,
264 int height,
265 uint8_t* out) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 std::stringstream ss;
267 ss << prefix << "." << width << "x" << height << "_P420.yuv";
268
kwiberg686a8ef2016-02-26 03:00:35 -0800269 std::unique_ptr<rtc::FileStream> stream(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000270 rtc::Filesystem::OpenFile(rtc::Pathname(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000271 GetTestFilePath(ss.str())), "rb"));
272 if (!stream) {
273 return false;
274 }
275
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000276 rtc::StreamResult res =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277 stream->ReadAll(out, I420_SIZE(width, height), NULL, NULL);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000278 return (res == rtc::SR_SUCCESS);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279}
280
281// Dumps the YUV image out to a file, for visual inspection.
282// PYUV tool can be used to view dump files.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200283void DumpPlanarYuvTestImage(const std::string& prefix,
284 const uint8_t* img,
285 int w,
286 int h) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000287 rtc::FileStream fs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288 char filename[256];
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000289 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290 prefix.c_str(), w, h);
291 fs.Open(filename, "wb", NULL);
292 fs.Write(img, I420_SIZE(w, h), NULL, NULL);
293}
294
295// Dumps the ARGB image out to a file, for visual inspection.
296// ffplay tool can be used to view dump files.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200297void DumpPlanarArgbTestImage(const std::string& prefix,
298 const uint8_t* img,
299 int w,
300 int h) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000301 rtc::FileStream fs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000302 char filename[256];
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000303 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_ARGB.raw",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000304 prefix.c_str(), w, h);
305 fs.Open(filename, "wb", NULL);
306 fs.Write(img, ARGB_SIZE(w, h), NULL, NULL);
307}
308
309bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200310 const uint8_t* y0 = frame0->GetYPlane();
311 const uint8_t* u0 = frame0->GetUPlane();
312 const uint8_t* v0 = frame0->GetVPlane();
313 const uint8_t* y1 = frame1->GetYPlane();
314 const uint8_t* u1 = frame1->GetUPlane();
315 const uint8_t* v1 = frame1->GetVPlane();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000316
317 for (size_t i = 0; i < frame0->GetHeight(); ++i) {
318 if (0 != memcmp(y0, y1, frame0->GetWidth())) {
319 return false;
320 }
321 y0 += frame0->GetYPitch();
322 y1 += frame1->GetYPitch();
323 }
324
325 for (size_t i = 0; i < frame0->GetChromaHeight(); ++i) {
326 if (0 != memcmp(u0, u1, frame0->GetChromaWidth())) {
327 return false;
328 }
329 if (0 != memcmp(v0, v1, frame0->GetChromaWidth())) {
330 return false;
331 }
332 u0 += frame0->GetUPitch();
333 v0 += frame0->GetVPitch();
334 u1 += frame1->GetUPitch();
335 v1 += frame1->GetVPitch();
336 }
337
338 return true;
339}
340
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000341cricket::StreamParams CreateSimStreamParams(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200342 const std::string& cname,
343 const std::vector<uint32_t>& ssrcs) {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000344 cricket::StreamParams sp;
345 cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs);
346 sp.ssrcs = ssrcs;
347 sp.ssrc_groups.push_back(sg);
348 sp.cname = cname;
349 return sp;
350}
351
352// There should be an rtx_ssrc per ssrc.
353cricket::StreamParams CreateSimWithRtxStreamParams(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200354 const std::string& cname,
355 const std::vector<uint32_t>& ssrcs,
356 const std::vector<uint32_t>& rtx_ssrcs) {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000357 cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs);
358 for (size_t i = 0; i < ssrcs.size(); ++i) {
359 sp.ssrcs.push_back(rtx_ssrcs[i]);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200360 std::vector<uint32_t> fid_ssrcs;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000361 fid_ssrcs.push_back(ssrcs[i]);
362 fid_ssrcs.push_back(rtx_ssrcs[i]);
363 cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs);
364 sp.ssrc_groups.push_back(fid_group);
365 }
366 return sp;
367}
368
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369} // namespace cricket