| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <climits> | 6 #include <climits> |
| 7 #include <cstdarg> | 7 #include <cstdarg> |
| 8 #include <cstdio> | 8 #include <cstdio> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| 11 #include "base/at_exit.h" | 11 #include "base/at_exit.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/ref_counted.h" | 14 #include "base/memory/ref_counted.h" |
| 15 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
| 16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 17 #include "base/threading/thread.h" | 17 #include "base/threading/thread.h" |
| 18 #include "base/time/default_tick_clock.h" | 18 #include "base/time/default_tick_clock.h" |
| 19 #include "media/base/video_frame.h" | 19 #include "media/base/video_frame.h" |
| 20 #include "media/cast/cast_config.h" | 20 #include "media/cast/cast_config.h" |
| 21 #include "media/cast/cast_environment.h" | 21 #include "media/cast/cast_environment.h" |
| 22 #include "media/cast/cast_receiver.h" | 22 #include "media/cast/cast_receiver.h" |
| 23 #include "media/cast/logging/logging_defines.h" | 23 #include "media/cast/logging/logging_defines.h" |
| 24 #include "media/cast/test/utility/default_config.h" |
| 25 #include "media/cast/test/utility/in_process_receiver.h" |
| 24 #include "media/cast/test/utility/input_builder.h" | 26 #include "media/cast/test/utility/input_builder.h" |
| 27 #include "media/cast/test/utility/standalone_cast_environment.h" |
| 25 #include "media/cast/transport/transport/udp_transport.h" | 28 #include "media/cast/transport/transport/udp_transport.h" |
| 26 #include "net/base/net_util.h" | 29 #include "net/base/net_util.h" |
| 27 | 30 |
| 28 #if defined(OS_LINUX) | 31 #if defined(OS_LINUX) |
| 29 #include "media/cast/test/linux_output_window.h" | 32 #include "media/cast/test/linux_output_window.h" |
| 30 #endif // OS_LINUX | 33 #endif // OS_LINUX |
| 31 | 34 |
| 32 namespace media { | 35 namespace media { |
| 33 namespace cast { | 36 namespace cast { |
| 37 |
| 34 // Settings chosen to match default sender settings. | 38 // Settings chosen to match default sender settings. |
| 35 #define DEFAULT_SEND_PORT "0" | 39 #define DEFAULT_SEND_PORT "0" |
| 36 #define DEFAULT_RECEIVE_PORT "2344" | 40 #define DEFAULT_RECEIVE_PORT "2344" |
| 37 #define DEFAULT_SEND_IP "0.0.0.0" | 41 #define DEFAULT_SEND_IP "0.0.0.0" |
| 38 #define DEFAULT_RESTART "0" | 42 #define DEFAULT_RESTART "0" |
| 39 #define DEFAULT_AUDIO_FEEDBACK_SSRC "1" | 43 #define DEFAULT_AUDIO_FEEDBACK_SSRC "1" |
| 40 #define DEFAULT_AUDIO_INCOMING_SSRC "2" | 44 #define DEFAULT_AUDIO_INCOMING_SSRC "2" |
| 41 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" | 45 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" |
| 42 #define DEFAULT_VIDEO_FEEDBACK_SSRC "12" | 46 #define DEFAULT_VIDEO_FEEDBACK_SSRC "12" |
| 43 #define DEFAULT_VIDEO_INCOMING_SSRC "11" | 47 #define DEFAULT_VIDEO_INCOMING_SSRC "11" |
| 44 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" | 48 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" |
| 45 #define DEFAULT_VIDEO_CODEC_WIDTH "640" | 49 #define DEFAULT_VIDEO_CODEC_WIDTH "640" |
| 46 #define DEFAULT_VIDEO_CODEC_HEIGHT "480" | 50 #define DEFAULT_VIDEO_CODEC_HEIGHT "480" |
| 47 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" | 51 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" |
| 48 | 52 |
| 49 static const int kAudioSamplingFrequency = 48000; | |
| 50 #if defined(OS_LINUX) | 53 #if defined(OS_LINUX) |
| 51 const int kVideoWindowWidth = 1280; | 54 const int kVideoWindowWidth = 1280; |
| 52 const int kVideoWindowHeight = 720; | 55 const int kVideoWindowHeight = 720; |
| 53 #endif // OS_LINUX | 56 #endif // OS_LINUX |
| 54 static const int kFrameTimerMs = 33; | |
| 55 | 57 |
| 56 void GetPorts(int* tx_port, int* rx_port) { | 58 void GetPorts(int* tx_port, int* rx_port) { |
| 57 test::InputBuilder tx_input( | 59 test::InputBuilder tx_input( |
| 58 "Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); | 60 "Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); |
| 59 *tx_port = tx_input.GetIntInput(); | 61 *tx_port = tx_input.GetIntInput(); |
| 60 | 62 |
| 61 test::InputBuilder rx_input( | 63 test::InputBuilder rx_input( |
| 62 "Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); | 64 "Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); |
| 63 *rx_port = rx_input.GetIntInput(); | 65 *rx_port = rx_input.GetIntInput(); |
| 64 } | 66 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 | 98 |
| 97 void GetPayloadtype(AudioReceiverConfig* audio_config) { | 99 void GetPayloadtype(AudioReceiverConfig* audio_config) { |
| 98 test::InputBuilder input("Choose audio receiver payload type.", | 100 test::InputBuilder input("Choose audio receiver payload type.", |
| 99 DEFAULT_AUDIO_PAYLOAD_TYPE, | 101 DEFAULT_AUDIO_PAYLOAD_TYPE, |
| 100 96, | 102 96, |
| 101 127); | 103 127); |
| 102 audio_config->rtp_payload_type = input.GetIntInput(); | 104 audio_config->rtp_payload_type = input.GetIntInput(); |
| 103 } | 105 } |
| 104 | 106 |
| 105 AudioReceiverConfig GetAudioReceiverConfig() { | 107 AudioReceiverConfig GetAudioReceiverConfig() { |
| 106 AudioReceiverConfig audio_config; | 108 AudioReceiverConfig audio_config = GetDefaultAudioReceiverConfig(); |
| 107 | |
| 108 GetSsrcs(&audio_config); | 109 GetSsrcs(&audio_config); |
| 109 GetPayloadtype(&audio_config); | 110 GetPayloadtype(&audio_config); |
| 110 | |
| 111 audio_config.rtcp_c_name = "audio_receiver@a.b.c.d"; | |
| 112 | |
| 113 VLOG(1) << "Using OPUS 48Khz stereo"; | |
| 114 audio_config.use_external_decoder = false; | |
| 115 audio_config.frequency = 48000; | |
| 116 audio_config.channels = 2; | |
| 117 audio_config.codec = transport::kOpus; | |
| 118 return audio_config; | 111 return audio_config; |
| 119 } | 112 } |
| 120 | 113 |
| 121 void GetPayloadtype(VideoReceiverConfig* video_config) { | 114 void GetPayloadtype(VideoReceiverConfig* video_config) { |
| 122 test::InputBuilder input("Choose video receiver payload type.", | 115 test::InputBuilder input("Choose video receiver payload type.", |
| 123 DEFAULT_VIDEO_PAYLOAD_TYPE, | 116 DEFAULT_VIDEO_PAYLOAD_TYPE, |
| 124 96, | 117 96, |
| 125 127); | 118 127); |
| 126 video_config->rtp_payload_type = input.GetIntInput(); | 119 video_config->rtp_payload_type = input.GetIntInput(); |
| 127 } | 120 } |
| 128 | 121 |
| 129 VideoReceiverConfig GetVideoReceiverConfig() { | 122 VideoReceiverConfig GetVideoReceiverConfig() { |
| 130 VideoReceiverConfig video_config; | 123 VideoReceiverConfig video_config = GetDefaultVideoReceiverConfig(); |
| 131 | |
| 132 GetSsrcs(&video_config); | 124 GetSsrcs(&video_config); |
| 133 GetPayloadtype(&video_config); | 125 GetPayloadtype(&video_config); |
| 134 | |
| 135 video_config.rtcp_c_name = "video_receiver@a.b.c.d"; | |
| 136 | |
| 137 video_config.use_external_decoder = false; | |
| 138 | |
| 139 VLOG(1) << "Using VP8"; | |
| 140 video_config.codec = transport::kVp8; | |
| 141 return video_config; | 126 return video_config; |
| 142 } | 127 } |
| 143 | 128 |
| 144 static void UpdateCastTransportStatus(transport::CastTransportStatus status) { | 129 // An InProcessReceiver that renders video frames to a LinuxOutputWindow. While |
| 145 VLOG(1) << "CastTransportStatus = " << status; | 130 // it does receive audio frames, it does not play them. |
| 146 } | 131 class ReceiverDisplay : public InProcessReceiver { |
| 147 | |
| 148 class ReceiveProcess : public base::RefCountedThreadSafe<ReceiveProcess> { | |
| 149 public: | 132 public: |
| 150 explicit ReceiveProcess(scoped_refptr<FrameReceiver> frame_receiver) | 133 ReceiverDisplay(const scoped_refptr<CastEnvironment>& cast_environment, |
| 151 : frame_receiver_(frame_receiver), | 134 const net::IPEndPoint& local_end_point, |
| 135 const net::IPEndPoint& remote_end_point, |
| 136 const AudioReceiverConfig& audio_config, |
| 137 const VideoReceiverConfig& video_config) |
| 138 : InProcessReceiver(cast_environment, |
| 139 local_end_point, |
| 140 remote_end_point, |
| 141 audio_config, |
| 142 video_config), |
| 152 #if defined(OS_LINUX) | 143 #if defined(OS_LINUX) |
| 153 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), | 144 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), |
| 154 #endif // OS_LINUX | 145 #endif // OS_LINUX |
| 155 last_playout_time_(), | 146 last_playout_time_(), |
| 156 last_render_time_() { | 147 last_render_time_() { |
| 157 } | 148 } |
| 158 | 149 |
| 159 void Start() { | 150 virtual ~ReceiverDisplay() {} |
| 160 GetAudioFrame(base::TimeDelta::FromMilliseconds(kFrameTimerMs)); | |
| 161 GetVideoFrame(); | |
| 162 } | |
| 163 | 151 |
| 164 protected: | 152 protected: |
| 165 virtual ~ReceiveProcess() {} | 153 virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame, |
| 166 | 154 const base::TimeTicks& render_time) OVERRIDE { |
| 167 private: | |
| 168 friend class base::RefCountedThreadSafe<ReceiveProcess>; | |
| 169 | |
| 170 void DisplayFrame(const scoped_refptr<media::VideoFrame>& video_frame, | |
| 171 const base::TimeTicks& render_time) { | |
| 172 #ifdef OS_LINUX | 155 #ifdef OS_LINUX |
| 173 render_.RenderFrame(video_frame); | 156 render_.RenderFrame(video_frame); |
| 174 #endif // OS_LINUX | 157 #endif // OS_LINUX |
| 175 // Print out the delta between frames. | 158 // Print out the delta between frames. |
| 176 if (!last_render_time_.is_null()) { | 159 if (!last_render_time_.is_null()) { |
| 177 base::TimeDelta time_diff = render_time - last_render_time_; | 160 base::TimeDelta time_diff = render_time - last_render_time_; |
| 178 VLOG(1) << " RenderDelay[mS] = " << time_diff.InMilliseconds(); | 161 VLOG(1) << "Size = " << video_frame->coded_size().ToString() |
| 162 << "; RenderDelay[mS] = " << time_diff.InMilliseconds(); |
| 179 } | 163 } |
| 180 last_render_time_ = render_time; | 164 last_render_time_ = render_time; |
| 181 GetVideoFrame(); | |
| 182 } | 165 } |
| 183 | 166 |
| 184 void ReceiveAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame, | 167 virtual void OnAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame, |
| 185 const base::TimeTicks& playout_time) { | 168 const base::TimeTicks& playout_time) OVERRIDE { |
| 186 // For audio just print the playout delta between audio frames. | 169 // For audio just print the playout delta between audio frames. |
| 187 // Default diff time is kFrameTimerMs. | |
| 188 base::TimeDelta time_diff = | |
| 189 base::TimeDelta::FromMilliseconds(kFrameTimerMs); | |
| 190 if (!last_playout_time_.is_null()) { | 170 if (!last_playout_time_.is_null()) { |
| 191 time_diff = playout_time - last_playout_time_; | 171 base::TimeDelta time_diff = playout_time - last_playout_time_; |
| 192 VLOG(1) << " ***PlayoutDelay[mS] = " << time_diff.InMilliseconds(); | 172 VLOG(1) << "SampleRate = " << audio_frame->frequency |
| 173 << "; PlayoutDelay[mS] = " << time_diff.InMilliseconds(); |
| 193 } | 174 } |
| 194 last_playout_time_ = playout_time; | 175 last_playout_time_ = playout_time; |
| 195 } | 176 } |
| 196 | 177 |
| 197 void GetAudioFrame(base::TimeDelta playout_diff) { | |
| 198 int num_10ms_blocks = playout_diff.InMilliseconds() / 10; | |
| 199 frame_receiver_->GetRawAudioFrame( | |
| 200 num_10ms_blocks, | |
| 201 kAudioSamplingFrequency, | |
| 202 base::Bind(&ReceiveProcess::ReceiveAudioFrame, this)); | |
| 203 base::MessageLoop::current()->PostDelayedTask( | |
| 204 FROM_HERE, | |
| 205 base::Bind(&ReceiveProcess::GetAudioFrame, this, playout_diff), | |
| 206 playout_diff); | |
| 207 } | |
| 208 | |
| 209 void GetVideoFrame() { | |
| 210 frame_receiver_->GetRawVideoFrame( | |
| 211 base::Bind(&ReceiveProcess::DisplayFrame, this)); | |
| 212 } | |
| 213 | |
| 214 scoped_refptr<FrameReceiver> frame_receiver_; | |
| 215 #ifdef OS_LINUX | 178 #ifdef OS_LINUX |
| 216 test::LinuxOutputWindow render_; | 179 test::LinuxOutputWindow render_; |
| 217 #endif // OS_LINUX | 180 #endif // OS_LINUX |
| 218 base::TimeTicks last_playout_time_; | 181 base::TimeTicks last_playout_time_; |
| 219 base::TimeTicks last_render_time_; | 182 base::TimeTicks last_render_time_; |
| 220 }; | 183 }; |
| 221 | 184 |
| 222 } // namespace cast | 185 } // namespace cast |
| 223 } // namespace media | 186 } // namespace media |
| 224 | 187 |
| 225 int main(int argc, char** argv) { | 188 int main(int argc, char** argv) { |
| 226 base::AtExitManager at_exit; | 189 base::AtExitManager at_exit; |
| 227 base::MessageLoopForIO main_message_loop; | |
| 228 CommandLine::Init(argc, argv); | 190 CommandLine::Init(argc, argv); |
| 229 InitLogging(logging::LoggingSettings()); | 191 InitLogging(logging::LoggingSettings()); |
| 230 | 192 |
| 231 VLOG(1) << "Cast Receiver"; | 193 // Enable raw event logging only. |
| 232 base::Thread audio_thread("Cast audio decoder thread"); | |
| 233 base::Thread video_thread("Cast video decoder thread"); | |
| 234 audio_thread.Start(); | |
| 235 video_thread.Start(); | |
| 236 | |
| 237 scoped_ptr<base::TickClock> clock(new base::DefaultTickClock()); | |
| 238 | |
| 239 // Enable main and receiver side threads only. Enable raw event logging. | |
| 240 // Running transport on the main thread. | |
| 241 media::cast::CastLoggingConfig logging_config; | 194 media::cast::CastLoggingConfig logging_config; |
| 242 logging_config.enable_raw_data_collection = true; | 195 logging_config.enable_raw_data_collection = true; |
| 243 | 196 |
| 244 scoped_refptr<media::cast::CastEnvironment> cast_environment( | 197 scoped_refptr<media::cast::CastEnvironment> cast_environment( |
| 245 new media::cast::CastEnvironment(clock.Pass(), | 198 new media::cast::StandaloneCastEnvironment(logging_config)); |
| 246 main_message_loop.message_loop_proxy(), | |
| 247 NULL, | |
| 248 audio_thread.message_loop_proxy(), | |
| 249 NULL, | |
| 250 video_thread.message_loop_proxy(), | |
| 251 main_message_loop.message_loop_proxy(), | |
| 252 logging_config)); | |
| 253 | 199 |
| 254 media::cast::AudioReceiverConfig audio_config = | 200 media::cast::AudioReceiverConfig audio_config = |
| 255 media::cast::GetAudioReceiverConfig(); | 201 media::cast::GetAudioReceiverConfig(); |
| 256 media::cast::VideoReceiverConfig video_config = | 202 media::cast::VideoReceiverConfig video_config = |
| 257 media::cast::GetVideoReceiverConfig(); | 203 media::cast::GetVideoReceiverConfig(); |
| 258 | 204 |
| 259 int remote_port, local_port; | 205 int remote_port, local_port; |
| 260 media::cast::GetPorts(&remote_port, &local_port); | 206 media::cast::GetPorts(&remote_port, &local_port); |
| 261 if (!local_port) { | 207 if (!local_port) { |
| 262 LOG(ERROR) << "Invalid local port."; | 208 LOG(ERROR) << "Invalid local port."; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 274 } | 220 } |
| 275 | 221 |
| 276 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { | 222 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { |
| 277 LOG(ERROR) << "Invalid local IP address."; | 223 LOG(ERROR) << "Invalid local IP address."; |
| 278 return 1; | 224 return 1; |
| 279 } | 225 } |
| 280 | 226 |
| 281 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); | 227 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); |
| 282 net::IPEndPoint local_end_point(local_ip_number, local_port); | 228 net::IPEndPoint local_end_point(local_ip_number, local_port); |
| 283 | 229 |
| 284 scoped_ptr<media::cast::transport::UdpTransport> transport( | 230 media::cast::ReceiverDisplay* const receiver_display = |
| 285 new media::cast::transport::UdpTransport( | 231 new media::cast::ReceiverDisplay(cast_environment, |
| 286 NULL, | 232 local_end_point, |
| 287 main_message_loop.message_loop_proxy(), | 233 remote_end_point, |
| 288 local_end_point, | 234 audio_config, |
| 289 remote_end_point, | 235 video_config); |
| 290 base::Bind(&media::cast::UpdateCastTransportStatus))); | 236 receiver_display->Start(); |
| 291 scoped_ptr<media::cast::CastReceiver> cast_receiver( | |
| 292 media::cast::CastReceiver::CreateCastReceiver( | |
| 293 cast_environment, audio_config, video_config, transport.get())); | |
| 294 | 237 |
| 295 // TODO(hubbe): Make the cast receiver do this automatically. | 238 base::MessageLoop().Run(); // Run forever (i.e., until SIGTERM). |
| 296 transport->StartReceiving(cast_receiver->packet_receiver()); | 239 NOTREACHED(); |
| 297 | |
| 298 scoped_refptr<media::cast::ReceiveProcess> receive_process( | |
| 299 new media::cast::ReceiveProcess(cast_receiver->frame_receiver())); | |
| 300 receive_process->Start(); | |
| 301 main_message_loop.Run(); | |
| 302 return 0; | 240 return 0; |
| 303 } | 241 } |
| OLD | NEW |