sender.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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
  3. // found in the LICENSE file.
  4. // Test application that simulates a cast sender - Data can be either generated
  5. // or read from a file.
  6. #include <stdint.h>
  7. #include <memory>
  8. #include <utility>
  9. #include "base/at_exit.h"
  10. #include "base/base_paths.h"
  11. #include "base/bind.h"
  12. #include "base/callback_helpers.h"
  13. #include "base/command_line.h"
  14. #include "base/files/file_path.h"
  15. #include "base/json/json_writer.h"
  16. #include "base/logging.h"
  17. #include "base/memory/raw_ptr.h"
  18. #include "base/message_loop/message_pump_type.h"
  19. #include "base/path_service.h"
  20. #include "base/run_loop.h"
  21. #include "base/strings/string_number_conversions.h"
  22. #include "base/task/single_thread_task_executor.h"
  23. #include "base/task/single_thread_task_runner.h"
  24. #include "base/threading/thread.h"
  25. #include "base/time/default_tick_clock.h"
  26. #include "base/values.h"
  27. #include "media/base/media.h"
  28. #include "media/base/video_frame.h"
  29. #include "media/cast/cast_config.h"
  30. #include "media/cast/cast_environment.h"
  31. #include "media/cast/cast_sender.h"
  32. #include "media/cast/logging/encoding_event_subscriber.h"
  33. #include "media/cast/logging/logging_defines.h"
  34. #include "media/cast/logging/proto/raw_events.pb.h"
  35. #include "media/cast/logging/receiver_time_offset_estimator_impl.h"
  36. #include "media/cast/logging/stats_event_subscriber.h"
  37. #include "media/cast/net/cast_transport.h"
  38. #include "media/cast/net/cast_transport_defines.h"
  39. #include "media/cast/net/udp_transport_impl.h"
  40. #include "media/cast/test/fake_media_source.h"
  41. #include "media/cast/test/utility/default_config.h"
  42. #include "media/cast/test/utility/input_builder.h"
  43. namespace {
  44. // Flags for this program:
  45. //
  46. // --address=xx.xx.xx.xx
  47. // IP address of receiver.
  48. //
  49. // --port=xxxx
  50. // Port number of receiver.
  51. //
  52. // --source-file=xxx.webm
  53. // WebM file as source of video frames.
  54. //
  55. // --fps=xx
  56. // Override framerate of the video stream.
  57. //
  58. // --vary-frame-sizes
  59. // Randomly vary the video frame sizes at random points in time. Has no
  60. // effect if --source-file is being used.
  61. const char kSwitchAddress[] = "address";
  62. const char kSwitchPort[] = "port";
  63. const char kSwitchSourceFile[] = "source-file";
  64. const char kSwitchFps[] = "fps";
  65. const char kSwitchVaryFrameSizes[] = "vary-frame-sizes";
  66. void UpdateCastTransportStatus(
  67. media::cast::CastTransportStatus status) {
  68. VLOG(1) << "Transport status: " << status;
  69. }
  70. void QuitLoopOnInitializationResult(media::cast::OperationalStatus result) {
  71. CHECK(result == media::cast::STATUS_INITIALIZED)
  72. << "Cast sender uninitialized";
  73. base::RunLoop::QuitCurrentWhenIdleDeprecated();
  74. }
  75. net::IPEndPoint CreateUDPAddress(const std::string& ip_str, uint16_t port) {
  76. net::IPAddress ip_address;
  77. CHECK(ip_address.AssignFromIPLiteral(ip_str));
  78. return net::IPEndPoint(ip_address, port);
  79. }
  80. void WriteLogsToFileAndDestroySubscribers(
  81. const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
  82. std::unique_ptr<media::cast::EncodingEventSubscriber>
  83. video_event_subscriber,
  84. std::unique_ptr<media::cast::EncodingEventSubscriber>
  85. audio_event_subscriber,
  86. base::ScopedFILE video_log_file,
  87. base::ScopedFILE audio_log_file) {
  88. cast_environment->logger()->Unsubscribe(video_event_subscriber.get());
  89. cast_environment->logger()->Unsubscribe(audio_event_subscriber.get());
  90. }
  91. void WriteStatsAndDestroySubscribers(
  92. const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
  93. std::unique_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber,
  94. std::unique_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber,
  95. std::unique_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> estimator) {
  96. cast_environment->logger()->Unsubscribe(video_stats_subscriber.get());
  97. cast_environment->logger()->Unsubscribe(audio_stats_subscriber.get());
  98. cast_environment->logger()->Unsubscribe(estimator.get());
  99. base::Value::Dict stats = video_stats_subscriber->GetStats();
  100. std::string json;
  101. base::JSONWriter::WriteWithOptions(
  102. stats, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
  103. VLOG(0) << "Video stats: " << json;
  104. stats = audio_stats_subscriber->GetStats();
  105. json.clear();
  106. base::JSONWriter::WriteWithOptions(
  107. stats, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
  108. VLOG(0) << "Audio stats: " << json;
  109. }
  110. class TransportClient : public media::cast::CastTransport::Client {
  111. public:
  112. explicit TransportClient(
  113. media::cast::LogEventDispatcher* log_event_dispatcher)
  114. : log_event_dispatcher_(log_event_dispatcher) {}
  115. TransportClient(const TransportClient&) = delete;
  116. TransportClient& operator=(const TransportClient&) = delete;
  117. void OnStatusChanged(media::cast::CastTransportStatus status) final {
  118. VLOG(1) << "Transport status: " << status;
  119. }
  120. void OnLoggingEventsReceived(
  121. std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
  122. std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events)
  123. final {
  124. DCHECK(log_event_dispatcher_);
  125. log_event_dispatcher_->DispatchBatchOfEvents(std::move(frame_events),
  126. std::move(packet_events));
  127. }
  128. void ProcessRtpPacket(std::unique_ptr<media::cast::Packet> packet) final {}
  129. private:
  130. const raw_ptr<media::cast::LogEventDispatcher>
  131. log_event_dispatcher_; // Not owned by this class.
  132. };
  133. } // namespace
  134. int main(int argc, char** argv) {
  135. base::AtExitManager at_exit;
  136. base::CommandLine::Init(argc, argv);
  137. InitLogging(logging::LoggingSettings());
  138. // Prepare media module for FFmpeg decoding.
  139. media::InitializeMediaLibrary();
  140. base::Thread test_thread("Cast sender test app thread");
  141. base::Thread audio_thread("Cast audio encoder thread");
  142. base::Thread video_thread("Cast video encoder thread");
  143. test_thread.Start();
  144. audio_thread.Start();
  145. video_thread.Start();
  146. base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
  147. // Default parameters.
  148. base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
  149. std::string remote_ip_address = cmd->GetSwitchValueASCII(kSwitchAddress);
  150. if (remote_ip_address.empty())
  151. remote_ip_address = "127.0.0.1";
  152. int remote_port = 0;
  153. if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchPort), &remote_port) ||
  154. remote_port < 0 || remote_port > 65535) {
  155. remote_port = 2344;
  156. }
  157. LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port
  158. << ".";
  159. media::cast::FrameSenderConfig audio_config =
  160. media::cast::GetDefaultAudioSenderConfig();
  161. media::cast::FrameSenderConfig video_config =
  162. media::cast::GetDefaultVideoSenderConfig();
  163. // Running transport on the main thread.
  164. // Setting up transport config.
  165. net::IPEndPoint remote_endpoint =
  166. CreateUDPAddress(remote_ip_address, static_cast<uint16_t>(remote_port));
  167. // Enable raw event and stats logging.
  168. // Running transport on the main thread.
  169. scoped_refptr<media::cast::CastEnvironment> cast_environment(
  170. new media::cast::CastEnvironment(
  171. base::DefaultTickClock::GetInstance(), io_task_executor.task_runner(),
  172. audio_thread.task_runner(), video_thread.task_runner()));
  173. // SendProcess initialization.
  174. std::unique_ptr<media::cast::FakeMediaSource> fake_media_source(
  175. new media::cast::FakeMediaSource(test_thread.task_runner(),
  176. cast_environment->Clock(), audio_config,
  177. video_config, false));
  178. int final_fps = 0;
  179. if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps),
  180. &final_fps)){
  181. final_fps = 0;
  182. }
  183. base::FilePath source_path = cmd->GetSwitchValuePath(kSwitchSourceFile);
  184. if (!source_path.empty()) {
  185. LOG(INFO) << "Source: " << source_path.value();
  186. fake_media_source->SetSourceFile(source_path, final_fps);
  187. }
  188. if (cmd->HasSwitch(kSwitchVaryFrameSizes))
  189. fake_media_source->SetVariableFrameSizeMode(true);
  190. // CastTransport initialization.
  191. std::unique_ptr<media::cast::CastTransport> transport_sender =
  192. media::cast::CastTransport::Create(
  193. cast_environment->Clock(), base::Seconds(1),
  194. std::make_unique<TransportClient>(cast_environment->logger()),
  195. std::make_unique<media::cast::UdpTransportImpl>(
  196. io_task_executor.task_runner(), net::IPEndPoint(),
  197. remote_endpoint, base::BindRepeating(&UpdateCastTransportStatus)),
  198. io_task_executor.task_runner());
  199. // Set up event subscribers.
  200. std::unique_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber;
  201. std::unique_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber;
  202. std::string video_log_file_name("/tmp/video_events.log.gz");
  203. std::string audio_log_file_name("/tmp/audio_events.log.gz");
  204. LOG(INFO) << "Logging audio events to: " << audio_log_file_name;
  205. LOG(INFO) << "Logging video events to: " << video_log_file_name;
  206. video_event_subscriber =
  207. std::make_unique<media::cast::EncodingEventSubscriber>(
  208. media::cast::VIDEO_EVENT, 10000);
  209. audio_event_subscriber =
  210. std::make_unique<media::cast::EncodingEventSubscriber>(
  211. media::cast::AUDIO_EVENT, 10000);
  212. cast_environment->logger()->Subscribe(video_event_subscriber.get());
  213. cast_environment->logger()->Subscribe(audio_event_subscriber.get());
  214. // Subscribers for stats.
  215. std::unique_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl>
  216. offset_estimator(new media::cast::ReceiverTimeOffsetEstimatorImpl());
  217. cast_environment->logger()->Subscribe(offset_estimator.get());
  218. std::unique_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber(
  219. new media::cast::StatsEventSubscriber(media::cast::VIDEO_EVENT,
  220. cast_environment->Clock(),
  221. offset_estimator.get()));
  222. std::unique_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber(
  223. new media::cast::StatsEventSubscriber(media::cast::AUDIO_EVENT,
  224. cast_environment->Clock(),
  225. offset_estimator.get()));
  226. cast_environment->logger()->Subscribe(video_stats_subscriber.get());
  227. cast_environment->logger()->Subscribe(audio_stats_subscriber.get());
  228. base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w"));
  229. if (!video_log_file) {
  230. VLOG(1) << "Failed to open video log file for writing.";
  231. exit(-1);
  232. }
  233. base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w"));
  234. if (!audio_log_file) {
  235. VLOG(1) << "Failed to open audio log file for writing.";
  236. exit(-1);
  237. }
  238. const int logging_duration_seconds = 10;
  239. io_task_executor.task_runner()->PostDelayedTask(
  240. FROM_HERE,
  241. base::BindOnce(&WriteLogsToFileAndDestroySubscribers, cast_environment,
  242. std::move(video_event_subscriber),
  243. std::move(audio_event_subscriber),
  244. std::move(video_log_file), std::move(audio_log_file)),
  245. base::Seconds(logging_duration_seconds));
  246. io_task_executor.task_runner()->PostDelayedTask(
  247. FROM_HERE,
  248. base::BindOnce(&WriteStatsAndDestroySubscribers, cast_environment,
  249. std::move(video_stats_subscriber),
  250. std::move(audio_stats_subscriber),
  251. std::move(offset_estimator)),
  252. base::Seconds(logging_duration_seconds));
  253. // CastSender initialization.
  254. std::unique_ptr<media::cast::CastSender> cast_sender =
  255. media::cast::CastSender::Create(cast_environment, transport_sender.get());
  256. io_task_executor.task_runner()->PostTask(
  257. FROM_HERE,
  258. base::BindOnce(&media::cast::CastSender::InitializeVideo,
  259. base::Unretained(cast_sender.get()),
  260. fake_media_source->get_video_config(),
  261. base::BindRepeating(&QuitLoopOnInitializationResult),
  262. base::DoNothing()));
  263. base::RunLoop().Run(); // Wait for video initialization.
  264. io_task_executor.task_runner()->PostTask(
  265. FROM_HERE,
  266. base::BindOnce(&media::cast::CastSender::InitializeAudio,
  267. base::Unretained(cast_sender.get()), audio_config,
  268. base::BindRepeating(&QuitLoopOnInitializationResult)));
  269. base::RunLoop().Run(); // Wait for audio initialization.
  270. fake_media_source->Start(cast_sender->audio_frame_input(),
  271. cast_sender->video_frame_input());
  272. base::RunLoop().Run();
  273. return 0;
  274. }