123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- // 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.
- // Test application that simulates a cast sender - Data can be either generated
- // or read from a file.
- #include <stdint.h>
- #include <memory>
- #include <utility>
- #include "base/at_exit.h"
- #include "base/base_paths.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/command_line.h"
- #include "base/files/file_path.h"
- #include "base/json/json_writer.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/message_loop/message_pump_type.h"
- #include "base/path_service.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/task/single_thread_task_executor.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread.h"
- #include "base/time/default_tick_clock.h"
- #include "base/values.h"
- #include "media/base/media.h"
- #include "media/base/video_frame.h"
- #include "media/cast/cast_config.h"
- #include "media/cast/cast_environment.h"
- #include "media/cast/cast_sender.h"
- #include "media/cast/logging/encoding_event_subscriber.h"
- #include "media/cast/logging/logging_defines.h"
- #include "media/cast/logging/proto/raw_events.pb.h"
- #include "media/cast/logging/receiver_time_offset_estimator_impl.h"
- #include "media/cast/logging/stats_event_subscriber.h"
- #include "media/cast/net/cast_transport.h"
- #include "media/cast/net/cast_transport_defines.h"
- #include "media/cast/net/udp_transport_impl.h"
- #include "media/cast/test/fake_media_source.h"
- #include "media/cast/test/utility/default_config.h"
- #include "media/cast/test/utility/input_builder.h"
- namespace {
- // Flags for this program:
- //
- // --address=xx.xx.xx.xx
- // IP address of receiver.
- //
- // --port=xxxx
- // Port number of receiver.
- //
- // --source-file=xxx.webm
- // WebM file as source of video frames.
- //
- // --fps=xx
- // Override framerate of the video stream.
- //
- // --vary-frame-sizes
- // Randomly vary the video frame sizes at random points in time. Has no
- // effect if --source-file is being used.
- const char kSwitchAddress[] = "address";
- const char kSwitchPort[] = "port";
- const char kSwitchSourceFile[] = "source-file";
- const char kSwitchFps[] = "fps";
- const char kSwitchVaryFrameSizes[] = "vary-frame-sizes";
- void UpdateCastTransportStatus(
- media::cast::CastTransportStatus status) {
- VLOG(1) << "Transport status: " << status;
- }
- void QuitLoopOnInitializationResult(media::cast::OperationalStatus result) {
- CHECK(result == media::cast::STATUS_INITIALIZED)
- << "Cast sender uninitialized";
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- net::IPEndPoint CreateUDPAddress(const std::string& ip_str, uint16_t port) {
- net::IPAddress ip_address;
- CHECK(ip_address.AssignFromIPLiteral(ip_str));
- return net::IPEndPoint(ip_address, port);
- }
- void WriteLogsToFileAndDestroySubscribers(
- const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
- std::unique_ptr<media::cast::EncodingEventSubscriber>
- video_event_subscriber,
- std::unique_ptr<media::cast::EncodingEventSubscriber>
- audio_event_subscriber,
- base::ScopedFILE video_log_file,
- base::ScopedFILE audio_log_file) {
- cast_environment->logger()->Unsubscribe(video_event_subscriber.get());
- cast_environment->logger()->Unsubscribe(audio_event_subscriber.get());
- }
- void WriteStatsAndDestroySubscribers(
- const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
- std::unique_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber,
- std::unique_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber,
- std::unique_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> estimator) {
- cast_environment->logger()->Unsubscribe(video_stats_subscriber.get());
- cast_environment->logger()->Unsubscribe(audio_stats_subscriber.get());
- cast_environment->logger()->Unsubscribe(estimator.get());
- base::Value::Dict stats = video_stats_subscriber->GetStats();
- std::string json;
- base::JSONWriter::WriteWithOptions(
- stats, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
- VLOG(0) << "Video stats: " << json;
- stats = audio_stats_subscriber->GetStats();
- json.clear();
- base::JSONWriter::WriteWithOptions(
- stats, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
- VLOG(0) << "Audio stats: " << json;
- }
- class TransportClient : public media::cast::CastTransport::Client {
- public:
- explicit TransportClient(
- media::cast::LogEventDispatcher* log_event_dispatcher)
- : log_event_dispatcher_(log_event_dispatcher) {}
- TransportClient(const TransportClient&) = delete;
- TransportClient& operator=(const TransportClient&) = delete;
- void OnStatusChanged(media::cast::CastTransportStatus status) final {
- VLOG(1) << "Transport status: " << status;
- }
- void OnLoggingEventsReceived(
- std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
- std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events)
- final {
- DCHECK(log_event_dispatcher_);
- log_event_dispatcher_->DispatchBatchOfEvents(std::move(frame_events),
- std::move(packet_events));
- }
- void ProcessRtpPacket(std::unique_ptr<media::cast::Packet> packet) final {}
- private:
- const raw_ptr<media::cast::LogEventDispatcher>
- log_event_dispatcher_; // Not owned by this class.
- };
- } // namespace
- int main(int argc, char** argv) {
- base::AtExitManager at_exit;
- base::CommandLine::Init(argc, argv);
- InitLogging(logging::LoggingSettings());
- // Prepare media module for FFmpeg decoding.
- media::InitializeMediaLibrary();
- base::Thread test_thread("Cast sender test app thread");
- base::Thread audio_thread("Cast audio encoder thread");
- base::Thread video_thread("Cast video encoder thread");
- test_thread.Start();
- audio_thread.Start();
- video_thread.Start();
- base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
- // Default parameters.
- base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
- std::string remote_ip_address = cmd->GetSwitchValueASCII(kSwitchAddress);
- if (remote_ip_address.empty())
- remote_ip_address = "127.0.0.1";
- int remote_port = 0;
- if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchPort), &remote_port) ||
- remote_port < 0 || remote_port > 65535) {
- remote_port = 2344;
- }
- LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port
- << ".";
- media::cast::FrameSenderConfig audio_config =
- media::cast::GetDefaultAudioSenderConfig();
- media::cast::FrameSenderConfig video_config =
- media::cast::GetDefaultVideoSenderConfig();
- // Running transport on the main thread.
- // Setting up transport config.
- net::IPEndPoint remote_endpoint =
- CreateUDPAddress(remote_ip_address, static_cast<uint16_t>(remote_port));
- // Enable raw event and stats logging.
- // Running transport on the main thread.
- scoped_refptr<media::cast::CastEnvironment> cast_environment(
- new media::cast::CastEnvironment(
- base::DefaultTickClock::GetInstance(), io_task_executor.task_runner(),
- audio_thread.task_runner(), video_thread.task_runner()));
- // SendProcess initialization.
- std::unique_ptr<media::cast::FakeMediaSource> fake_media_source(
- new media::cast::FakeMediaSource(test_thread.task_runner(),
- cast_environment->Clock(), audio_config,
- video_config, false));
- int final_fps = 0;
- if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps),
- &final_fps)){
- final_fps = 0;
- }
- base::FilePath source_path = cmd->GetSwitchValuePath(kSwitchSourceFile);
- if (!source_path.empty()) {
- LOG(INFO) << "Source: " << source_path.value();
- fake_media_source->SetSourceFile(source_path, final_fps);
- }
- if (cmd->HasSwitch(kSwitchVaryFrameSizes))
- fake_media_source->SetVariableFrameSizeMode(true);
- // CastTransport initialization.
- std::unique_ptr<media::cast::CastTransport> transport_sender =
- media::cast::CastTransport::Create(
- cast_environment->Clock(), base::Seconds(1),
- std::make_unique<TransportClient>(cast_environment->logger()),
- std::make_unique<media::cast::UdpTransportImpl>(
- io_task_executor.task_runner(), net::IPEndPoint(),
- remote_endpoint, base::BindRepeating(&UpdateCastTransportStatus)),
- io_task_executor.task_runner());
- // Set up event subscribers.
- std::unique_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber;
- std::unique_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber;
- std::string video_log_file_name("/tmp/video_events.log.gz");
- std::string audio_log_file_name("/tmp/audio_events.log.gz");
- LOG(INFO) << "Logging audio events to: " << audio_log_file_name;
- LOG(INFO) << "Logging video events to: " << video_log_file_name;
- video_event_subscriber =
- std::make_unique<media::cast::EncodingEventSubscriber>(
- media::cast::VIDEO_EVENT, 10000);
- audio_event_subscriber =
- std::make_unique<media::cast::EncodingEventSubscriber>(
- media::cast::AUDIO_EVENT, 10000);
- cast_environment->logger()->Subscribe(video_event_subscriber.get());
- cast_environment->logger()->Subscribe(audio_event_subscriber.get());
- // Subscribers for stats.
- std::unique_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl>
- offset_estimator(new media::cast::ReceiverTimeOffsetEstimatorImpl());
- cast_environment->logger()->Subscribe(offset_estimator.get());
- std::unique_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber(
- new media::cast::StatsEventSubscriber(media::cast::VIDEO_EVENT,
- cast_environment->Clock(),
- offset_estimator.get()));
- std::unique_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber(
- new media::cast::StatsEventSubscriber(media::cast::AUDIO_EVENT,
- cast_environment->Clock(),
- offset_estimator.get()));
- cast_environment->logger()->Subscribe(video_stats_subscriber.get());
- cast_environment->logger()->Subscribe(audio_stats_subscriber.get());
- base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w"));
- if (!video_log_file) {
- VLOG(1) << "Failed to open video log file for writing.";
- exit(-1);
- }
- base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w"));
- if (!audio_log_file) {
- VLOG(1) << "Failed to open audio log file for writing.";
- exit(-1);
- }
- const int logging_duration_seconds = 10;
- io_task_executor.task_runner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&WriteLogsToFileAndDestroySubscribers, cast_environment,
- std::move(video_event_subscriber),
- std::move(audio_event_subscriber),
- std::move(video_log_file), std::move(audio_log_file)),
- base::Seconds(logging_duration_seconds));
- io_task_executor.task_runner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&WriteStatsAndDestroySubscribers, cast_environment,
- std::move(video_stats_subscriber),
- std::move(audio_stats_subscriber),
- std::move(offset_estimator)),
- base::Seconds(logging_duration_seconds));
- // CastSender initialization.
- std::unique_ptr<media::cast::CastSender> cast_sender =
- media::cast::CastSender::Create(cast_environment, transport_sender.get());
- io_task_executor.task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&media::cast::CastSender::InitializeVideo,
- base::Unretained(cast_sender.get()),
- fake_media_source->get_video_config(),
- base::BindRepeating(&QuitLoopOnInitializationResult),
- base::DoNothing()));
- base::RunLoop().Run(); // Wait for video initialization.
- io_task_executor.task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&media::cast::CastSender::InitializeAudio,
- base::Unretained(cast_sender.get()), audio_config,
- base::BindRepeating(&QuitLoopOnInitializationResult)));
- base::RunLoop().Run(); // Wait for audio initialization.
- fake_media_source->Start(cast_sender->audio_frame_input(),
- cast_sender->video_frame_input());
- base::RunLoop().Run();
- return 0;
- }
|