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 32 #include "talk/media/base/executablehelpers.h" 33 #include "talk/media/base/rtpdump.h" 34 #include "talk/media/base/videocapturer.h" 35 #include "talk/media/base/videoframe.h" 36 #include "webrtc/base/bytebuffer.h" 37 #include "webrtc/base/fileutils.h" 38 #include "webrtc/base/gunit.h" 39 #include "webrtc/base/pathutils.h" 40 #include "webrtc/base/stream.h" 41 #include "webrtc/base/stringutils.h" 42 #include "webrtc/base/testutils.h" 43 44 namespace cricket { 45 46 ///////////////////////////////////////////////////////////////////////// 47 // Implementation of RawRtpPacket 48 ///////////////////////////////////////////////////////////////////////// 49 void RawRtpPacket::WriteToByteBuffer( 50 uint32 in_ssrc, rtc::ByteBuffer *buf) const { 51 if (!buf) return; 52 53 buf->WriteUInt8(ver_to_cc); 54 buf->WriteUInt8(m_to_pt); 55 buf->WriteUInt16(sequence_number); 56 buf->WriteUInt32(timestamp); 57 buf->WriteUInt32(in_ssrc); 58 buf->WriteBytes(payload, sizeof(payload)); 59 } 60 61 bool RawRtpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) { 62 if (!buf) return false; 63 64 bool ret = true; 65 ret &= buf->ReadUInt8(&ver_to_cc); 66 ret &= buf->ReadUInt8(&m_to_pt); 67 ret &= buf->ReadUInt16(&sequence_number); 68 ret &= buf->ReadUInt32(×tamp); 69 ret &= buf->ReadUInt32(&ssrc); 70 ret &= buf->ReadBytes(payload, sizeof(payload)); 71 return ret; 72 } 73 74 bool RawRtpPacket::SameExceptSeqNumTimestampSsrc( 75 const RawRtpPacket& packet, uint16 seq, uint32 ts, uint32 ssc) const { 76 return sequence_number == seq && 77 timestamp == ts && 78 ver_to_cc == packet.ver_to_cc && 79 m_to_pt == packet.m_to_pt && 80 ssrc == ssc && 81 0 == memcmp(payload, packet.payload, sizeof(payload)); 82 } 83 84 ///////////////////////////////////////////////////////////////////////// 85 // Implementation of RawRtcpPacket 86 ///////////////////////////////////////////////////////////////////////// 87 void RawRtcpPacket::WriteToByteBuffer(rtc::ByteBuffer *buf) const { 88 if (!buf) return; 89 90 buf->WriteUInt8(ver_to_count); 91 buf->WriteUInt8(type); 92 buf->WriteUInt16(length); 93 buf->WriteBytes(payload, sizeof(payload)); 94 } 95 96 bool RawRtcpPacket::ReadFromByteBuffer(rtc::ByteBuffer* buf) { 97 if (!buf) return false; 98 99 bool ret = true; 100 ret &= buf->ReadUInt8(&ver_to_count); 101 ret &= buf->ReadUInt8(&type); 102 ret &= buf->ReadUInt16(&length); 103 ret &= buf->ReadBytes(payload, sizeof(payload)); 104 return ret; 105 } 106 107 bool RawRtcpPacket::EqualsTo(const RawRtcpPacket& packet) const { 108 return ver_to_count == packet.ver_to_count && 109 type == packet.type && 110 length == packet.length && 111 0 == memcmp(payload, packet.payload, sizeof(payload)); 112 } 113 114 ///////////////////////////////////////////////////////////////////////// 115 // Implementation of class RtpTestUtility 116 ///////////////////////////////////////////////////////////////////////// 117 const RawRtpPacket RtpTestUtility::kTestRawRtpPackets[] = { 118 {0x80, 0, 0, 0, RtpTestUtility::kDefaultSsrc, "RTP frame 0"}, 119 {0x80, 0, 1, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, 120 {0x80, 0, 2, 30, RtpTestUtility::kDefaultSsrc, "RTP frame 1"}, 121 {0x80, 0, 3, 60, RtpTestUtility::kDefaultSsrc, "RTP frame 2"} 122 }; 123 const RawRtcpPacket RtpTestUtility::kTestRawRtcpPackets[] = { 124 // The Version is 2, the Length is 2, and the payload has 8 bytes. 125 {0x80, 0, 2, "RTCP0000"}, 126 {0x80, 0, 2, "RTCP0001"}, 127 {0x80, 0, 2, "RTCP0002"}, 128 {0x80, 0, 2, "RTCP0003"}, 129 }; 130 131 size_t RtpTestUtility::GetTestPacketCount() { 132 return rtc::_min( 133 ARRAY_SIZE(kTestRawRtpPackets), 134 ARRAY_SIZE(kTestRawRtcpPackets)); 135 } 136 137 bool RtpTestUtility::WriteTestPackets( 138 size_t count, bool rtcp, uint32 rtp_ssrc, RtpDumpWriter* writer) { 139 if (!writer || count > GetTestPacketCount()) return false; 140 141 bool result = true; 142 uint32 elapsed_time_ms = 0; 143 for (size_t i = 0; i < count && result; ++i) { 144 rtc::ByteBuffer buf; 145 if (rtcp) { 146 kTestRawRtcpPackets[i].WriteToByteBuffer(&buf); 147 } else { 148 kTestRawRtpPackets[i].WriteToByteBuffer(rtp_ssrc, &buf); 149 } 150 151 RtpDumpPacket dump_packet(buf.Data(), buf.Length(), elapsed_time_ms, rtcp); 152 elapsed_time_ms += kElapsedTimeInterval; 153 result &= (rtc::SR_SUCCESS == writer->WritePacket(dump_packet)); 154 } 155 return result; 156 } 157 158 bool RtpTestUtility::VerifyTestPacketsFromStream( 159 size_t count, rtc::StreamInterface* stream, uint32 ssrc) { 160 if (!stream) return false; 161 162 uint32 prev_elapsed_time = 0; 163 bool result = true; 164 stream->Rewind(); 165 RtpDumpLoopReader reader(stream); 166 for (size_t i = 0; i < count && result; ++i) { 167 // Which loop and which index in the loop are we reading now. 168 size_t loop = i / GetTestPacketCount(); 169 size_t index = i % GetTestPacketCount(); 170 171 RtpDumpPacket packet; 172 result &= (rtc::SR_SUCCESS == reader.ReadPacket(&packet)); 173 // Check the elapsed time of the dump packet. 174 result &= (packet.elapsed_time >= prev_elapsed_time); 175 prev_elapsed_time = packet.elapsed_time; 176 177 // Check the RTP or RTCP packet. 178 rtc::ByteBuffer buf(reinterpret_cast<const char*>(&packet.data[0]), 179 packet.data.size()); 180 if (packet.is_rtcp()) { 181 // RTCP packet. 182 RawRtcpPacket rtcp_packet; 183 result &= rtcp_packet.ReadFromByteBuffer(&buf); 184 result &= rtcp_packet.EqualsTo(kTestRawRtcpPackets[index]); 185 } else { 186 // RTP packet. 187 RawRtpPacket rtp_packet; 188 result &= rtp_packet.ReadFromByteBuffer(&buf); 189 result &= rtp_packet.SameExceptSeqNumTimestampSsrc( 190 kTestRawRtpPackets[index], 191 static_cast<uint16>(kTestRawRtpPackets[index].sequence_number + 192 loop * GetTestPacketCount()), 193 static_cast<uint32>(kTestRawRtpPackets[index].timestamp + 194 loop * kRtpTimestampIncrease), 195 ssrc); 196 } 197 } 198 199 stream->Rewind(); 200 return result; 201 } 202 203 bool RtpTestUtility::VerifyPacket(const RtpDumpPacket* dump, 204 const RawRtpPacket* raw, 205 bool header_only) { 206 if (!dump || !raw) return false; 207 208 rtc::ByteBuffer buf; 209 raw->WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf); 210 211 if (header_only) { 212 size_t header_len = 0; 213 dump->GetRtpHeaderLen(&header_len); 214 return header_len == dump->data.size() && 215 buf.Length() > dump->data.size() && 216 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); 217 } else { 218 return buf.Length() == dump->data.size() && 219 0 == memcmp(buf.Data(), &dump->data[0], dump->data.size()); 220 } 221 } 222 223 // Implementation of VideoCaptureListener. 224 VideoCapturerListener::VideoCapturerListener(VideoCapturer* capturer) 225 : last_capture_state_(CS_STARTING), 226 frame_count_(0), 227 frame_fourcc_(0), 228 frame_width_(0), 229 frame_height_(0), 230 frame_size_(0), 231 resolution_changed_(false) { 232 capturer->SignalStateChange.connect(this, 233 &VideoCapturerListener::OnStateChange); 234 capturer->SignalFrameCaptured.connect(this, 235 &VideoCapturerListener::OnFrameCaptured); 236 } 237 238 void VideoCapturerListener::OnStateChange(VideoCapturer* capturer, 239 CaptureState result) { 240 last_capture_state_ = result; 241 } 242 243 void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer, 244 const CapturedFrame* frame) { 245 ++frame_count_; 246 if (1 == frame_count_) { 247 frame_fourcc_ = frame->fourcc; 248 frame_width_ = frame->width; 249 frame_height_ = frame->height; 250 frame_size_ = frame->data_size; 251 } else if (frame_width_ != frame->width || frame_height_ != frame->height) { 252 resolution_changed_ = true; 253 } 254 } 255 256 // Returns the absolute path to a file in the testdata/ directory. 257 std::string GetTestFilePath(const std::string& filename) { 258 // Locate test data directory. 259 #ifdef ENABLE_WEBRTC 260 rtc::Pathname path = rtc::GetExecutablePath(); 261 EXPECT_FALSE(path.empty()); 262 path.AppendPathname("../../talk/"); 263 #else 264 rtc::Pathname path = testing::GetTalkDirectory(); 265 EXPECT_FALSE(path.empty()); // must be run from inside "talk" 266 #endif 267 path.AppendFolder("media/testdata/"); 268 path.SetFilename(filename); 269 return path.pathname(); 270 } 271 272 // Loads the image with the specified prefix and size into |out|. 273 bool LoadPlanarYuvTestImage(const std::string& prefix, 274 int width, int height, uint8* out) { 275 std::stringstream ss; 276 ss << prefix << "." << width << "x" << height << "_P420.yuv"; 277 278 rtc::scoped_ptr<rtc::FileStream> stream( 279 rtc::Filesystem::OpenFile(rtc::Pathname( 280 GetTestFilePath(ss.str())), "rb")); 281 if (!stream) { 282 return false; 283 } 284 285 rtc::StreamResult res = 286 stream->ReadAll(out, I420_SIZE(width, height), NULL, NULL); 287 return (res == rtc::SR_SUCCESS); 288 } 289 290 // Dumps the YUV image out to a file, for visual inspection. 291 // PYUV tool can be used to view dump files. 292 void DumpPlanarYuvTestImage(const std::string& prefix, const uint8* img, 293 int w, int h) { 294 rtc::FileStream fs; 295 char filename[256]; 296 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv", 297 prefix.c_str(), w, h); 298 fs.Open(filename, "wb", NULL); 299 fs.Write(img, I420_SIZE(w, h), NULL, NULL); 300 } 301 302 // Dumps the ARGB image out to a file, for visual inspection. 303 // ffplay tool can be used to view dump files. 304 void DumpPlanarArgbTestImage(const std::string& prefix, const uint8* img, 305 int w, int h) { 306 rtc::FileStream fs; 307 char filename[256]; 308 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_ARGB.raw", 309 prefix.c_str(), w, h); 310 fs.Open(filename, "wb", NULL); 311 fs.Write(img, ARGB_SIZE(w, h), NULL, NULL); 312 } 313 314 bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1) { 315 const uint8* y0 = frame0->GetYPlane(); 316 const uint8* u0 = frame0->GetUPlane(); 317 const uint8* v0 = frame0->GetVPlane(); 318 const uint8* y1 = frame1->GetYPlane(); 319 const uint8* u1 = frame1->GetUPlane(); 320 const uint8* v1 = frame1->GetVPlane(); 321 322 for (size_t i = 0; i < frame0->GetHeight(); ++i) { 323 if (0 != memcmp(y0, y1, frame0->GetWidth())) { 324 return false; 325 } 326 y0 += frame0->GetYPitch(); 327 y1 += frame1->GetYPitch(); 328 } 329 330 for (size_t i = 0; i < frame0->GetChromaHeight(); ++i) { 331 if (0 != memcmp(u0, u1, frame0->GetChromaWidth())) { 332 return false; 333 } 334 if (0 != memcmp(v0, v1, frame0->GetChromaWidth())) { 335 return false; 336 } 337 u0 += frame0->GetUPitch(); 338 v0 += frame0->GetVPitch(); 339 u1 += frame1->GetUPitch(); 340 v1 += frame1->GetVPitch(); 341 } 342 343 return true; 344 } 345 346 cricket::StreamParams CreateSimStreamParams( 347 const std::string& cname, const std::vector<uint32>& ssrcs) { 348 cricket::StreamParams sp; 349 cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs); 350 sp.ssrcs = ssrcs; 351 sp.ssrc_groups.push_back(sg); 352 sp.cname = cname; 353 return sp; 354 } 355 356 // There should be an rtx_ssrc per ssrc. 357 cricket::StreamParams CreateSimWithRtxStreamParams( 358 const std::string& cname, const std::vector<uint32>& ssrcs, 359 const std::vector<uint32>& rtx_ssrcs) { 360 cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs); 361 for (size_t i = 0; i < ssrcs.size(); ++i) { 362 sp.ssrcs.push_back(rtx_ssrcs[i]); 363 std::vector<uint32> fid_ssrcs; 364 fid_ssrcs.push_back(ssrcs[i]); 365 fid_ssrcs.push_back(rtx_ssrcs[i]); 366 cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs); 367 sp.ssrc_groups.push_back(fid_group); 368 } 369 return sp; 370 } 371 372 } // namespace cricket 373