summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/cast/test/end2end_unittest.cc94
-rw-r--r--media/cast/test/utility/udp_proxy.cc27
-rw-r--r--media/cast/test/utility/udp_proxy.h16
-rw-r--r--media/cast/test/utility/udp_proxy_main.cc2
-rw-r--r--media/cast/transport/cast_transport_config.h7
-rw-r--r--media/cast/video_receiver/codecs/vp8/vp8_decoder.h45
-rw-r--r--media/cast/video_receiver/video_decoder.cc46
-rw-r--r--media/cast/video_receiver/video_decoder.h1
-rw-r--r--media/cast/video_sender/codecs/vp8/vp8_encoder.h17
-rw-r--r--media/cast/video_sender/external_video_encoder.cc3
-rw-r--r--media/cast/video_sender/fake_software_video_encoder.cc59
-rw-r--r--media/cast/video_sender/fake_software_video_encoder.h35
-rw-r--r--media/cast/video_sender/software_video_encoder.h46
-rw-r--r--media/cast/video_sender/video_encoder_impl.cc41
-rw-r--r--media/cast/video_sender/video_encoder_impl.h4
-rw-r--r--media/cast/video_sender/video_sender.gypi3
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',