// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <algorithm> #include <climits> #include <cstdarg> #include <cstdio> #include <string> #include "base/at_exit.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread.h" #include "base/time/default_tick_clock.h" #include "media/base/video_frame.h" #include "media/cast/cast_config.h" #include "media/cast/cast_environment.h" #include "media/cast/cast_receiver.h" #include "media/cast/logging/logging_defines.h" #include "media/cast/test/transport/transport.h" #include "media/cast/test/utility/input_helper.h" #if defined(OS_LINUX) #include "media/cast/test/linux_output_window.h" #endif // OS_LINUX namespace media { namespace cast { // Settings chosen to match default sender settings. #define DEFAULT_SEND_PORT "2346" #define DEFAULT_RECEIVE_PORT "2344" #define DEFAULT_SEND_IP "127.0.0.1" #define DEFAULT_RESTART "0" #define DEFAULT_AUDIO_FEEDBACK_SSRC "1" #define DEFAULT_AUDIO_INCOMING_SSRC "2" #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" #define DEFAULT_VIDEO_FEEDBACK_SSRC "12" #define DEFAULT_VIDEO_INCOMING_SSRC "11" #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" #define DEFAULT_VIDEO_CODEC_WIDTH "640" #define DEFAULT_VIDEO_CODEC_HEIGHT "480" #define DEFAULT_VIDEO_CODEC_BITRATE "2000" static const int kAudioSamplingFrequency = 48000; #if defined(OS_LINUX) const int kVideoWindowWidth = 1280; const int kVideoWindowHeight = 720; #endif // OS_LINUX static const int kFrameTimerMs = 33; void GetPorts(int* tx_port, int* rx_port) { test::InputBuilder tx_input("Enter send port.", DEFAULT_SEND_PORT, 1, INT_MAX); *tx_port = tx_input.GetIntInput(); test::InputBuilder rx_input("Enter receive port.", DEFAULT_RECEIVE_PORT, 1, INT_MAX); *rx_port = rx_input.GetIntInput(); } std::string GetIpAddress(const std::string display_text) { test::InputBuilder input(display_text, DEFAULT_SEND_IP, INT_MIN, INT_MAX); std::string ip_address = input.GetStringInput(); // Ensure correct form: while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) { ip_address = input.GetStringInput(); } return ip_address; } void GetSsrcs(AudioReceiverConfig* audio_config) { test::InputBuilder input_tx("Choose audio sender SSRC.", DEFAULT_AUDIO_FEEDBACK_SSRC, 1, INT_MAX); audio_config->feedback_ssrc = input_tx.GetIntInput(); test::InputBuilder input_rx("Choose audio receiver SSRC.", DEFAULT_AUDIO_INCOMING_SSRC, 1, INT_MAX); audio_config->incoming_ssrc = input_tx.GetIntInput(); } void GetSsrcs(VideoReceiverConfig* video_config) { test::InputBuilder input_tx("Choose video sender SSRC.", DEFAULT_VIDEO_FEEDBACK_SSRC, 1, INT_MAX); video_config->feedback_ssrc = input_tx.GetIntInput(); test::InputBuilder input_rx("Choose video receiver SSRC.", DEFAULT_VIDEO_INCOMING_SSRC, 1, INT_MAX); video_config->incoming_ssrc = input_rx.GetIntInput(); } void GetPayloadtype(AudioReceiverConfig* audio_config) { test::InputBuilder input("Choose audio receiver payload type.", DEFAULT_AUDIO_PAYLOAD_TYPE, 96, 127); audio_config->rtp_payload_type = input.GetIntInput(); } AudioReceiverConfig GetAudioReceiverConfig() { AudioReceiverConfig audio_config; GetSsrcs(&audio_config); GetPayloadtype(&audio_config); audio_config.rtcp_c_name = "audio_receiver@a.b.c.d"; VLOG(1) << "Using OPUS 48Khz stereo"; audio_config.use_external_decoder = false; audio_config.frequency = 48000; audio_config.channels = 2; audio_config.codec = kOpus; return audio_config; } void GetPayloadtype(VideoReceiverConfig* video_config) { test::InputBuilder input("Choose video receiver payload type.", DEFAULT_VIDEO_PAYLOAD_TYPE, 96, 127); video_config->rtp_payload_type = input.GetIntInput(); } VideoReceiverConfig GetVideoReceiverConfig() { VideoReceiverConfig video_config; GetSsrcs(&video_config); GetPayloadtype(&video_config); video_config.rtcp_c_name = "video_receiver@a.b.c.d"; video_config.use_external_decoder = false; VLOG(1) << "Using VP8"; video_config.codec = kVp8; return video_config; } class ReceiveProcess : public base::RefCountedThreadSafe<ReceiveProcess> { public: explicit ReceiveProcess(scoped_refptr<FrameReceiver> frame_receiver) : frame_receiver_(frame_receiver), #if defined(OS_LINUX) render_(0, 0, kVideoWindowWidth, kVideoWindowHeight, "Cast_receiver"), #endif // OS_LINUX last_playout_time_(), last_render_time_() {} void Start() { GetAudioFrame(base::TimeDelta::FromMilliseconds(kFrameTimerMs)); GetVideoFrame(); } protected: virtual ~ReceiveProcess() {} private: friend class base::RefCountedThreadSafe<ReceiveProcess>; void DisplayFrame(const scoped_refptr<media::VideoFrame>& video_frame, const base::TimeTicks& render_time) { #ifdef OS_LINUX render_.RenderFrame(video_frame); #endif // OS_LINUX // Print out the delta between frames. if (!last_render_time_.is_null()){ base::TimeDelta time_diff = render_time - last_render_time_; VLOG(0) << " RenderDelay[mS] = " << time_diff.InMilliseconds(); } last_render_time_ = render_time; GetVideoFrame(); } void ReceiveAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame, const base::TimeTicks& playout_time) { // For audio just print the playout delta between audio frames. // Default diff time is kFrameTimerMs. base::TimeDelta time_diff = base::TimeDelta::FromMilliseconds(kFrameTimerMs); if (!last_playout_time_.is_null()){ time_diff = playout_time - last_playout_time_; VLOG(0) << " ***PlayoutDelay[mS] = " << time_diff.InMilliseconds(); } last_playout_time_ = playout_time; GetAudioFrame(time_diff); } void GetAudioFrame(base::TimeDelta playout_diff) { int num_10ms_blocks = playout_diff.InMilliseconds() / 10; frame_receiver_->GetRawAudioFrame(num_10ms_blocks, kAudioSamplingFrequency, base::Bind(&ReceiveProcess::ReceiveAudioFrame, this)); } void GetVideoFrame() { frame_receiver_->GetRawVideoFrame( base::Bind(&ReceiveProcess::DisplayFrame, this)); } scoped_refptr<FrameReceiver> frame_receiver_; #ifdef OS_LINUX test::LinuxOutputWindow render_; #endif // OS_LINUX base::TimeTicks last_playout_time_; base::TimeTicks last_render_time_; }; } // namespace cast } // namespace media int main(int argc, char** argv) { base::AtExitManager at_exit; base::MessageLoopForIO main_message_loop; VLOG(1) << "Cast Receiver"; base::Thread main_thread("Cast main send thread"); base::Thread audio_thread("Cast audio decoder thread"); base::Thread video_thread("Cast video decoder thread"); main_thread.Start(); audio_thread.Start(); video_thread.Start(); base::DefaultTickClock clock; // Enable receiver side threads, and disable logging. scoped_refptr<media::cast::CastEnvironment> cast_environment(new media::cast::CastEnvironment(&clock, main_thread.message_loop_proxy(), NULL, audio_thread.message_loop_proxy(), NULL, video_thread.message_loop_proxy(), media::cast::GetDefaultCastLoggingConfig())); media::cast::AudioReceiverConfig audio_config = media::cast::GetAudioReceiverConfig(); media::cast::VideoReceiverConfig video_config = media::cast::GetVideoReceiverConfig(); scoped_ptr<media::cast::test::Transport> transport( new media::cast::test::Transport(main_message_loop.message_loop_proxy())); scoped_ptr<media::cast::CastReceiver> cast_receiver( media::cast::CastReceiver::CreateCastReceiver( cast_environment, audio_config, video_config, transport->packet_sender())); media::cast::PacketReceiver* packet_receiver = cast_receiver->packet_receiver(); int send_to_port, receive_port; media::cast::GetPorts(&send_to_port, &receive_port); std::string ip_address = media::cast::GetIpAddress("Enter destination IP."); std::string local_ip_address = media::cast::GetIpAddress("Enter local IP."); transport->SetLocalReceiver(packet_receiver, ip_address, local_ip_address, receive_port); transport->SetSendDestination(ip_address, send_to_port); scoped_refptr<media::cast::ReceiveProcess> receive_process( new media::cast::ReceiveProcess(cast_receiver->frame_receiver())); receive_process->Start(); main_message_loop.Run(); transport->StopReceiving(); return 0; }