1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * 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. 9 */ 10 11 #include <stdio.h> 12 13 #include <map> 14 #include <sstream> 15 16 #include "gflags/gflags.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 #include "webrtc/call.h" 20 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 21 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" 22 #include "webrtc/system_wrappers/interface/clock.h" 23 #include "webrtc/system_wrappers/interface/scoped_ptr.h" 24 #include "webrtc/system_wrappers/interface/sleep.h" 25 #include "webrtc/test/encoder_settings.h" 26 #include "webrtc/test/null_transport.h" 27 #include "webrtc/test/rtp_file_reader.h" 28 #include "webrtc/test/run_loop.h" 29 #include "webrtc/test/run_test.h" 30 #include "webrtc/test/video_capturer.h" 31 #include "webrtc/test/video_renderer.h" 32 #include "webrtc/typedefs.h" 33 34 namespace webrtc { 35 namespace flags { 36 37 // TODO(pbos): Multiple receivers. 38 39 // Flag for payload type. 40 static bool ValidatePayloadType(const char* flagname, int32_t payload_type) { 41 return payload_type > 0 && payload_type <= 127; 42 } 43 DEFINE_int32(payload_type, 0, "Payload type"); 44 static int PayloadType() { return static_cast<int>(FLAGS_payload_type); } 45 static const bool payload_dummy = 46 google::RegisterFlagValidator(&FLAGS_payload_type, &ValidatePayloadType); 47 48 // Flag for SSRC. 49 static bool ValidateSsrc(const char* flagname, uint64_t ssrc) { 50 return ssrc > 0 && ssrc <= 0xFFFFFFFFu; 51 } 52 53 DEFINE_uint64(ssrc, 0, "Incoming SSRC"); 54 static uint32_t Ssrc() { return static_cast<uint32_t>(FLAGS_ssrc); } 55 static const bool ssrc_dummy = 56 google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrc); 57 58 static bool ValidateOptionalPayloadType(const char* flagname, 59 int32_t payload_type) { 60 return payload_type == -1 || ValidatePayloadType(flagname, payload_type); 61 } 62 63 // Flag for RED payload type. 64 DEFINE_int32(red_payload_type, -1, "RED payload type"); 65 static int RedPayloadType() { 66 return static_cast<int>(FLAGS_red_payload_type); 67 } 68 static const bool red_dummy = 69 google::RegisterFlagValidator(&FLAGS_red_payload_type, 70 &ValidateOptionalPayloadType); 71 72 // Flag for ULPFEC payload type. 73 DEFINE_int32(fec_payload_type, -1, "ULPFEC payload type"); 74 static int FecPayloadType() { 75 return static_cast<int>(FLAGS_fec_payload_type); 76 } 77 static const bool fec_dummy = 78 google::RegisterFlagValidator(&FLAGS_fec_payload_type, 79 &ValidateOptionalPayloadType); 80 81 // Flag for abs-send-time id. 82 static bool ValidateRtpHeaderExtensionId(const char* flagname, 83 int32_t extension_id) { 84 return extension_id >= -1 || extension_id < 15; 85 } 86 DEFINE_int32(abs_send_time_id, -1, "RTP extension ID for abs-send-time"); 87 static int AbsSendTimeId() { return static_cast<int>(FLAGS_abs_send_time_id); } 88 static const bool abs_send_time_dummy = 89 google::RegisterFlagValidator(&FLAGS_abs_send_time_id, 90 &ValidateRtpHeaderExtensionId); 91 92 // Flag for transmission-offset id. 93 DEFINE_int32(transmission_offset_id, 94 -1, 95 "RTP extension ID for transmission-offset"); 96 static int TransmissionOffsetId() { 97 return static_cast<int>(FLAGS_transmission_offset_id); 98 } 99 static const bool timestamp_offset_dummy = 100 google::RegisterFlagValidator(&FLAGS_transmission_offset_id, 101 &ValidateRtpHeaderExtensionId); 102 103 // Flag for rtpdump input file. 104 bool ValidateInputFilenameNotEmpty(const char* flagname, 105 const std::string& string) { 106 return string != ""; 107 } 108 DEFINE_string(input_file, "", "input file"); 109 static std::string InputFile() { 110 return static_cast<std::string>(FLAGS_input_file); 111 } 112 static const bool input_file_dummy = 113 google::RegisterFlagValidator(&FLAGS_input_file, 114 &ValidateInputFilenameNotEmpty); 115 116 // Flag for raw output files. 117 DEFINE_string(out_base, "", "Basename (excluding .yuv) for raw output"); 118 static std::string OutBase() { 119 return static_cast<std::string>(FLAGS_out_base); 120 } 121 122 // Flag for video codec. 123 DEFINE_string(codec, "VP8", "Video codec"); 124 static std::string Codec() { return static_cast<std::string>(FLAGS_codec); } 125 126 } // namespace flags 127 128 static const uint32_t kReceiverLocalSsrc = 0x123456; 129 130 class FileRenderPassthrough : public VideoRenderer { 131 public: 132 FileRenderPassthrough(const std::string& basename, VideoRenderer* renderer) 133 : basename_(basename), 134 renderer_(renderer), 135 file_(NULL), 136 count_(0), 137 last_width_(0), 138 last_height_(0) {} 139 140 ~FileRenderPassthrough() { 141 if (file_ != NULL) 142 fclose(file_); 143 } 144 145 private: 146 virtual void RenderFrame(const I420VideoFrame& video_frame, 147 int time_to_render_ms) OVERRIDE { 148 if (renderer_ != NULL) 149 renderer_->RenderFrame(video_frame, time_to_render_ms); 150 if (basename_ == "") 151 return; 152 if (last_width_ != video_frame.width() || 153 last_height_ != video_frame.height()) { 154 if (file_ != NULL) 155 fclose(file_); 156 std::stringstream filename; 157 filename << basename_; 158 if (++count_ > 1) 159 filename << '-' << count_; 160 filename << '_' << video_frame.width() << 'x' << video_frame.height() 161 << ".yuv"; 162 file_ = fopen(filename.str().c_str(), "wb"); 163 if (file_ == NULL) { 164 fprintf(stderr, 165 "Couldn't open file for writing: %s\n", 166 filename.str().c_str()); 167 } 168 } 169 last_width_ = video_frame.width(); 170 last_height_ = video_frame.height(); 171 if (file_ == NULL) 172 return; 173 PrintI420VideoFrame(video_frame, file_); 174 } 175 176 const std::string basename_; 177 VideoRenderer* const renderer_; 178 FILE* file_; 179 size_t count_; 180 int last_width_; 181 int last_height_; 182 }; 183 184 void RtpReplay() { 185 scoped_ptr<test::VideoRenderer> playback_video(test::VideoRenderer::Create( 186 "Playback Video", 640, 480)); 187 FileRenderPassthrough file_passthrough(flags::OutBase(), 188 playback_video.get()); 189 190 // TODO(pbos): Might be good to have a transport that prints keyframe requests 191 // etc. 192 test::NullTransport transport; 193 Call::Config call_config(&transport); 194 scoped_ptr<Call> call(Call::Create(call_config)); 195 196 VideoReceiveStream::Config receive_config; 197 receive_config.rtp.remote_ssrc = flags::Ssrc(); 198 receive_config.rtp.local_ssrc = kReceiverLocalSsrc; 199 receive_config.rtp.fec.ulpfec_payload_type = flags::FecPayloadType(); 200 receive_config.rtp.fec.red_payload_type = flags::RedPayloadType(); 201 receive_config.rtp.nack.rtp_history_ms = 1000; 202 if (flags::TransmissionOffsetId() != -1) { 203 receive_config.rtp.extensions.push_back( 204 RtpExtension(RtpExtension::kTOffset, flags::TransmissionOffsetId())); 205 } 206 if (flags::AbsSendTimeId() != -1) { 207 receive_config.rtp.extensions.push_back( 208 RtpExtension(RtpExtension::kAbsSendTime, flags::AbsSendTimeId())); 209 } 210 receive_config.renderer = &file_passthrough; 211 212 VideoSendStream::Config::EncoderSettings encoder_settings; 213 encoder_settings.payload_name = flags::Codec(); 214 encoder_settings.payload_type = flags::PayloadType(); 215 VideoCodec codec = test::CreateDecoderVideoCodec(encoder_settings); 216 receive_config.codecs.push_back(codec); 217 218 VideoReceiveStream* receive_stream = 219 call->CreateVideoReceiveStream(receive_config); 220 221 scoped_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create( 222 test::RtpFileReader::kRtpDump, flags::InputFile())); 223 if (rtp_reader.get() == NULL) { 224 rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap, 225 flags::InputFile())); 226 if (rtp_reader.get() == NULL) { 227 fprintf(stderr, 228 "Couldn't open input file as either a rtpdump or .pcap. Note " 229 "that .pcapng is not supported.\n"); 230 return; 231 } 232 } 233 receive_stream->Start(); 234 235 uint32_t last_time_ms = 0; 236 int num_packets = 0; 237 std::map<uint32_t, int> unknown_packets; 238 while (true) { 239 test::RtpFileReader::Packet packet; 240 if (!rtp_reader->NextPacket(&packet)) 241 break; 242 ++num_packets; 243 switch (call->Receiver()->DeliverPacket(packet.data, packet.length)) { 244 case PacketReceiver::DELIVERY_OK: 245 break; 246 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: { 247 RTPHeader header; 248 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); 249 parser->Parse(packet.data, packet.length, &header); 250 if (unknown_packets[header.ssrc] == 0) 251 fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc); 252 ++unknown_packets[header.ssrc]; 253 break; 254 } 255 case PacketReceiver::DELIVERY_PACKET_ERROR: 256 fprintf(stderr, "Packet error, corrupt packets or incorrect setup?\n"); 257 break; 258 } 259 if (last_time_ms != 0 && last_time_ms != packet.time_ms) { 260 SleepMs(packet.time_ms - last_time_ms); 261 } 262 last_time_ms = packet.time_ms; 263 } 264 fprintf(stderr, "num_packets: %d\n", num_packets); 265 266 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin(); 267 it != unknown_packets.end(); 268 ++it) { 269 fprintf( 270 stderr, "Packets for unknown ssrc '%u': %d\n", it->first, it->second); 271 } 272 273 call->DestroyVideoReceiveStream(receive_stream); 274 } 275 } // namespace webrtc 276 277 int main(int argc, char* argv[]) { 278 ::testing::InitGoogleTest(&argc, argv); 279 google::ParseCommandLineFlags(&argc, &argv, true); 280 281 webrtc::test::RunTest(webrtc::RtpReplay); 282 return 0; 283 } 284