Home | History | Annotate | Download | only in base
      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(&timestamp);
     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