diff options
-rw-r--r-- | media/cast/test/end2end_unittest.cc | 94 | ||||
-rw-r--r-- | media/cast/test/utility/udp_proxy.cc | 27 | ||||
-rw-r--r-- | media/cast/test/utility/udp_proxy.h | 16 | ||||
-rw-r--r-- | media/cast/test/utility/udp_proxy_main.cc | 2 | ||||
-rw-r--r-- | media/cast/transport/cast_transport_config.h | 7 | ||||
-rw-r--r-- | media/cast/video_receiver/codecs/vp8/vp8_decoder.h | 45 | ||||
-rw-r--r-- | media/cast/video_receiver/video_decoder.cc | 46 | ||||
-rw-r--r-- | media/cast/video_receiver/video_decoder.h | 1 | ||||
-rw-r--r-- | media/cast/video_sender/codecs/vp8/vp8_encoder.h | 17 | ||||
-rw-r--r-- | media/cast/video_sender/external_video_encoder.cc | 3 | ||||
-rw-r--r-- | media/cast/video_sender/fake_software_video_encoder.cc | 59 | ||||
-rw-r--r-- | media/cast/video_sender/fake_software_video_encoder.h | 35 | ||||
-rw-r--r-- | media/cast/video_sender/software_video_encoder.h | 46 | ||||
-rw-r--r-- | media/cast/video_sender/video_encoder_impl.cc | 41 | ||||
-rw-r--r-- | media/cast/video_sender/video_encoder_impl.h | 4 | ||||
-rw-r--r-- | media/cast/video_sender/video_sender.gypi | 3 |
16 files changed, 374 insertions, 72 deletions
diff --git a/media/cast/test/end2end_unittest.cc b/media/cast/test/end2end_unittest.cc index 4e38a53..6dccbcf 100644 --- a/media/cast/test/end2end_unittest.cc +++ b/media/cast/test/end2end_unittest.cc @@ -33,6 +33,7 @@ #include "media/cast/test/fake_single_thread_task_runner.h" #include "media/cast/test/utility/audio_utility.h" #include "media/cast/test/utility/default_config.h" +#include "media/cast/test/utility/udp_proxy.h" #include "media/cast/test/utility/video_utility.h" #include "media/cast/transport/cast_transport_config.h" #include "media/cast/transport/cast_transport_defines.h" @@ -159,8 +160,30 @@ std::map<uint16, LoggingEventCounts> GetEventCountForPacketEvents( return event_counter_for_packet; } +void CountVideoFrame(int* counter, + const scoped_refptr<media::VideoFrame>& video_frame, + const base::TimeTicks& render_time, bool continuous) { + ++*counter; +} + } // namespace +class LoopBackPacketPipe : public test::PacketPipe { + public: + LoopBackPacketPipe(const transport::PacketReceiverCallback& packet_receiver) + : packet_receiver_(packet_receiver) {} + + virtual ~LoopBackPacketPipe() {} + + // PacketPipe implementations. + virtual void Send(scoped_ptr<transport::Packet> packet) OVERRIDE { + packet_receiver_.Run(packet.Pass()); + } + + private: + transport::PacketReceiverCallback packet_receiver_; +}; + // Class that sends the packet direct from sender into the receiver with the // ability to drop packets between the two. class LoopBackTransport : public transport::PacketSender { @@ -173,7 +196,13 @@ class LoopBackTransport : public transport::PacketSender { void SetPacketReceiver( const transport::PacketReceiverCallback& packet_receiver) { - packet_receiver_ = packet_receiver; + scoped_ptr<test::PacketPipe> loopback_pipe( + new LoopBackPacketPipe(packet_receiver)); + if (packet_pipe_) { + packet_pipe_->AppendToPipe(loopback_pipe.Pass()); + } else { + packet_pipe_ = loopback_pipe.Pass(); + } } virtual bool SendPacket(transport::PacketRef packet, @@ -193,7 +222,7 @@ class LoopBackTransport : public transport::PacketSender { // Reset the is_reference bit in the cast header. (*packet_copy)[kCommonRtpHeaderLength] &= kCastReferenceFrameIdBitReset; } - packet_receiver_.Run(packet_copy.Pass()); + packet_pipe_->Send(packet_copy.Pass()); return true; } @@ -205,12 +234,18 @@ class LoopBackTransport : public transport::PacketSender { void AlwaysResetReferenceFrameId() { reset_reference_frame_id_ = true; } + void SetPacketPipe(scoped_ptr<test::PacketPipe> pipe) { + // Append the loopback pipe to the end. + pipe->AppendToPipe(packet_pipe_.Pass()); + packet_pipe_ = pipe.Pass(); + } + private: - transport::PacketReceiverCallback packet_receiver_; bool send_packets_; bool drop_packets_belonging_to_odd_frames_; bool reset_reference_frame_id_; scoped_refptr<CastEnvironment> cast_environment_; + scoped_ptr<test::PacketPipe> packet_pipe_; }; // Class that verifies the audio frames coming out of the receiver. @@ -436,7 +471,8 @@ class End2EndTest : public ::testing::Test { &event_subscriber_sender_); } - void Configure(transport::AudioCodec audio_codec, + void Configure(transport::VideoCodec video_codec, + transport::AudioCodec audio_codec, int audio_sampling_frequency, bool external_audio_decoder, int max_number_of_video_buffers_used) { @@ -476,7 +512,7 @@ class End2EndTest : public ::testing::Test { video_sender_config_.max_frame_rate = 30; video_sender_config_.max_number_of_video_buffers_used = max_number_of_video_buffers_used; - video_sender_config_.codec = transport::kVp8; + video_sender_config_.codec = video_codec; video_receiver_config_.feedback_ssrc = video_sender_config_.incoming_feedback_ssrc; @@ -601,6 +637,11 @@ class End2EndTest : public ::testing::Test { video_frame_input_->InsertRawVideoFrame(video_frame, capture_time); } + void SendFakeVideoFrame(const base::TimeTicks& capture_time) { + video_frame_input_->InsertRawVideoFrame( + media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2)), capture_time); + } + void RunTasks(int during_ms) { for (int i = 0; i < during_ms; ++i) { // Call process the timers every 1 ms. @@ -664,7 +705,7 @@ class End2EndTest : public ::testing::Test { }; TEST_F(End2EndTest, LoopNoLossPcm16) { - Configure(transport::kPcm16, 32000, false, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, false, 1); // Reduce video resolution to allow processing multiple frames within a // reasonable time frame. video_sender_config_.width = kVideoQcifWidth; @@ -717,7 +758,7 @@ TEST_F(End2EndTest, LoopNoLossPcm16) { // This tests our external decoder interface for Audio. // Audio test without packet loss using raw PCM 16 audio "codec"; TEST_F(End2EndTest, LoopNoLossPcm16ExternalDecoder) { - Configure(transport::kPcm16, 32000, true, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, true, 1); Create(); const int kNumIterations = 10; @@ -735,7 +776,8 @@ TEST_F(End2EndTest, LoopNoLossPcm16ExternalDecoder) { // This tests our Opus audio codec without video. TEST_F(End2EndTest, LoopNoLossOpus) { - Configure(transport::kOpus, kDefaultAudioSamplingRate, false, 1); + Configure(transport::kVp8, transport::kOpus, kDefaultAudioSamplingRate, + false, 1); Create(); const int kNumIterations = 300; @@ -761,7 +803,8 @@ TEST_F(End2EndTest, LoopNoLossOpus) { // in audio_receiver.cc for likely cause(s) of this bug. // http://crbug.com/356942 TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) { - Configure(transport::kPcm16, kDefaultAudioSamplingRate, false, 1); + Configure(transport::kVp8, transport::kPcm16, kDefaultAudioSamplingRate, + false, 1); Create(); int video_start = kVideoStart; @@ -847,7 +890,8 @@ TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) { // This tests a network glitch lasting for 10 video frames. // Flaky. See crbug.com/351596. TEST_F(End2EndTest, DISABLED_GlitchWith3Buffers) { - Configure(transport::kOpus, kDefaultAudioSamplingRate, false, 3); + Configure(transport::kVp8, transport::kOpus, kDefaultAudioSamplingRate, + false, 3); video_sender_config_.rtp_config.max_delay_ms = 67; video_receiver_config_.rtp_max_delay_ms = 67; Create(); @@ -909,7 +953,8 @@ TEST_F(End2EndTest, DISABLED_GlitchWith3Buffers) { // Disabled due to flakiness and crashiness. http://crbug.com/360951 TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) { - Configure(transport::kOpus, kDefaultAudioSamplingRate, false, 3); + Configure(transport::kVp8, transport::kOpus, kDefaultAudioSamplingRate, false, + 3); video_sender_config_.rtp_config.max_delay_ms = 67; video_receiver_config_.rtp_max_delay_ms = 67; Create(); @@ -946,7 +991,8 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) { } TEST_F(End2EndTest, ResetReferenceFrameId) { - Configure(transport::kOpus, kDefaultAudioSamplingRate, false, 3); + Configure(transport::kVp8, transport::kOpus, kDefaultAudioSamplingRate, + false, 3); video_sender_config_.rtp_config.max_delay_ms = 67; video_receiver_config_.rtp_max_delay_ms = 67; Create(); @@ -977,7 +1023,7 @@ TEST_F(End2EndTest, ResetReferenceFrameId) { } TEST_F(End2EndTest, CryptoVideo) { - Configure(transport::kPcm16, 32000, false, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, false, 1); transport_video_config_.base.aes_iv_mask = ConvertFromBase16String("1234567890abcdeffedcba0987654321"); @@ -1013,7 +1059,7 @@ TEST_F(End2EndTest, CryptoVideo) { } TEST_F(End2EndTest, CryptoAudio) { - Configure(transport::kPcm16, 32000, false, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, false, 1); transport_audio_config_.base.aes_iv_mask = ConvertFromBase16String("abcdeffedcba12345678900987654321"); @@ -1040,7 +1086,7 @@ TEST_F(End2EndTest, CryptoAudio) { // Video test without packet loss - tests the logging aspects of the end2end, // but is basically equivalent to LoopNoLossPcm16. TEST_F(End2EndTest, VideoLogging) { - Configure(transport::kPcm16, 32000, false, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, false, 1); Create(); int video_start = kVideoStart; @@ -1166,7 +1212,7 @@ TEST_F(End2EndTest, VideoLogging) { // Audio test without packet loss - tests the logging aspects of the end2end, // but is basically equivalent to LoopNoLossPcm16. TEST_F(End2EndTest, AudioLogging) { - Configure(transport::kPcm16, 32000, false, 1); + Configure(transport::kVp8, transport::kPcm16, 32000, false, 1); Create(); int audio_diff = kFrameTimerMs; @@ -1257,6 +1303,22 @@ TEST_F(End2EndTest, AudioLogging) { EXPECT_EQ(total_event_count_for_frame, expected_event_count_for_frame); } +TEST_F(End2EndTest, BasicFakeSoftwareVideo) { + Configure(transport::kFakeSoftwareVideo, transport::kPcm16, 32000, false, 1); + Create(); + + int frames_counter = 0; + int received_counter = 0; + for (; frames_counter < 1000; ++frames_counter) { + SendFakeVideoFrame(testing_clock_sender_->NowTicks()); + frame_receiver_->GetRawVideoFrame( + base::Bind(&CountVideoFrame, &received_counter)); + RunTasks(kFrameTimerMs); + } + RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline. + EXPECT_EQ(1000, received_counter); +} + // TODO(pwestin): Add repeatable packet loss test. // TODO(pwestin): Add test for misaligned send get calls. // TODO(pwestin): Add more tests that does not resample. diff --git a/media/cast/test/utility/udp_proxy.cc b/media/cast/test/utility/udp_proxy.cc index 5936ef5..eed3744 100644 --- a/media/cast/test/utility/udp_proxy.cc +++ b/media/cast/test/utility/udp_proxy.cc @@ -21,10 +21,11 @@ const size_t kMaxPacketSize = 65536; PacketPipe::PacketPipe() {} PacketPipe::~PacketPipe() {} -void PacketPipe::InitOnIOThread() { - task_runner_ = base::MessageLoopProxy::current(); +void PacketPipe::InitOnIOThread( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { + task_runner_ = task_runner; if (pipe_) { - pipe_->InitOnIOThread(); + pipe_->InitOnIOThread(task_runner); } } void PacketPipe::AppendToPipe(scoped_ptr<PacketPipe> pipe) { @@ -181,8 +182,9 @@ class RandomSortedDelay : public PacketPipe { Schedule(); } } - virtual void InitOnIOThread() OVERRIDE { - PacketPipe::InitOnIOThread(); + virtual void InitOnIOThread( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) OVERRIDE { + PacketPipe::InitOnIOThread(task_runner); // As we start the stream, assume that we are in a random // place between two extra delays, thus multiplier = 1.0; ScheduleExtraDelay(1.0); @@ -261,8 +263,9 @@ class NetworkGlitchPipe : public PacketPipe { max_outage_time_(average_outage_time * 2), weak_factory_(this) {} - virtual void InitOnIOThread() OVERRIDE { - PacketPipe::InitOnIOThread(); + virtual void InitOnIOThread( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) OVERRIDE { + PacketPipe::InitOnIOThread(task_runner); Flip(); } @@ -444,8 +447,8 @@ class UDPProxyImpl : public UDPProxy { BuildPipe(&to_dest_pipe_, new PacketSender(socket_.get(), &destination_)); BuildPipe(&from_dest_pipe_, new PacketSender(socket_.get(), &return_address_)); - to_dest_pipe_->InitOnIOThread(); - from_dest_pipe_->InitOnIOThread(); + to_dest_pipe_->InitOnIOThread(base::MessageLoopProxy::current()); + from_dest_pipe_->InitOnIOThread(base::MessageLoopProxy::current()); VLOG(0) << "From:" << local_port_.ToString(); VLOG(0) << "To:" << destination_.ToString(); @@ -463,8 +466,7 @@ class UDPProxyImpl : public UDPProxy { stop_event->Signal(); } - void ProcessPacket(scoped_refptr<net::IOBuffer> recv_buf, - int len) { + void ProcessPacket(scoped_refptr<net::IOBuffer> recv_buf, int len) { DCHECK_NE(len, net::ERR_IO_PENDING); VLOG(1) << "Got packet, len = " << len; if (len < 0) { @@ -481,8 +483,7 @@ class UDPProxyImpl : public UDPProxy { } } - void ReadCallback(scoped_refptr<net::IOBuffer> recv_buf, - int len) { + void ReadCallback(scoped_refptr<net::IOBuffer> recv_buf, int len) { ProcessPacket(recv_buf, len); PollRead(); } diff --git a/media/cast/test/utility/udp_proxy.h b/media/cast/test/utility/udp_proxy.h index 322572a..41c686e 100644 --- a/media/cast/test/utility/udp_proxy.h +++ b/media/cast/test/utility/udp_proxy.h @@ -27,7 +27,9 @@ class PacketPipe { PacketPipe(); virtual ~PacketPipe(); virtual void Send(scoped_ptr<transport::Packet> packet) = 0; - virtual void InitOnIOThread(); + // Allows injection of fake test runner for testing. + virtual void InitOnIOThread( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); virtual void AppendToPipe(scoped_ptr<PacketPipe> pipe); protected: scoped_ptr<PacketPipe> pipe_; @@ -77,17 +79,15 @@ scoped_ptr<PacketPipe> NewRandomUnsortedDelay(double delay); // packet is asically |min_delay| + random( |random_delay| ) // However, every now and then a delay of |big_delay| will be // inserted (roughly every |seconds_between_big_delay| seconds). -scoped_ptr<PacketPipe> NewRandomSortedDelay( - double random_delay, - double big_delay, - double seconds_between_big_delay); +scoped_ptr<PacketPipe> NewRandomSortedDelay(double random_delay, + double big_delay, + double seconds_between_big_delay); // This PacketPipe emulates network outages. It basically waits // for 0-2*|average_work_time| seconds, then kills the network for // 0-|2*average_outage_time| seconds. Then it starts over again. -scoped_ptr<PacketPipe> NewNetworkGlitchPipe( - double average_work_time, - double average_outage_time); +scoped_ptr<PacketPipe> NewNetworkGlitchPipe(double average_work_time, + double average_outage_time); // This method builds a stack of PacketPipes to emulate a reasonably // good wifi network. ~5mbit, 1% packet loss, ~3ms latency. diff --git a/media/cast/test/utility/udp_proxy_main.cc b/media/cast/test/utility/udp_proxy_main.cc index 4e196d7..58019b4 100644 --- a/media/cast/test/utility/udp_proxy_main.cc +++ b/media/cast/test/utility/udp_proxy_main.cc @@ -15,7 +15,7 @@ int main(int argc, char** argv) { if (argc < 5) { fprintf(stderr, - "Usage: udp_proxy <localport> <remotehost> <remoteport> <type>\n", + "Usage: udp_proxy <localport> <remotehost> <remoteport> <type>\n" "Where type is one of: perfect, wifi, evil\n"); exit(1); } diff --git a/media/cast/transport/cast_transport_config.h b/media/cast/transport/cast_transport_config.h index a3887b5..cddd6d1 100644 --- a/media/cast/transport/cast_transport_config.h +++ b/media/cast/transport/cast_transport_config.h @@ -24,13 +24,10 @@ enum RtcpMode { kRtcpReducedSize, // Reduced-size RTCP mode is described by RFC 5506. }; -enum VideoCodec { - kVp8, - kH264, - kVideoCodecLast = kH264 -}; +enum VideoCodec { kFakeSoftwareVideo, kVp8, kH264, kVideoCodecLast = kH264 }; enum AudioCodec { + kFakeSoftwareAudio, kOpus, kPcm16, kExternalAudio, diff --git a/media/cast/video_receiver/codecs/vp8/vp8_decoder.h b/media/cast/video_receiver/codecs/vp8/vp8_decoder.h new file mode 100644 index 0000000..62ee8ad --- /dev/null +++ b/media/cast/video_receiver/codecs/vp8/vp8_decoder.h @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +#ifndef MEDIA_CAST_VIDEO_RECEVIER_CODECS_VP8_VP8_DECODER_H_ +#define MEDIA_CAST_VIDEO_RECEVIER_CODECS_VP8_VP8_DECODER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "media/cast/cast_config.h" +#include "media/cast/cast_environment.h" +#include "media/cast/cast_receiver.h" +#include "media/cast/video_receiver/software_video_decoder.h" +#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" + +typedef struct vpx_codec_ctx vpx_dec_ctx_t; + +// TODO(mikhal): Look into reusing VpxVideoDecoder. +namespace media { +namespace cast { + +class Vp8Decoder : public SoftwareVideoDecoder { + public: + explicit Vp8Decoder(scoped_refptr<CastEnvironment> cast_environment); + virtual ~Vp8Decoder(); + + // SoftwareVideoDecoder implementations. + virtual bool Decode(const transport::EncodedVideoFrame* encoded_frame, + const base::TimeTicks render_time, + const VideoFrameDecodedCallback& frame_decoded_cb) + OVERRIDE; + + private: + // Initialize the decoder. + void InitDecoder(); + scoped_ptr<vpx_dec_ctx_t> decoder_; + scoped_refptr<CastEnvironment> cast_environment_; + + DISALLOW_COPY_AND_ASSIGN(Vp8Decoder); +}; + +} // namespace cast +} // namespace media + +#endif // MEDIA_CAST_VIDEO_RECEVIER_CODECS_VP8_VP8_DECODER_H_ diff --git a/media/cast/video_receiver/video_decoder.cc b/media/cast/video_receiver/video_decoder.cc index 0964c07..9f16749 100644 --- a/media/cast/video_receiver/video_decoder.cc +++ b/media/cast/video_receiver/video_decoder.cc @@ -6,9 +6,11 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/json/json_reader.h" #include "base/location.h" #include "base/logging.h" #include "base/stl_util.h" +#include "base/values.h" #include "media/base/video_util.h" #include "media/cast/cast_defines.h" #include "media/cast/cast_environment.h" @@ -175,11 +177,55 @@ class VideoDecoder::Vp8Impl : public VideoDecoder::ImplBase { DISALLOW_COPY_AND_ASSIGN(Vp8Impl); }; +#ifndef OFFICIAL_BUILD +// A fake video decoder that always output 2x2 black frames. +class VideoDecoder::FakeImpl : public VideoDecoder::ImplBase { + public: + explicit FakeImpl(const scoped_refptr<CastEnvironment>& cast_environment) + : ImplBase(cast_environment, transport::kFakeSoftwareVideo), + last_decoded_id_(-1) { + if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED) + return; + ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED; + } + + private: + virtual ~FakeImpl() {} + + virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE { + base::JSONReader reader; + scoped_ptr<base::Value> values(reader.Read( + base::StringPiece(reinterpret_cast<char*>(data), len))); + base::DictionaryValue* dict = NULL; + values->GetAsDictionary(&dict); + + bool key = false; + int id = 0; + int ref = 0; + dict->GetBoolean("key", &key); + dict->GetInteger("id", &id); + dict->GetInteger("ref", &ref); + DCHECK(id == last_decoded_id_ + 1); + last_decoded_id_ = id; + return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2)); + } + + int last_decoded_id_; + + DISALLOW_COPY_AND_ASSIGN(FakeImpl); +}; +#endif + VideoDecoder::VideoDecoder( const scoped_refptr<CastEnvironment>& cast_environment, const VideoReceiverConfig& video_config) : cast_environment_(cast_environment) { switch (video_config.codec) { +#ifndef OFFICIAL_BUILD + case transport::kFakeSoftwareVideo: + impl_ = new FakeImpl(cast_environment); + break; +#endif case transport::kVp8: impl_ = new Vp8Impl(cast_environment); break; diff --git a/media/cast/video_receiver/video_decoder.h b/media/cast/video_receiver/video_decoder.h index e6bd91b..7f0db54 100644 --- a/media/cast/video_receiver/video_decoder.h +++ b/media/cast/video_receiver/video_decoder.h @@ -47,6 +47,7 @@ class VideoDecoder { const DecodeFrameCallback& callback); private: + class FakeImpl; class ImplBase; class Vp8Impl; diff --git a/media/cast/video_sender/codecs/vp8/vp8_encoder.h b/media/cast/video_sender/codecs/vp8/vp8_encoder.h index 396746d..aff6215 100644 --- a/media/cast/video_sender/codecs/vp8/vp8_encoder.h +++ b/media/cast/video_sender/codecs/vp8/vp8_encoder.h @@ -10,6 +10,7 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "media/cast/cast_config.h" +#include "media/cast/video_sender/software_video_encoder.h" #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" namespace media { @@ -24,27 +25,27 @@ namespace cast { const int kNumberOfVp8VideoBuffers = 3; -class Vp8Encoder { +class Vp8Encoder : public SoftwareVideoEncoder { public: Vp8Encoder(const VideoSenderConfig& video_config, uint8 max_unacked_frames); - ~Vp8Encoder(); + virtual ~Vp8Encoder(); // Initialize the encoder before Encode() can be called. This method // must be called on the thread that Encode() is called. - void Initialize(); + virtual void Initialize() OVERRIDE; // Encode a raw image (as a part of a video stream). - bool Encode(const scoped_refptr<media::VideoFrame>& video_frame, - transport::EncodedVideoFrame* encoded_image); + virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame, + transport::EncodedVideoFrame* encoded_image) OVERRIDE; // Update the encoder with a new target bit rate. - void UpdateRates(uint32 new_bitrate); + virtual void UpdateRates(uint32 new_bitrate) OVERRIDE; // Set the next frame to be a key frame. - void GenerateKeyFrame(); + virtual void GenerateKeyFrame() OVERRIDE; - void LatestFrameIdToReference(uint32 frame_id); + virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE; private: enum Vp8Buffers { diff --git a/media/cast/video_sender/external_video_encoder.cc b/media/cast/video_sender/external_video_encoder.cc index acae1a4..bbb67e0 100644 --- a/media/cast/video_sender/external_video_encoder.cc +++ b/media/cast/video_sender/external_video_encoder.cc @@ -104,6 +104,9 @@ class LocalVideoEncodeAcceleratorClient case transport::kH264: output_profile = media::H264PROFILE_MAIN; break; + case transport::kFakeSoftwareVideo: + NOTREACHED() << "Fake software video encoder cannot be external"; + break; } codec_ = video_config.codec; max_frame_rate_ = video_config.max_frame_rate; diff --git a/media/cast/video_sender/fake_software_video_encoder.cc b/media/cast/video_sender/fake_software_video_encoder.cc new file mode 100644 index 0000000..0df0d6e --- /dev/null +++ b/media/cast/video_sender/fake_software_video_encoder.cc @@ -0,0 +1,59 @@ +// Copyright 2014 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 "media/cast/video_sender/fake_software_video_encoder.h" + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "media/cast/transport/cast_transport_config.h" + +#ifndef OFFICIAL_BUILD + +namespace media { +namespace cast { + +FakeSoftwareVideoEncoder::FakeSoftwareVideoEncoder() + : next_frame_is_key_(true), + frame_id_(0), + frame_id_to_reference_(0) { +} + +FakeSoftwareVideoEncoder::~FakeSoftwareVideoEncoder() {} + +void FakeSoftwareVideoEncoder::Initialize() {} + +bool FakeSoftwareVideoEncoder::Encode( + const scoped_refptr<media::VideoFrame>& video_frame, + transport::EncodedVideoFrame* encoded_image) { + encoded_image->codec = transport::kFakeSoftwareVideo; + encoded_image->key_frame = next_frame_is_key_; + next_frame_is_key_ = false; + encoded_image->frame_id = frame_id_++; + encoded_image->last_referenced_frame_id = frame_id_to_reference_; + + base::DictionaryValue values; + values.Set("key", base::Value::CreateBooleanValue(encoded_image->key_frame)); + values.Set("id", base::Value::CreateIntegerValue(encoded_image->frame_id)); + values.Set("ref", base::Value::CreateIntegerValue( + encoded_image->last_referenced_frame_id)); + base::JSONWriter::Write(&values, &encoded_image->data); + return true; +} + +void FakeSoftwareVideoEncoder::UpdateRates(uint32 new_bitrate) { + // TODO(hclam): Implement bitrate control. +} + +void FakeSoftwareVideoEncoder::GenerateKeyFrame() { + next_frame_is_key_ = true; +} + +void FakeSoftwareVideoEncoder::LatestFrameIdToReference(uint32 frame_id) { + frame_id_to_reference_ = frame_id; +} + +} // namespace cast +} // namespace media + +#endif diff --git a/media/cast/video_sender/fake_software_video_encoder.h b/media/cast/video_sender/fake_software_video_encoder.h new file mode 100644 index 0000000..bcc5ed0 --- /dev/null +++ b/media/cast/video_sender/fake_software_video_encoder.h @@ -0,0 +1,35 @@ +// Copyright 2014 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. + +#ifndef MEDIA_CAST_VIDEO_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_ +#define MEDIA_CAST_VIDEO_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_ + +#include "media/cast/video_sender/software_video_encoder.h" + +namespace media { +namespace cast { + +class FakeSoftwareVideoEncoder : public SoftwareVideoEncoder { + public: + FakeSoftwareVideoEncoder(); + virtual ~FakeSoftwareVideoEncoder(); + + // SoftwareVideoEncoder implementations. + virtual void Initialize() OVERRIDE; + virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame, + transport::EncodedVideoFrame* encoded_image) OVERRIDE; + virtual void UpdateRates(uint32 new_bitrate) OVERRIDE; + virtual void GenerateKeyFrame() OVERRIDE; + virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE; + + private: + bool next_frame_is_key_; + uint32 frame_id_; + uint32 frame_id_to_reference_; +}; + +} // namespace cast +} // namespace media + +#endif // MEDIA_CAST_VIDEO_SENDER_FAKE_SOFTWARE_VIDEO_ENCODER_H_ diff --git a/media/cast/video_sender/software_video_encoder.h b/media/cast/video_sender/software_video_encoder.h new file mode 100644 index 0000000..3d63f20 --- /dev/null +++ b/media/cast/video_sender/software_video_encoder.h @@ -0,0 +1,46 @@ +// Copyright 2014 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. + +#ifndef MEDIA_CAST_VIDEO_SENDER_SOFTWARE_VIDEO_ENCODER_H_ +#define MEDIA_CAST_VIDEO_SENDER_SOFTWARE_VIDEO_ENCODER_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" + +namespace media { +class VideoFrame; +} + +namespace media { +namespace cast { +namespace transport { +struct EncodedVideoFrame; +} // namespace transport + +class SoftwareVideoEncoder { + public: + virtual ~SoftwareVideoEncoder() {} + + // Initialize the encoder before Encode() can be called. This method + // must be called on the thread that Encode() is called. + virtual void Initialize() = 0; + + // Encode a raw image (as a part of a video stream). + virtual bool Encode(const scoped_refptr<media::VideoFrame>& video_frame, + transport::EncodedVideoFrame* encoded_image) = 0; + + // Update the encoder with a new target bit rate. + virtual void UpdateRates(uint32 new_bitrate) = 0; + + // Set the next frame to be a key frame. + virtual void GenerateKeyFrame() = 0; + + // Set the last frame to reference. + virtual void LatestFrameIdToReference(uint32 frame_id) = 0; +}; + +} // namespace cast +} // namespace media + +#endif // MEDIA_CAST_VIDEO_SENDER_SOFTWARE_VIDEO_ENCODER_H_ diff --git a/media/cast/video_sender/video_encoder_impl.cc b/media/cast/video_sender/video_encoder_impl.cc index 854f8c4..0ee7ee4 100644 --- a/media/cast/video_sender/video_encoder_impl.cc +++ b/media/cast/video_sender/video_encoder_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/cast/video_sender/video_encoder.h" +#include "media/cast/video_sender/video_encoder_impl.h" #include "base/bind.h" #include "base/bind_helpers.h" @@ -11,7 +11,8 @@ #include "base/message_loop/message_loop.h" #include "media/base/video_frame.h" #include "media/cast/cast_defines.h" -#include "media/cast/video_sender/video_encoder_impl.h" +#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h" +#include "media/cast/video_sender/fake_software_video_encoder.h" namespace media { namespace cast { @@ -20,31 +21,31 @@ namespace { typedef base::Callback<void(Vp8Encoder*)> PassEncoderCallback; -void InitializeVp8EncoderOnEncoderThread( +void InitializeEncoderOnEncoderThread( const scoped_refptr<CastEnvironment>& environment, - Vp8Encoder* vp8_encoder) { + SoftwareVideoEncoder* encoder) { DCHECK(environment->CurrentlyOn(CastEnvironment::VIDEO)); - vp8_encoder->Initialize(); + encoder->Initialize(); } void EncodeVideoFrameOnEncoderThread( scoped_refptr<CastEnvironment> environment, - Vp8Encoder* vp8_encoder, + SoftwareVideoEncoder* encoder, const scoped_refptr<media::VideoFrame>& video_frame, const base::TimeTicks& capture_time, const VideoEncoderImpl::CodecDynamicConfig& dynamic_config, const VideoEncoderImpl::FrameEncodedCallback& frame_encoded_callback) { DCHECK(environment->CurrentlyOn(CastEnvironment::VIDEO)); if (dynamic_config.key_frame_requested) { - vp8_encoder->GenerateKeyFrame(); + encoder->GenerateKeyFrame(); } - vp8_encoder->LatestFrameIdToReference( + encoder->LatestFrameIdToReference( dynamic_config.latest_frame_id_to_reference); - vp8_encoder->UpdateRates(dynamic_config.bit_rate); + encoder->UpdateRates(dynamic_config.bit_rate); scoped_ptr<transport::EncodedVideoFrame> encoded_frame( new transport::EncodedVideoFrame()); - bool retval = vp8_encoder->Encode(video_frame, encoded_frame.get()); + bool retval = encoder->Encode(video_frame, encoded_frame.get()); encoded_frame->rtp_timestamp = transport::GetVideoRtpTimestamp(capture_time); @@ -73,12 +74,16 @@ VideoEncoderImpl::VideoEncoderImpl( skip_next_frame_(false), skip_count_(0) { if (video_config.codec == transport::kVp8) { - vp8_encoder_.reset(new Vp8Encoder(video_config, max_unacked_frames)); + encoder_.reset(new Vp8Encoder(video_config, max_unacked_frames)); cast_environment_->PostTask(CastEnvironment::VIDEO, FROM_HERE, - base::Bind(&InitializeVp8EncoderOnEncoderThread, + base::Bind(&InitializeEncoderOnEncoderThread, cast_environment, - vp8_encoder_.get())); + encoder_.get())); +#ifndef OFFICIAL_BUILD + } else if (video_config.codec == transport::kFakeSoftwareVideo) { + encoder_.reset(new FakeSoftwareVideoEncoder()); +#endif } else { DCHECK(false) << "Invalid config"; // Codec not supported. } @@ -90,11 +95,12 @@ VideoEncoderImpl::VideoEncoderImpl( VideoEncoderImpl::~VideoEncoderImpl() { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (vp8_encoder_) { + if (encoder_) { cast_environment_->PostTask( CastEnvironment::VIDEO, FROM_HERE, - base::Bind(&base::DeletePointer<Vp8Encoder>, vp8_encoder_.release())); + base::Bind(&base::DeletePointer<SoftwareVideoEncoder>, + encoder_.release())); } } @@ -103,9 +109,6 @@ bool VideoEncoderImpl::EncodeVideoFrame( const base::TimeTicks& capture_time, const FrameEncodedCallback& frame_encoded_callback) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - if (video_config_.codec != transport::kVp8) - return false; - if (skip_next_frame_) { ++skip_count_; return false; @@ -121,7 +124,7 @@ bool VideoEncoderImpl::EncodeVideoFrame( FROM_HERE, base::Bind(&EncodeVideoFrameOnEncoderThread, cast_environment_, - vp8_encoder_.get(), + encoder_.get(), video_frame, capture_time, dynamic_config_, diff --git a/media/cast/video_sender/video_encoder_impl.h b/media/cast/video_sender/video_encoder_impl.h index 887a779..4bc0a83 100644 --- a/media/cast/video_sender/video_encoder_impl.h +++ b/media/cast/video_sender/video_encoder_impl.h @@ -8,7 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "media/cast/cast_config.h" #include "media/cast/cast_environment.h" -#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h" +#include "media/cast/video_sender/software_video_encoder.h" #include "media/cast/video_sender/video_encoder.h" namespace media { @@ -65,7 +65,7 @@ class VideoEncoderImpl : public VideoEncoder { // dereferenced on the main thread. We manage the lifetime of this member // manually because it needs to be initialize, used and destroyed on the // video encoder thread and video encoder thread can out-live the main thread. - scoped_ptr<Vp8Encoder> vp8_encoder_; + scoped_ptr<SoftwareVideoEncoder> encoder_; DISALLOW_COPY_AND_ASSIGN(VideoEncoderImpl); }; diff --git a/media/cast/video_sender/video_sender.gypi b/media/cast/video_sender/video_sender.gypi index 1845b81..6e12615 100644 --- a/media/cast/video_sender/video_sender.gypi +++ b/media/cast/video_sender/video_sender.gypi @@ -16,6 +16,9 @@ 'sources': [ 'external_video_encoder.h', 'external_video_encoder.cc', + 'fake_software_video_encoder.h', + 'fake_software_video_encoder.cc', + 'software_video_encoder.h', 'video_encoder.h', 'video_encoder_impl.h', 'video_encoder_impl.cc', |