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/in_process_receiver.h" |
24 #include "media/cast/test/utility/input_builder.h" | 25 #include "media/cast/test/utility/input_builder.h" |
25 #include "media/cast/transport/transport/udp_transport.h" | 26 #include "media/cast/transport/transport/udp_transport.h" |
26 #include "net/base/net_util.h" | 27 #include "net/base/net_util.h" |
27 | 28 |
28 #if defined(OS_LINUX) | 29 #if defined(OS_LINUX) |
29 #include "media/cast/test/linux_output_window.h" | 30 #include "media/cast/test/linux_output_window.h" |
30 #endif // OS_LINUX | 31 #endif // OS_LINUX |
31 | 32 |
32 namespace media { | 33 namespace media { |
33 namespace cast { | 34 namespace cast { |
| 35 |
34 // Settings chosen to match default sender settings. | 36 // Settings chosen to match default sender settings. |
35 #define DEFAULT_SEND_PORT "0" | 37 #define DEFAULT_SEND_PORT "0" |
36 #define DEFAULT_RECEIVE_PORT "2344" | 38 #define DEFAULT_RECEIVE_PORT "2344" |
37 #define DEFAULT_SEND_IP "0.0.0.0" | 39 #define DEFAULT_SEND_IP "0.0.0.0" |
38 #define DEFAULT_RESTART "0" | 40 #define DEFAULT_RESTART "0" |
39 #define DEFAULT_AUDIO_FEEDBACK_SSRC "1" | 41 #define DEFAULT_AUDIO_FEEDBACK_SSRC "1" |
40 #define DEFAULT_AUDIO_INCOMING_SSRC "2" | 42 #define DEFAULT_AUDIO_INCOMING_SSRC "2" |
41 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" | 43 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" |
42 #define DEFAULT_VIDEO_FEEDBACK_SSRC "12" | 44 #define DEFAULT_VIDEO_FEEDBACK_SSRC "12" |
43 #define DEFAULT_VIDEO_INCOMING_SSRC "11" | 45 #define DEFAULT_VIDEO_INCOMING_SSRC "11" |
44 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" | 46 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" |
45 #define DEFAULT_VIDEO_CODEC_WIDTH "640" | 47 #define DEFAULT_VIDEO_CODEC_WIDTH "640" |
46 #define DEFAULT_VIDEO_CODEC_HEIGHT "480" | 48 #define DEFAULT_VIDEO_CODEC_HEIGHT "480" |
47 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" | 49 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" |
48 | 50 |
49 static const int kAudioSamplingFrequency = 48000; | |
50 #if defined(OS_LINUX) | 51 #if defined(OS_LINUX) |
51 const int kVideoWindowWidth = 1280; | 52 const int kVideoWindowWidth = 1280; |
52 const int kVideoWindowHeight = 720; | 53 const int kVideoWindowHeight = 720; |
53 #endif // OS_LINUX | 54 #endif // OS_LINUX |
54 static const int kFrameTimerMs = 33; | |
55 | 55 |
56 void GetPorts(int* tx_port, int* rx_port) { | 56 void GetPorts(int* tx_port, int* rx_port) { |
57 test::InputBuilder tx_input( | 57 test::InputBuilder tx_input( |
58 "Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); | 58 "Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); |
59 *tx_port = tx_input.GetIntInput(); | 59 *tx_port = tx_input.GetIntInput(); |
60 | 60 |
61 test::InputBuilder rx_input( | 61 test::InputBuilder rx_input( |
62 "Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); | 62 "Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); |
63 *rx_port = rx_input.GetIntInput(); | 63 *rx_port = rx_input.GetIntInput(); |
64 } | 64 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 | 96 |
97 void GetPayloadtype(AudioReceiverConfig* audio_config) { | 97 void GetPayloadtype(AudioReceiverConfig* audio_config) { |
98 test::InputBuilder input("Choose audio receiver payload type.", | 98 test::InputBuilder input("Choose audio receiver payload type.", |
99 DEFAULT_AUDIO_PAYLOAD_TYPE, | 99 DEFAULT_AUDIO_PAYLOAD_TYPE, |
100 96, | 100 96, |
101 127); | 101 127); |
102 audio_config->rtp_payload_type = input.GetIntInput(); | 102 audio_config->rtp_payload_type = input.GetIntInput(); |
103 } | 103 } |
104 | 104 |
105 AudioReceiverConfig GetAudioReceiverConfig() { | 105 AudioReceiverConfig GetAudioReceiverConfig() { |
106 AudioReceiverConfig audio_config; | 106 AudioReceiverConfig audio_config = InProcessReceiver::GetDefaultAudioConfig(); |
107 | |
108 GetSsrcs(&audio_config); | 107 GetSsrcs(&audio_config); |
109 GetPayloadtype(&audio_config); | 108 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; | 109 return audio_config; |
119 } | 110 } |
120 | 111 |
121 void GetPayloadtype(VideoReceiverConfig* video_config) { | 112 void GetPayloadtype(VideoReceiverConfig* video_config) { |
122 test::InputBuilder input("Choose video receiver payload type.", | 113 test::InputBuilder input("Choose video receiver payload type.", |
123 DEFAULT_VIDEO_PAYLOAD_TYPE, | 114 DEFAULT_VIDEO_PAYLOAD_TYPE, |
124 96, | 115 96, |
125 127); | 116 127); |
126 video_config->rtp_payload_type = input.GetIntInput(); | 117 video_config->rtp_payload_type = input.GetIntInput(); |
127 } | 118 } |
128 | 119 |
129 VideoReceiverConfig GetVideoReceiverConfig() { | 120 VideoReceiverConfig GetVideoReceiverConfig() { |
130 VideoReceiverConfig video_config; | 121 VideoReceiverConfig video_config = InProcessReceiver::GetDefaultVideoConfig(); |
131 | |
132 GetSsrcs(&video_config); | 122 GetSsrcs(&video_config); |
133 GetPayloadtype(&video_config); | 123 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; | 124 return video_config; |
142 } | 125 } |
143 | 126 |
144 static void UpdateCastTransportStatus(transport::CastTransportStatus status) { | 127 // An InProcessReceiver that renders video frames to a LinuxOutputWindow. While |
145 VLOG(1) << "CastTransportStatus = " << status; | 128 // it does receive audio frames, it does not play them. |
146 } | 129 class ReceiverDisplay : public InProcessReceiver { |
147 | |
148 class ReceiveProcess : public base::RefCountedThreadSafe<ReceiveProcess> { | |
149 public: | 130 public: |
150 explicit ReceiveProcess(scoped_refptr<FrameReceiver> frame_receiver) | 131 ReceiverDisplay(const scoped_refptr<CastEnvironment>& cast_environment, |
151 : frame_receiver_(frame_receiver), | 132 const net::IPEndPoint& local_end_point, |
| 133 const net::IPEndPoint& remote_end_point, |
| 134 const AudioReceiverConfig& audio_config, |
| 135 const VideoReceiverConfig& video_config) |
| 136 : InProcessReceiver(cast_environment, |
| 137 local_end_point, |
| 138 remote_end_point, |
| 139 audio_config, |
| 140 video_config), |
152 #if defined(OS_LINUX) | 141 #if defined(OS_LINUX) |
153 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), | 142 render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), |
154 #endif // OS_LINUX | 143 #endif // OS_LINUX |
155 last_playout_time_(), | 144 last_playout_time_(), |
156 last_render_time_() { | 145 last_render_time_() { |
157 } | 146 } |
158 | 147 virtual ~ReceiverDisplay() {} |
159 void Start() { | |
160 GetAudioFrame(base::TimeDelta::FromMilliseconds(kFrameTimerMs)); | |
161 GetVideoFrame(); | |
162 } | |
163 | 148 |
164 protected: | 149 protected: |
165 virtual ~ReceiveProcess() {} | 150 virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame, |
166 | 151 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 | 152 #ifdef OS_LINUX |
173 render_.RenderFrame(video_frame); | 153 render_.RenderFrame(video_frame); |
174 #endif // OS_LINUX | 154 #endif // OS_LINUX |
175 // Print out the delta between frames. | 155 // Print out the delta between frames. |
176 if (!last_render_time_.is_null()) { | 156 if (!last_render_time_.is_null()) { |
177 base::TimeDelta time_diff = render_time - last_render_time_; | 157 base::TimeDelta time_diff = render_time - last_render_time_; |
178 VLOG(1) << " RenderDelay[mS] = " << time_diff.InMilliseconds(); | 158 VLOG(1) << " RenderDelay[mS] = " << time_diff.InMilliseconds(); |
179 } | 159 } |
180 last_render_time_ = render_time; | 160 last_render_time_ = render_time; |
181 GetVideoFrame(); | |
182 } | 161 } |
183 | 162 |
184 void ReceiveAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame, | 163 virtual void OnAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame, |
185 const base::TimeTicks& playout_time) { | 164 const base::TimeTicks& playout_time) OVERRIDE { |
186 // For audio just print the playout delta between audio frames. | 165 // 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()) { | 166 if (!last_playout_time_.is_null()) { |
191 time_diff = playout_time - last_playout_time_; | 167 base::TimeDelta time_diff = playout_time - last_playout_time_; |
192 VLOG(1) << " ***PlayoutDelay[mS] = " << time_diff.InMilliseconds(); | 168 VLOG(1) << " ***PlayoutDelay[mS] = " << time_diff.InMilliseconds(); |
193 } | 169 } |
194 last_playout_time_ = playout_time; | 170 last_playout_time_ = playout_time; |
195 } | 171 } |
196 | 172 |
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 | 173 #ifdef OS_LINUX |
216 test::LinuxOutputWindow render_; | 174 test::LinuxOutputWindow render_; |
217 #endif // OS_LINUX | 175 #endif // OS_LINUX |
218 base::TimeTicks last_playout_time_; | 176 base::TimeTicks last_playout_time_; |
219 base::TimeTicks last_render_time_; | 177 base::TimeTicks last_render_time_; |
220 }; | 178 }; |
221 | 179 |
222 } // namespace cast | 180 } // namespace cast |
223 } // namespace media | 181 } // namespace media |
224 | 182 |
225 int main(int argc, char** argv) { | 183 int main(int argc, char** argv) { |
226 base::AtExitManager at_exit; | 184 base::AtExitManager at_exit; |
227 base::MessageLoopForIO main_message_loop; | |
228 CommandLine::Init(argc, argv); | 185 CommandLine::Init(argc, argv); |
229 InitLogging(logging::LoggingSettings()); | 186 InitLogging(logging::LoggingSettings()); |
230 | 187 |
231 VLOG(1) << "Cast Receiver"; | 188 // 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; | 189 media::cast::CastLoggingConfig logging_config; |
242 logging_config.enable_raw_data_collection = true; | 190 logging_config.enable_raw_data_collection = true; |
243 | 191 |
244 scoped_refptr<media::cast::CastEnvironment> cast_environment( | 192 scoped_refptr<media::cast::CastEnvironment> cast_environment( |
245 new media::cast::CastEnvironment(clock.Pass(), | 193 new media::cast::CastEnvironment(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 | 194 |
254 media::cast::AudioReceiverConfig audio_config = | 195 media::cast::AudioReceiverConfig audio_config = |
255 media::cast::GetAudioReceiverConfig(); | 196 media::cast::GetAudioReceiverConfig(); |
256 media::cast::VideoReceiverConfig video_config = | 197 media::cast::VideoReceiverConfig video_config = |
257 media::cast::GetVideoReceiverConfig(); | 198 media::cast::GetVideoReceiverConfig(); |
258 | 199 |
259 int remote_port, local_port; | 200 int remote_port, local_port; |
260 media::cast::GetPorts(&remote_port, &local_port); | 201 media::cast::GetPorts(&remote_port, &local_port); |
261 if (!local_port) { | 202 if (!local_port) { |
262 LOG(ERROR) << "Invalid local port."; | 203 LOG(ERROR) << "Invalid local port."; |
(...skipping 11 matching lines...) Expand all Loading... |
274 } | 215 } |
275 | 216 |
276 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { | 217 if (!net::ParseIPLiteralToNumber(local_ip_address, &local_ip_number)) { |
277 LOG(ERROR) << "Invalid local IP address."; | 218 LOG(ERROR) << "Invalid local IP address."; |
278 return 1; | 219 return 1; |
279 } | 220 } |
280 | 221 |
281 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); | 222 net::IPEndPoint remote_end_point(remote_ip_number, remote_port); |
282 net::IPEndPoint local_end_point(local_ip_number, local_port); | 223 net::IPEndPoint local_end_point(local_ip_number, local_port); |
283 | 224 |
284 scoped_ptr<media::cast::transport::UdpTransport> transport( | 225 media::cast::ReceiverDisplay receiver_display(cast_environment, |
285 new media::cast::transport::UdpTransport( | 226 local_end_point, |
286 NULL, | 227 remote_end_point, |
287 main_message_loop.message_loop_proxy(), | 228 audio_config, |
288 local_end_point, | 229 video_config); |
289 remote_end_point, | 230 receiver_display.Start(); |
290 base::Bind(&media::cast::UpdateCastTransportStatus))); | |
291 scoped_ptr<media::cast::CastReceiver> cast_receiver( | |
292 media::cast::CastReceiver::CreateCastReceiver( | |
293 cast_environment, audio_config, video_config, transport.get())); | |
294 | 231 |
295 // TODO(hubbe): Make the cast receiver do this automatically. | 232 base::MessageLoop().Run(); // Run forever (i.e., until SIGTERM). |
296 transport->StartReceiving(cast_receiver->packet_receiver()); | |
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; | 233 return 0; |
303 } | 234 } |
OLD | NEW |