1 /* 2 * libjingle 3 * Copyright 2004 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/media/base/testutils.h" 29 30 #include <math.h> 31 #include <algorithm> 32 33 #include "talk/media/base/executablehelpers.h" 34 #include "talk/media/base/rtpdump.h" 35 #include "talk/media/base/videocapturer.h" 36 #include "talk/media/base/videoframe.h" 37 #include "webrtc/base/bytebuffer.h" 38 #include "webrtc/base/fileutils.h" 39 #include "webrtc/base/gunit.h" 40 #include "webrtc/base/pathutils.h" 41 #include "webrtc/base/stream.h" 42 #include "webrtc/base/stringutils.h" 43 #include "webrtc/base/testutils.h" 44 45 namespace cricket { 46 47 ///////////////////////////////////////////////////////////////////////// 48 // Implementation of RawRtpPacket 49 ///////////////////////////////////////////////////////////////////////// 50 void RawRtpPacket::WriteToByteBuffer(uint32_t in_ssrc, 51 rtc::ByteBuffer* buf) const { 52 if (!buf) return; 53 54 buf->WriteUInt8(ver_to_cc); 55 buf->WriteUInt8(m_to_pt); 56 buf->WriteUInt16(sequence_number); 57 buf->WriteUInt32(timestamp); 58 buf->WriteUInt32(in_ssrc); 59 buf->WriteBytes(payload, sizeof(payload)); 60 } 61 62 bool RawRtpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) { 63 if (!buf) return false; 64 65 bool ret = true; 66 ret &= buf->ReadUInt8(&ver_to_cc); 67 ret &= buf->ReadUInt8(&m_to_pt); 68 ret &= buf->ReadUInt16(&sequence_number); 69 ret &= buf->ReadUInt32(×tamp); 70 ret &= buf->ReadUInt32(&ssrc); 71 ret &= buf->ReadBytes(payload, sizeof(payload)); 72 return ret; 73 } 74 75 bool RawRtpPacket::SameExceptSeqNumTimestampSsrc(const RawRtpPacket& packet, 76 uint16_t seq, 77 uint32_t ts, 78 uint32_t ssc) const { 79 return sequence_number == seq && 80 timestamp == ts && 81 ver_to_cc == packet.ver_to_cc && 82 m_to_pt == packet.m_to_pt && 83 ssrc == ssc && 84 0 == memcmp(payload, packet.payload, sizeof(payload)); 85 } 86 87 ///////////////////////////////////////////////////////////////////////// 88 // Implementation of RawRtcpPacket 89 ///////////////////////////////////////////////////////////////////////// 90 void RawRtcpPacket::WriteToByteBuffer(rtc::ByteBuffer *buf) const { 91 if (!buf) return; 92 93 buf->WriteUInt8(ver_to_count); 94 buf->WriteUInt8(type); 95 buf->WriteUInt16(length); 96 buf->WriteBytes(payload, sizeof(payload)); 97 } 98 99 bool RawRtcpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) { 100 if (!buf) return false; 101 102 bool ret = true; 103 ret &= buf->ReadUInt8(&ver_to_count); 104 ret &= buf->ReadUInt8(&type); 105 ret &= buf->ReadUInt16(&length); 106 ret &= buf->ReadBytes(payload, sizeof(payload)); 107 return ret; 108 } 109 110 bool RawRtcpPacket::EqualsTo(const RawRtcpPacket& packet) const { 111 return ver_to_count == packet.ver_to_count && 112 type == packet.type && 113 length == packet.length && 114 0 == memcmp(payload, packet.payload, sizeof(payload)); 115 } 116 117 ///////////////////////////////////////////////////////////////////////// 118 // Implementation of class RtpTestUtility 119 ///////////////////////////////////////////////////////////////////////// 120 const RawRtpPacket RtpTestUtility::kTestRawRtpPackets[] = { 121 {0x80, 0, 0, 0, RtpTestUtility::kDefaultSsrc, "RTP frame 0"}, 122 {0x80, 0, 1, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, 123 {0x80, 0, 2, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, 124 {0x80, 0, 3, 60, RtpTestUtility::kDefaultSsrc, "RTP frame 2"} 125 }; 126 const RawRtcpPacket RtpTestUtility::kTestRawRtcpPackets[] = { 127 // The Version is 2, the Length is 2, and the payload has 8 bytes. 128 {0x80, 0, 2, "RTCP0000"}, 129 {0x80, 0, 2, "RTCP0001"}, 130 {0x80, 0, 2, "RTCP0002"}, 131 {0x80, 0, 2, "RTCP0003"}, 132 }; 133 134 size_t RtpTestUtility::GetTestPacketCount() { 135 return std::min(arraysize(kTestRawRtpPackets), 136 arraysize(kTestRawRtcpPackets)); 137 } 138 139 bool RtpTestUtility::WriteTestPackets(size_t count, 140 bool rtcp, 141 uint32_t rtp_ssrc, 142 RtpDumpWriter* writer) { 143 if (!writer || count > GetTestPacketCount()) return false; 144 145 bool result = true; 146 uint32_t elapsed_time_ms = 0; 147 for (size_t i = 0; i < count && result; ++i) { 148 rtc::ByteBuffer buf; 149 if (rtcp) { 150 kTestRawRtcpPackets[i].WriteToByteBuffer(&buf); 151 } else { 152 kTestRawRtpPackets[i].WriteToByteBuffer(rtp_ssrc, &buf); 153 } 154 155 RtpDumpPacket dump_packet(buf.Data(), buf.Length(), elapsed_time_ms, rtcp); 156 elapsed_time_ms += kElapsedTimeInterval; 157 result &= (rtc::SR_SUCCESS == writer->WritePacket(dump_packet)); 158 } 159 return result; 160 } 161 162 bool RtpTestUtility::VerifyTestPacketsFromStream(size_t count, 163 rtc::StreamInterface* stream, 164 uint32_t ssrc) { 165 if (!stream) return false; 166 167 uint32_t prev_elapsed_time = 0; 168 bool result = true; 169 stream->Rewind(); 170 RtpDumpLoopReader reader(stream); 171 for (size_t i = 0; i < count && result; ++i) { 172 // Which loop and which index in the loop are we reading now. 173 size_t loop = i / GetTestPacketCount(); 174 size_t index = i % GetTestPacketCount(); 175 176 RtpDumpPacket packet; 177 result &= (rtc::SR_SUCCESS == reader.ReadPacket(&packet)); 178 // Check the elapsed time of the dump packet. 179 result &= (packet.elapsed_time >= prev_elapsed_time); 180 prev_elapsed_time = packet.elapsed_time; 181 182 // Check the RTP or RTCP packet. 183 rtc::ByteBuffer buf(reinterpret_cast<const char*>(&packet.data[0]), 184 packet.data.size()); 185 if (packet.is_rtcp()) { 186 // RTCP packet. 187 RawRtcpPacket rtcp_packet; 188 result &= rtcp_packet.ReadFromByteBuffer(&buf); 189 result &= rtcp_packet.EqualsTo(kTestRawRtcpPackets[index]); 190 } else { 191 // RTP packet. 192 RawRtpPacket rtp_packet; 193 result &= rtp_packet.ReadFromByteBuffer(&buf); 194 result &= rtp_packet.SameExceptSeqNumTimestampSsrc( 195 kTestRawRtpPackets[index], 196 static_cast<uint16_t>(kTestRawRtpPackets[index].sequence_number + 197 loop * GetTestPacketCount()), 198 static_cast<uint32_t>(kTestRawRtpPackets[index].timestamp + 199 loop * kRtpTimestampIncrease), 200 ssrc); 201 } 202 } 203 204 stream->Rewind(); 205 return result; 206 } 207 208 bool RtpTestUtility::VerifyPacket(const RtpDumpPacket* dump, 209 const RawRtpPacket* raw, 210 bool header_only) { 211 if (!dump || !raw) return false; 212 213 rtc::ByteBuffer buf; 214 raw->WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf); 215 216 if (header_only) { 217 size_t header_len = 0; 218 dump->GetRtpHeaderLen(&header_len); 219 return header_len == dump->data.size() && 220 buf.Length() > dump->data.size() && 221 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); 222 } else { 223 return buf.Length() == dump->data.size() && 224 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); 225 } 226 } 227 228 // Implementation of VideoCaptureListener. 229 VideoCapturerListener::VideoCapturerListener(VideoCapturer* capturer) 230 : last_capture_state_(CS_STARTING), 231 frame_count_(0), 232 frame_fourcc_(0), 233 frame_width_(0), 234 frame_height_(0), 235 frame_size_(0), 236 resolution_changed_(false) { 237 capturer->SignalStateChange.connect(this, 238 &VideoCapturerListener::OnStateChange); 239 capturer->SignalFrameCaptured.connect(this, 240 &VideoCapturerListener::OnFrameCaptured); 241 } 242 243 void VideoCapturerListener::OnStateChange(VideoCapturer* capturer, 244 CaptureState result) { 245 last_capture_state_ = result; 246 } 247 248 void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer, 249 const CapturedFrame* frame) { 250 ++frame_count_; 251 if (1 == frame_count_) { 252 frame_fourcc_ = frame->fourcc; 253 frame_width_ = frame->width; 254 frame_height_ = frame->height; 255 frame_size_ = frame->data_size; 256 } else if (frame_width_ != frame->width || frame_height_ != frame->height) { 257 resolution_changed_ = true; 258 } 259 } 260 261 // Returns the absolute path to a file in the testdata/ directory. 262 std::string GetTestFilePath(const std::string& filename) { 263 // Locate test data directory. 264 #ifdef ENABLE_WEBRTC 265 rtc::Pathname path = rtc::GetExecutablePath(); 266 EXPECT_FALSE(path.empty()); 267 path.AppendPathname("../../talk/"); 268 #else 269 rtc::Pathname path = testing::GetTalkDirectory(); 270 EXPECT_FALSE(path.empty()); // must be run from inside "talk" 271 #endif 272 path.AppendFolder("media/testdata/"); 273 path.SetFilename(filename); 274 return path.pathname(); 275 } 276 277 // Loads the image with the specified prefix and size into |out|. 278 bool LoadPlanarYuvTestImage(const std::string& prefix, 279 int width, 280 int height, 281 uint8_t* out) { 282 std::stringstream ss; 283 ss << prefix << "." << width << "x" << height << "_P420.yuv"; 284 285 rtc::scoped_ptr<rtc::FileStream> stream( 286 rtc::Filesystem::OpenFile(rtc::Pathname( 287 GetTestFilePath(ss.str())), "rb")); 288 if (!stream) { 289 return false; 290 } 291 292 rtc::StreamResult res = 293 stream->ReadAll(out, I420_SIZE(width, height), NULL, NULL); 294 return (res == rtc::SR_SUCCESS); 295 } 296 297 // Dumps the YUV image out to a file, for visual inspection. 298 // PYUV tool can be used to view dump files. 299 void DumpPlanarYuvTestImage(const std::string& prefix, 300 const uint8_t* img, 301 int w, 302 int h) { 303 rtc::FileStream fs; 304 char filename[256]; 305 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv", 306 prefix.c_str(), w, h); 307 fs.Open(filename, "wb", NULL); 308 fs.Write(img, I420_SIZE(w, h), NULL, NULL); 309 } 310 311 // Dumps the ARGB image out to a file, for visual inspection. 312 // ffplay tool can be used to view dump files. 313 void DumpPlanarArgbTestImage(const std::string& prefix, 314 const uint8_t* img, 315 int w, 316 int h) { 317 rtc::FileStream fs; 318 char filename[256]; 319 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_ARGB.raw", 320 prefix.c_str(), w, h); 321 fs.Open(filename, "wb", NULL); 322 fs.Write(img, ARGB_SIZE(w, h), NULL, NULL); 323 } 324 325 bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1) { 326 const uint8_t* y0 = frame0->GetYPlane(); 327 const uint8_t* u0 = frame0->GetUPlane(); 328 const uint8_t* v0 = frame0->GetVPlane(); 329 const uint8_t* y1 = frame1->GetYPlane(); 330 const uint8_t* u1 = frame1->GetUPlane(); 331 const uint8_t* v1 = frame1->GetVPlane(); 332 333 for (size_t i = 0; i < frame0->GetHeight(); ++i) { 334 if (0 != memcmp(y0, y1, frame0->GetWidth())) { 335 return false; 336 } 337 y0 += frame0->GetYPitch(); 338 y1 += frame1->GetYPitch(); 339 } 340 341 for (size_t i = 0; i < frame0->GetChromaHeight(); ++i) { 342 if (0 != memcmp(u0, u1, frame0->GetChromaWidth())) { 343 return false; 344 } 345 if (0 != memcmp(v0, v1, frame0->GetChromaWidth())) { 346 return false; 347 } 348 u0 += frame0->GetUPitch(); 349 v0 += frame0->GetVPitch(); 350 u1 += frame1->GetUPitch(); 351 v1 += frame1->GetVPitch(); 352 } 353 354 return true; 355 } 356 357 cricket::StreamParams CreateSimStreamParams( 358 const std::string& cname, 359 const std::vector<uint32_t>& ssrcs) { 360 cricket::StreamParams sp; 361 cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs); 362 sp.ssrcs = ssrcs; 363 sp.ssrc_groups.push_back(sg); 364 sp.cname = cname; 365 return sp; 366 } 367 368 // There should be an rtx_ssrc per ssrc. 369 cricket::StreamParams CreateSimWithRtxStreamParams( 370 const std::string& cname, 371 const std::vector<uint32_t>& ssrcs, 372 const std::vector<uint32_t>& rtx_ssrcs) { 373 cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs); 374 for (size_t i = 0; i < ssrcs.size(); ++i) { 375 sp.ssrcs.push_back(rtx_ssrcs[i]); 376 std::vector<uint32_t> fid_ssrcs; 377 fid_ssrcs.push_back(ssrcs[i]); 378 fid_ssrcs.push_back(rtx_ssrcs[i]); 379 cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs); 380 sp.ssrc_groups.push_back(fid_group); 381 } 382 return sp; 383 } 384 385 } // namespace cricket 386