Commit 0b15c8c3 authored by pwestin@google.com's avatar pwestin@google.com

Cast: Adding support for GPU accelerated encode

Removed the old VideoEncoderController interface and
made video_encoder a pure virtual class that
ExternalVideoEncoder and VideoEncoderImpl implements. 
This CL also remove the possibility to send in
pre-encoded video frames.

BUG=325998

Review URL: https://codereview.chromium.org/116623002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243589 0039d316-1c4b-4281-b951-d872f2087c98
parent 7065c505
...@@ -84,6 +84,12 @@ ...@@ -84,6 +84,12 @@
'test/crypto_utility.h', 'test/crypto_utility.h',
'test/encode_decode_test.cc', 'test/encode_decode_test.cc',
'test/end2end_unittest.cc', 'test/end2end_unittest.cc',
'test/fake_gpu_video_accelerator_factories.cc',
'test/fake_gpu_video_accelerator_factories.h',
'test/fake_task_runner.cc',
'test/fake_task_runner.h',
'test/fake_video_encode_accelerator.cc',
'test/fake_video_encode_accelerator.h',
'transport/pacing/mock_paced_packet_sender.cc', 'transport/pacing/mock_paced_packet_sender.cc',
'transport/pacing/mock_paced_packet_sender.h', 'transport/pacing/mock_paced_packet_sender.h',
'transport/pacing/paced_sender_unittest.cc', 'transport/pacing/paced_sender_unittest.cc',
...@@ -93,9 +99,8 @@ ...@@ -93,9 +99,8 @@
'transport/rtp_sender/rtp_packetizer/test/rtp_header_parser.h', 'transport/rtp_sender/rtp_packetizer/test/rtp_header_parser.h',
'video_receiver/video_decoder_unittest.cc', 'video_receiver/video_decoder_unittest.cc',
'video_receiver/video_receiver_unittest.cc', 'video_receiver/video_receiver_unittest.cc',
'video_sender/mock_video_encoder_controller.cc', 'video_sender/external_video_encoder_unittest.cc',
'video_sender/mock_video_encoder_controller.h', 'video_sender/video_encoder_impl_unittest.cc',
'video_sender/video_encoder_unittest.cc',
'video_sender/video_sender_unittest.cc', 'video_sender/video_sender_unittest.cc',
], # source ], # source
}, },
......
...@@ -25,7 +25,6 @@ enum RtcpMode { ...@@ -25,7 +25,6 @@ enum RtcpMode {
enum VideoCodec { enum VideoCodec {
kVp8, kVp8,
kH264, kH264,
kExternalVideo,
}; };
enum AudioCodec { enum AudioCodec {
...@@ -205,28 +204,6 @@ class PacketReceiver : public base::RefCountedThreadSafe<PacketReceiver> { ...@@ -205,28 +204,6 @@ class PacketReceiver : public base::RefCountedThreadSafe<PacketReceiver> {
friend class base::RefCountedThreadSafe<PacketReceiver>; friend class base::RefCountedThreadSafe<PacketReceiver>;
}; };
class VideoEncoderController {
public:
// Inform the encoder about the new target bit rate.
virtual void SetBitRate(int new_bit_rate) = 0;
// Inform the encoder to not encode the next frame.
// Note: this setting is sticky and should last until called with false.
virtual void SkipNextFrame(bool skip_next_frame) = 0;
// Inform the encoder to encode the next frame as a key frame.
virtual void GenerateKeyFrame() = 0;
// Inform the encoder to only reference frames older or equal to frame_id;
virtual void LatestFrameIdToReference(uint32 frame_id) = 0;
// Query the codec about how many frames it has skipped due to slow ACK.
virtual int NumberOfSkippedFrames() const = 0;
protected:
virtual ~VideoEncoderController() {}
};
} // namespace cast } // namespace cast
} // namespace media } // namespace media
......
...@@ -32,8 +32,8 @@ CastEnvironment::CastEnvironment( ...@@ -32,8 +32,8 @@ CastEnvironment::CastEnvironment(
CastEnvironment::~CastEnvironment() {} CastEnvironment::~CastEnvironment() {}
bool CastEnvironment::PostTask(ThreadId identifier, bool CastEnvironment::PostTask(ThreadId identifier,
const tracked_objects::Location& from_here, const tracked_objects::Location& from_here,
const base::Closure& task) { const base::Closure& task) {
scoped_refptr<TaskRunner> task_runner = scoped_refptr<TaskRunner> task_runner =
GetMessageTaskRunnerForThread(identifier); GetMessageTaskRunnerForThread(identifier);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "media/cast/cast_config.h" #include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/filters/gpu_video_accelerator_factories.h"
namespace media { namespace media {
class AudioBus; class AudioBus;
...@@ -38,14 +39,6 @@ class FrameInput : public base::RefCountedThreadSafe<FrameInput> { ...@@ -38,14 +39,6 @@ class FrameInput : public base::RefCountedThreadSafe<FrameInput> {
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time) = 0; const base::TimeTicks& capture_time) = 0;
// The video_frame must be valid until the callback is called.
// The callback is called from the main cast thread as soon as
// the cast sender is done with the frame; it does not mean that the encoded
// frame has been sent out.
virtual void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
const base::TimeTicks& capture_time,
const base::Closure callback) = 0;
// The |audio_bus| must be valid until the |done_callback| is called. // The |audio_bus| must be valid until the |done_callback| is called.
// The callback is called from the main cast thread as soon as the encoder is // The callback is called from the main cast thread as soon as the encoder is
// done with |audio_bus|; it does not mean that the encoded data has been // done with |audio_bus|; it does not mean that the encoded data has been
...@@ -78,8 +71,9 @@ class CastSender { ...@@ -78,8 +71,9 @@ class CastSender {
scoped_refptr<CastEnvironment> cast_environment, scoped_refptr<CastEnvironment> cast_environment,
const AudioSenderConfig& audio_config, const AudioSenderConfig& audio_config,
const VideoSenderConfig& video_config, const VideoSenderConfig& video_config,
VideoEncoderController* const video_encoder_controller, const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
PacketSender* const packet_sender); PacketSender* const packet_sender);
// TODO(pwestin): Add callback for status messages; initialized, errors etc.
virtual ~CastSender() {} virtual ~CastSender() {}
......
...@@ -32,14 +32,6 @@ class LocalFrameInput : public FrameInput { ...@@ -32,14 +32,6 @@ class LocalFrameInput : public FrameInput {
video_frame, capture_time)); video_frame, capture_time));
} }
virtual void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
const base::TimeTicks& capture_time,
const base::Closure callback) OVERRIDE {
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(&VideoSender::InsertCodedVideoFrame, video_sender_,
video_frame, capture_time, callback));
}
virtual void InsertAudio(const AudioBus* audio_bus, virtual void InsertAudio(const AudioBus* audio_bus,
const base::TimeTicks& recorded_time, const base::TimeTicks& recorded_time,
const base::Closure& done_callback) OVERRIDE { const base::Closure& done_callback) OVERRIDE {
...@@ -153,25 +145,21 @@ CastSender* CastSender::CreateCastSender( ...@@ -153,25 +145,21 @@ CastSender* CastSender::CreateCastSender(
scoped_refptr<CastEnvironment> cast_environment, scoped_refptr<CastEnvironment> cast_environment,
const AudioSenderConfig& audio_config, const AudioSenderConfig& audio_config,
const VideoSenderConfig& video_config, const VideoSenderConfig& video_config,
VideoEncoderController* const video_encoder_controller, const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
PacketSender* const packet_sender) { PacketSender* const packet_sender) {
return new CastSenderImpl(cast_environment, return new CastSenderImpl(cast_environment, audio_config, video_config,
audio_config, gpu_factories, packet_sender);
video_config,
video_encoder_controller,
packet_sender);
} }
CastSenderImpl::CastSenderImpl( CastSenderImpl::CastSenderImpl(
scoped_refptr<CastEnvironment> cast_environment, scoped_refptr<CastEnvironment> cast_environment,
const AudioSenderConfig& audio_config, const AudioSenderConfig& audio_config,
const VideoSenderConfig& video_config, const VideoSenderConfig& video_config,
VideoEncoderController* const video_encoder_controller, const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
PacketSender* const packet_sender) PacketSender* const packet_sender)
: pacer_(cast_environment, packet_sender), : pacer_(cast_environment, packet_sender),
audio_sender_(cast_environment, audio_config, &pacer_), audio_sender_(cast_environment, audio_config, &pacer_),
video_sender_(cast_environment, video_config, video_encoder_controller, video_sender_(cast_environment, video_config, gpu_factories, &pacer_),
&pacer_),
frame_input_(new LocalFrameInput(cast_environment, frame_input_(new LocalFrameInput(cast_environment,
audio_sender_.AsWeakPtr(), audio_sender_.AsWeakPtr(),
video_sender_.AsWeakPtr())), video_sender_.AsWeakPtr())),
......
...@@ -28,11 +28,12 @@ class VideoSender; ...@@ -28,11 +28,12 @@ class VideoSender;
// together such as pacer, packet receiver, frame input, audio and video sender. // together such as pacer, packet receiver, frame input, audio and video sender.
class CastSenderImpl : public CastSender { class CastSenderImpl : public CastSender {
public: public:
CastSenderImpl(scoped_refptr<CastEnvironment> cast_environment, CastSenderImpl(
const AudioSenderConfig& audio_config, scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config, const AudioSenderConfig& audio_config,
VideoEncoderController* const video_encoder_controller, const VideoSenderConfig& video_config,
PacketSender* const packet_sender); const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
PacketSender* const packet_sender);
virtual ~CastSenderImpl(); virtual ~CastSenderImpl();
......
// 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/test/fake_gpu_video_accelerator_factories.h"
#include "base/logging.h"
#include "media/cast/test/fake_video_encode_accelerator.h"
namespace media {
namespace cast {
namespace test {
FakeGpuVideoAcceleratorFactories::FakeGpuVideoAcceleratorFactories(
const scoped_refptr<base::TaskRunner>& fake_task_runner)
: fake_task_runner_(fake_task_runner) {
}
FakeGpuVideoAcceleratorFactories::~FakeGpuVideoAcceleratorFactories() {
}
scoped_ptr<VideoEncodeAccelerator>
FakeGpuVideoAcceleratorFactories::CreateVideoEncodeAccelerator(
VideoEncodeAccelerator::Client* client) {
return scoped_ptr<VideoEncodeAccelerator>(
new FakeVideoEncodeAccelerator(client));
}
base::SharedMemory* FakeGpuVideoAcceleratorFactories::CreateSharedMemory(
size_t size) {
base::SharedMemory* shm = new base::SharedMemory();
if (!shm->CreateAndMapAnonymous(size)) {
NOTREACHED();
}
return shm;
}
scoped_refptr<base::SingleThreadTaskRunner>
FakeGpuVideoAcceleratorFactories::GetTaskRunner() {
return scoped_refptr<base::SingleThreadTaskRunner>(
static_cast<base::SingleThreadTaskRunner*>(fake_task_runner_.get()));
}
uint32 FakeGpuVideoAcceleratorFactories::CreateTextures(
int32 count,
const gfx::Size& size,
std::vector<uint32>* texture_ids,
std::vector<gpu::Mailbox>* texture_mailboxes,
uint32 texture_target) {
return 0;
}
scoped_ptr<VideoDecodeAccelerator>
FakeGpuVideoAcceleratorFactories::CreateVideoDecodeAccelerator(
VideoCodecProfile profile,
VideoDecodeAccelerator::Client* client) {
return scoped_ptr<VideoDecodeAccelerator>(
static_cast<media::VideoDecodeAccelerator*>(NULL));
}
bool FakeGpuVideoAcceleratorFactories::IsAborted() {
return false;
}
} // namespace test
} // namespace cast
} // namespace media
// 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_TEST_FAKE_GPU_VIDEO_ACCELERATOR_FACTORIES_H_
#define MEDIA_CAST_TEST_FAKE_GPU_VIDEO_ACCELERATOR_FACTORIES_H_
#include "media/filters/gpu_video_accelerator_factories.h"
#include "base/message_loop/message_loop.h"
#include "media/cast/test/fake_task_runner.h"
namespace media {
namespace cast {
namespace test {
class FakeGpuVideoAcceleratorFactories : public GpuVideoAcceleratorFactories {
public:
explicit FakeGpuVideoAcceleratorFactories(
const scoped_refptr<base::TaskRunner>& fake_task_runner);
virtual scoped_ptr<VideoEncodeAccelerator> CreateVideoEncodeAccelerator(
VideoEncodeAccelerator::Client* client) OVERRIDE;
virtual base::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() OVERRIDE;
//
// The following functions are no-op.
//
virtual uint32 CreateTextures(int32 count,
const gfx::Size& size,
std::vector<uint32>* texture_ids,
std::vector<gpu::Mailbox>* texture_mailboxes,
uint32 texture_target) OVERRIDE;
virtual void DeleteTexture(uint32 texture_id) OVERRIDE {}
virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {}
virtual void ReadPixels(uint32 texture_id,
const gfx::Size& size,
const SkBitmap& pixels) OVERRIDE {};
virtual scoped_ptr<VideoDecodeAccelerator> CreateVideoDecodeAccelerator(
VideoCodecProfile profile,
VideoDecodeAccelerator::Client* client) OVERRIDE;
virtual void Abort() OVERRIDE {}
virtual bool IsAborted() OVERRIDE;
private:
friend class base::RefCountedThreadSafe<FakeGpuVideoAcceleratorFactories>;
virtual ~FakeGpuVideoAcceleratorFactories();
const scoped_refptr<base::TaskRunner> fake_task_runner_;
DISALLOW_COPY_AND_ASSIGN(FakeGpuVideoAcceleratorFactories);
};
} // namespace test
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_TEST_FAKE_GPU_VIDEO_ACCELERATOR_FACTORIES_H_
// 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/test/fake_video_encode_accelerator.h"
#include "base/logging.h"
namespace media {
namespace cast {
namespace test {
static const unsigned int kMinimumInputCount = 1;
static const size_t kMinimumOutputBufferSize = 123456;
FakeVideoEncodeAccelerator::FakeVideoEncodeAccelerator(
VideoEncodeAccelerator::Client* client)
: client_(client),
first_(true) {
DCHECK(client);
}
FakeVideoEncodeAccelerator::~FakeVideoEncodeAccelerator() {
}
void FakeVideoEncodeAccelerator::Initialize(
media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
VideoCodecProfile output_profile,
uint32 initial_bitrate) {
DCHECK(client_);
if (output_profile != media::VP8PROFILE_MAIN &&
output_profile != media::H264PROFILE_MAIN) {
client_->NotifyError(kInvalidArgumentError);
return;
}
client_->NotifyInitializeDone();
client_->RequireBitstreamBuffers(kMinimumInputCount,
input_visible_size,
kMinimumOutputBufferSize);
}
void FakeVideoEncodeAccelerator::Encode(const scoped_refptr<VideoFrame>& frame,
bool force_keyframe) {
DCHECK(client_);
DCHECK(!available_buffer_ids_.empty());
// Fake that we have encoded the frame; resulting in using the full output
// buffer.
int32 id = available_buffer_ids_.front();
available_buffer_ids_.pop_front();
bool is_key_fame = force_keyframe;
if (first_) {
is_key_fame = true;
first_ = false;
}
client_->BitstreamBufferReady(id, kMinimumOutputBufferSize, is_key_fame);
}
void FakeVideoEncodeAccelerator::UseOutputBitstreamBuffer(
const BitstreamBuffer& buffer) {
available_buffer_ids_.push_back(buffer.id());
}
void FakeVideoEncodeAccelerator::RequestEncodingParametersChange(
uint32 bitrate, uint32 framerate) {
// No-op.
}
void FakeVideoEncodeAccelerator::Destroy() {
delete this;
}
} // namespace test
} // namespace cast
} // namespace media
// 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_TEST_FAKE_MOCK_VIDEO_ENCODE_ACCELERATOR_H_
#define MEDIA_CAST_TEST_FAKE_MOCK_VIDEO_ENCODE_ACCELERATOR_H_
#include "media/video/video_encode_accelerator.h"
#include <list>
#include "media/base/bitstream_buffer.h"
namespace media {
namespace cast {
namespace test {
class FakeVideoEncodeAccelerator : public VideoEncodeAccelerator {
public:
explicit FakeVideoEncodeAccelerator(VideoEncodeAccelerator::Client* client);
virtual void Initialize(media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
VideoCodecProfile output_profile,
uint32 initial_bitrate) OVERRIDE;
virtual void Encode(const scoped_refptr<VideoFrame>& frame,
bool force_keyframe) OVERRIDE;
virtual void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) OVERRIDE;
virtual void RequestEncodingParametersChange(uint32 bitrate,
uint32 framerate) OVERRIDE;
virtual void Destroy() OVERRIDE;
private:
virtual ~FakeVideoEncodeAccelerator();
VideoEncodeAccelerator::Client* client_;
bool first_;
std::list<int32> available_buffer_ids_;
DISALLOW_COPY_AND_ASSIGN(FakeVideoEncodeAccelerator);
};
} // namespace test
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_TEST_FAKE_MOCK_VIDEO_ENCODE_ACCELERATOR_H_
...@@ -23,9 +23,6 @@ VideoDecoder::VideoDecoder(const VideoReceiverConfig& video_config, ...@@ -23,9 +23,6 @@ VideoDecoder::VideoDecoder(const VideoReceiverConfig& video_config,
case kH264: case kH264:
NOTIMPLEMENTED(); NOTIMPLEMENTED();
break; break;
case kExternalVideo:
DCHECK(false) << "Invalid codec";
break;
} }
} }
......
...@@ -20,7 +20,6 @@ namespace cast { ...@@ -20,7 +20,6 @@ namespace cast {
using testing::_; using testing::_;
// Random frame size for testing. // Random frame size for testing.
const int kFrameSize = 2345;
static const int64 kStartMillisecond = GG_INT64_C(1245); static const int64 kStartMillisecond = GG_INT64_C(1245);
namespace { namespace {
...@@ -76,18 +75,6 @@ TEST_F(VideoDecoderTest, DISABLED_SizeZero) { ...@@ -76,18 +75,6 @@ TEST_F(VideoDecoderTest, DISABLED_SizeZero) {
"Empty frame"); "Empty frame");
} }
// TODO(pwestin): EXPECT_DEATH tests can not pass valgrind.
TEST_F(VideoDecoderTest, DISABLED_InvalidCodec) {
EncodedVideoFrame encoded_frame;
base::TimeTicks render_time;
encoded_frame.data.assign(kFrameSize, 0);
encoded_frame.codec = kExternalVideo;
EXPECT_DEATH(
decoder_->DecodeVideoFrame(&encoded_frame, render_time, base::Bind(
&DecodeTestFrameCallback::DecodeComplete, test_callback_)),
"Invalid codec");
}
// TODO(pwestin): Test decoding a real frame. // TODO(pwestin): Test decoding a real frame.
} // namespace cast } // namespace cast
......
...@@ -147,8 +147,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, ...@@ -147,8 +147,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
if (key_frame_requested_) { if (key_frame_requested_) {
flags = VPX_EFLAG_FORCE_KF; flags = VPX_EFLAG_FORCE_KF;
// Self reference. // Self reference.
latest_frame_id_to_reference = latest_frame_id_to_reference = last_encoded_frame_id_ + 1;
static_cast<uint8>(last_encoded_frame_id_ + 1);
// We can pick any buffer as buffer_to_update since we update // We can pick any buffer as buffer_to_update since we update
// them all. // them all.
buffer_to_update = kLastBuffer; buffer_to_update = kLastBuffer;
......
// 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/external_video_encoder.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/cast/cast_defines.h"
#include "media/video/video_encode_accelerator.h"
namespace {
// We allocate more input buffers than what is asked for by
// RequireBitstreamBuffers() due to potential threading timing.
static const int kInputBufferExtraCount = 1;
static const int kOutputBufferCount = 3;
void LogFrameEncodedEvent(
const base::TimeTicks& now,
media::cast::CastEnvironment* const cast_environment,
const base::TimeTicks& capture_time) {
cast_environment->Logging()->InsertFrameEvent(
now,
media::cast::kVideoFrameEncoded,
media::cast::GetVideoRtpTimestamp(capture_time),
media::cast::kFrameIdUnknown);
}
} // namespace
namespace media {
namespace cast {
// Container for the associated data of a video frame being processed.
struct EncodedFrameReturnData {
EncodedFrameReturnData(base::TimeTicks c_time,
VideoEncoder::FrameEncodedCallback callback) {
capture_time = c_time;
frame_encoded_callback = callback;
}
base::TimeTicks capture_time;
VideoEncoder::FrameEncodedCallback frame_encoded_callback;
};
// The ExternalVideoEncoder class can be deleted directly by cast, while
// LocalVideoEncodeAcceleratorClient stays around long enough to properly shut
// down the VideoEncodeAccelerator.
class LocalVideoEncodeAcceleratorClient
: public VideoEncodeAccelerator::Client,
public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> {
public:
LocalVideoEncodeAcceleratorClient(
scoped_refptr<CastEnvironment> cast_environment,
scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories,
const base::WeakPtr<ExternalVideoEncoder>& weak_owner)
: cast_environment_(cast_environment),
gpu_factories_(gpu_factories),
encoder_task_runner_(gpu_factories->GetTaskRunner()),
weak_owner_(weak_owner),
last_encoded_frame_id_(kStartFrameId) {
DCHECK(encoder_task_runner_);
}
// Initialize the real HW encoder.
void Initialize(const VideoSenderConfig& video_config) {
DCHECK(gpu_factories_);
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
video_encode_accelerator_ =
gpu_factories_->CreateVideoEncodeAccelerator(this).Pass();
if (!video_encode_accelerator_) return;
VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
switch (video_config.codec) {
case kVp8:
output_profile = media::VP8PROFILE_MAIN;
break;
case kH264:
output_profile = media::H264PROFILE_MAIN;
break;
}
codec_ = video_config.codec;
max_frame_rate_ = video_config.max_frame_rate;
// Asynchronous initialization call; NotifyInitializeDone or NotifyError
// will be called once the HW is initialized.
video_encode_accelerator_->Initialize(
media::VideoFrame::I420,
gfx::Size(video_config.width, video_config.height),
output_profile,
video_config.start_bitrate);
}
// Free the HW.
void Destroy() {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
if (video_encode_accelerator_) {
video_encode_accelerator_.release()->Destroy();
}
}
void SetBitRate(uint32 bit_rate) {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
video_encode_accelerator_->RequestEncodingParametersChange(
bit_rate, max_frame_rate_);
}
void EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
bool key_frame_requested,
const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
if (input_buffers_free_.empty()) {
NOTREACHED();
VLOG(2) << "EncodeVideoFrame(): drop frame due to no hw buffers";
return;
}
const int index = input_buffers_free_.back();
base::SharedMemory* input_buffer = input_buffers_[index];
// TODO(pwestin): this allocation and copy can be removed once we don't
// pass the video frame through liblingle.
scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::WrapExternalPackedMemory(
video_frame->format(),
video_frame->coded_size(),
video_frame->visible_rect(),
video_frame->natural_size(),
reinterpret_cast<uint8*>(input_buffer->memory()),
input_buffer->mapped_size(),
input_buffer->handle(),
video_frame->GetTimestamp(),
base::Bind(&LocalVideoEncodeAcceleratorClient::FinishedWithInBuffer,
this,
index));
if (!frame) {
VLOG(1) << "EncodeVideoFrame(): failed to create frame";
NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
return;
}
// Do a stride copy of the input frame to match the input requirements.
media::CopyYPlane(video_frame->data(VideoFrame::kYPlane),
video_frame->stride(VideoFrame::kYPlane),
video_frame->natural_size().height(),
frame.get());
media::CopyUPlane(video_frame->data(VideoFrame::kUPlane),
video_frame->stride(VideoFrame::kUPlane),
video_frame->natural_size().height(),
frame.get());
media::CopyVPlane(video_frame->data(VideoFrame::kVPlane),
video_frame->stride(VideoFrame::kVPlane),
video_frame->natural_size().height(),
frame.get());
encoded_frame_data_storage_.push_back(
EncodedFrameReturnData(capture_time, frame_encoded_callback));
// BitstreamBufferReady will be called once the encoder is done.
video_encode_accelerator_->Encode(frame, key_frame_requested);
}
protected:
virtual void NotifyInitializeDone() OVERRIDE {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(&ExternalVideoEncoder::EncoderInitialized,
weak_owner_));
}
virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
VLOG(1) << "ExternalVideoEncoder NotifyError: " << error;
if (video_encode_accelerator_) {
video_encode_accelerator_.release()->Destroy();
}
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(&ExternalVideoEncoder::EncoderError,
weak_owner_));
}
// Called to allocate the input and output buffers.
virtual void RequireBitstreamBuffers(unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size) OVERRIDE {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
DCHECK(video_encode_accelerator_);
for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) {
base::SharedMemory* shm = gpu_factories_->CreateSharedMemory(
media::VideoFrame::AllocationSize(media::VideoFrame::I420,
input_coded_size));
if (!shm) {
VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer ";
return;
}
input_buffers_.push_back(shm);
input_buffers_free_.push_back(i);
}
for (int j = 0; j < kOutputBufferCount; ++j) {
base::SharedMemory* shm =
gpu_factories_->CreateSharedMemory(output_buffer_size);
if (!shm) {
VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer ";
return;
}
output_buffers_.push_back(shm);
}
// Immediately provide all output buffers to the VEA.
for (size_t i = 0; i < output_buffers_.size(); ++i) {
video_encode_accelerator_->UseOutputBitstreamBuffer(
media::BitstreamBuffer(static_cast<int32>(i),
output_buffers_[i]->handle(),
output_buffers_[i]->mapped_size()));
}
}
// Encoder has encoded a frame and it's available in one of out output
// buffers.
virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
size_t payload_size,
bool key_frame) OVERRIDE {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
if (bitstream_buffer_id < 0 ||
bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) {
NOTREACHED();
VLOG(1) << "BitstreamBufferReady(): invalid bitstream_buffer_id="
<< bitstream_buffer_id;
NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
return;
}
base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
if (payload_size > output_buffer->mapped_size()) {
NOTREACHED();
VLOG(1) << "BitstreamBufferReady(): invalid payload_size = "
<< payload_size;
NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
return;
}
if (encoded_frame_data_storage_.empty()) {
NOTREACHED();
NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
return;
}
scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
encoded_frame->codec = codec_;
encoded_frame->key_frame = key_frame;
encoded_frame->last_referenced_frame_id = last_encoded_frame_id_;
last_encoded_frame_id_++;
encoded_frame->frame_id = last_encoded_frame_id_;
if (key_frame) {
// Self referenced.
encoded_frame->last_referenced_frame_id = encoded_frame->frame_id;
}
encoded_frame->data.insert(
0, static_cast<const char*>(output_buffer->memory()), payload_size);
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(encoded_frame_data_storage_.front().frame_encoded_callback,
base::Passed(&encoded_frame),
encoded_frame_data_storage_.front().capture_time));
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(LogFrameEncodedEvent, now, cast_environment_,
encoded_frame_data_storage_.front().capture_time));
encoded_frame_data_storage_.pop_front();
// We need to re-add the output buffer to the encoder after we are done
// with it.
video_encode_accelerator_->UseOutputBitstreamBuffer(
media::BitstreamBuffer(bitstream_buffer_id,
output_buffers_[bitstream_buffer_id]->handle(),
output_buffers_[bitstream_buffer_id]->mapped_size()));
}
private:
// Encoder is done with the provided input buffer.
void FinishedWithInBuffer(int input_index) {
DCHECK(encoder_task_runner_);
DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
DCHECK_GE(input_index, 0);
DCHECK_LT(input_index, static_cast<int>(input_buffers_.size()));
VLOG(2) << "EncodeFrameFinished(): index=" << input_index;
input_buffers_free_.push_back(input_index);
}
friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>;
virtual ~LocalVideoEncodeAcceleratorClient() {}
const scoped_refptr<CastEnvironment> cast_environment_;
scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
const base::WeakPtr<ExternalVideoEncoder> weak_owner_;
scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
int max_frame_rate_;
VideoCodec codec_;
uint32 last_encoded_frame_id_;
// Shared memory buffers for input/output with the VideoAccelerator.
ScopedVector<base::SharedMemory> input_buffers_;
ScopedVector<base::SharedMemory> output_buffers_;
// Input buffers ready to be filled with input from Encode(). As a LIFO since
// we don't care about ordering.
std::vector<int> input_buffers_free_;
// FIFO list.
std::list<EncodedFrameReturnData> encoded_frame_data_storage_;
DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient);
};
ExternalVideoEncoder::ExternalVideoEncoder(
scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories)
: video_config_(video_config),
cast_environment_(cast_environment),
encoder_active_(false),
key_frame_requested_(false),
skip_next_frame_(false),
skip_count_(0),
encoder_task_runner_(gpu_factories->GetTaskRunner()),
weak_factory_(this) {
DCHECK(gpu_factories);
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
video_accelerator_client_ = new LocalVideoEncodeAcceleratorClient(
cast_environment, gpu_factories, weak_factory_.GetWeakPtr());
encoder_task_runner_->PostTask(
FROM_HERE,
base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize,
video_accelerator_client_, video_config));
}
ExternalVideoEncoder::~ExternalVideoEncoder() {
encoder_task_runner_->PostTask(
FROM_HERE,
base::Bind(&LocalVideoEncodeAcceleratorClient::Destroy,
video_accelerator_client_));
}
void ExternalVideoEncoder::EncoderInitialized() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
encoder_active_ = true;
}
void ExternalVideoEncoder::EncoderError() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
encoder_active_ = false;
}
bool ExternalVideoEncoder::EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
const FrameEncodedCallback& frame_encoded_callback) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
if (!encoder_active_) return false;
if (skip_next_frame_) {
VLOG(1) << "Skip encoding frame";
++skip_count_;
skip_next_frame_ = false;
return false;
}
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
cast_environment_->Logging()->InsertFrameEvent(now, kVideoFrameSentToEncoder,
GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
encoder_task_runner_->PostTask(
FROM_HERE,
base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame,
video_accelerator_client_, video_frame, capture_time,
key_frame_requested_, frame_encoded_callback));
key_frame_requested_ = false;
return true;
}
// Inform the encoder about the new target bit rate.
void ExternalVideoEncoder::SetBitRate(int new_bit_rate) {
encoder_task_runner_->PostTask(
FROM_HERE,
base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate,
video_accelerator_client_, new_bit_rate));
}
// Inform the encoder to not encode the next frame.
void ExternalVideoEncoder::SkipNextFrame(bool skip_next_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
skip_next_frame_ = skip_next_frame;
}
// Inform the encoder to encode the next frame as a key frame.
void ExternalVideoEncoder::GenerateKeyFrame() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
key_frame_requested_ = true;
}
// Inform the encoder to only reference frames older or equal to frame_id;
void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
// Do nothing not supported.
}
int ExternalVideoEncoder::NumberOfSkippedFrames() const {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
return skip_count_;
}
} // namespace cast
} // namespace media
// 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_EXTERNAL_VIDEO_ENCODER_H_
#define MEDIA_CAST_VIDEO_SENDER_EXTERNAL_VIDEO_ENCODER_H_
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
#include "media/cast/video_sender/video_encoder.h"
#include "media/filters/gpu_video_accelerator_factories.h"
namespace media {
class VideoFrame;
}
namespace media {
namespace cast {
class LocalVideoEncodeAcceleratorClient;
// This object is called external from the main cast thread and internally from
// the video encoder thread.
class ExternalVideoEncoder : public VideoEncoder {
public:
ExternalVideoEncoder(
scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories);
virtual ~ExternalVideoEncoder();
// Called from the main cast thread. This function post the encode task to the
// video encoder thread;
// The video_frame must be valid until the closure callback is called.
// The closure callback is called from the video encoder thread as soon as
// the encoder is done with the frame; it does not mean that the encoded frame
// has been sent out.
// Once the encoded frame is ready the frame_encoded_callback is called.
virtual bool EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
const FrameEncodedCallback& frame_encoded_callback) OVERRIDE;
// The following functions are called from the main cast thread.
virtual void SetBitRate(int new_bit_rate) OVERRIDE;
virtual void SkipNextFrame(bool skip_next_frame) OVERRIDE;
virtual void GenerateKeyFrame() OVERRIDE;
virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
virtual int NumberOfSkippedFrames() const OVERRIDE;
protected:
void EncoderInitialized();
void EncoderError();
private:
friend class LocalVideoEncodeAcceleratorClient;
const VideoSenderConfig video_config_;
scoped_refptr<CastEnvironment> cast_environment_;
bool encoder_active_;
bool key_frame_requested_;
bool skip_next_frame_;
int skip_count_;
scoped_refptr<LocalVideoEncodeAcceleratorClient> video_accelerator_client_;
scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
// Weak pointer factory for posting back LocalVideoEncodeAcceleratorClient
// notifications to ExternalVideoEncoder.
base::WeakPtrFactory<ExternalVideoEncoder> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ExternalVideoEncoder);
};
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_VIDEO_SENDER_EXTERNAL_VIDEO_ENCODER_H_
// 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 <vector>
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
#include "media/cast/test/fake_gpu_video_accelerator_factories.h"
#include "media/cast/test/fake_task_runner.h"
#include "media/cast/test/fake_video_encode_accelerator.h"
#include "media/cast/test/video_utility.h"
#include "media/cast/video_sender/external_video_encoder.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
namespace cast {
using testing::_;
namespace {
class TestVideoEncoderCallback :
public base::RefCountedThreadSafe<TestVideoEncoderCallback> {
public:
TestVideoEncoderCallback() {}
void SetExpectedResult(bool expected_key_frame,
uint8 expected_frame_id,
uint8 expected_last_referenced_frame_id,
const base::TimeTicks& expected_capture_time) {
expected_key_frame_ = expected_key_frame;
expected_frame_id_ = expected_frame_id;
expected_last_referenced_frame_id_ = expected_last_referenced_frame_id;
expected_capture_time_ = expected_capture_time;
}
void DeliverEncodedVideoFrame(scoped_ptr<EncodedVideoFrame> encoded_frame,
const base::TimeTicks& capture_time) {
EXPECT_EQ(expected_key_frame_, encoded_frame->key_frame);
EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id);
EXPECT_EQ(expected_last_referenced_frame_id_,
encoded_frame->last_referenced_frame_id);
EXPECT_EQ(expected_capture_time_, capture_time);
}
protected:
virtual ~TestVideoEncoderCallback() {}
private:
friend class base::RefCountedThreadSafe<TestVideoEncoderCallback>;
bool expected_key_frame_;
uint8 expected_frame_id_;
uint8 expected_last_referenced_frame_id_;
base::TimeTicks expected_capture_time_;
};
} // namespace
class ExternalVideoEncoderTest : public ::testing::Test {
protected:
ExternalVideoEncoderTest()
: test_video_encoder_callback_(new TestVideoEncoderCallback()) {
video_config_.sender_ssrc = 1;
video_config_.incoming_feedback_ssrc = 2;
video_config_.rtp_payload_type = 127;
video_config_.use_external_encoder = true;
video_config_.width = 320;
video_config_.height = 240;
video_config_.max_bitrate = 5000000;
video_config_.min_bitrate = 1000000;
video_config_.start_bitrate = 2000000;
video_config_.max_qp = 56;
video_config_.min_qp = 0;
video_config_.max_frame_rate = 30;
video_config_.max_number_of_video_buffers_used = 3;
video_config_.codec = kVp8;
gfx::Size size(video_config_.width, video_config_.height);
video_frame_ = media::VideoFrame::CreateFrame(VideoFrame::I420,
size, gfx::Rect(size), size, base::TimeDelta());
PopulateVideoFrame(video_frame_, 123);
}
virtual ~ExternalVideoEncoderTest() {}
virtual void SetUp() {
task_runner_ = new test::FakeTaskRunner(&testing_clock_);
cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
task_runner_, task_runner_, task_runner_, task_runner_,
GetDefaultCastLoggingConfig());
video_encoder_.reset(new ExternalVideoEncoder(
cast_environment_,
video_config_,
new test::FakeGpuVideoAcceleratorFactories(task_runner_)));
}
base::SimpleTestTickClock testing_clock_;
scoped_refptr<TestVideoEncoderCallback> test_video_encoder_callback_;
VideoSenderConfig video_config_;
scoped_refptr<test::FakeTaskRunner> task_runner_;
scoped_ptr<VideoEncoder> video_encoder_;
scoped_refptr<media::VideoFrame> video_frame_;
scoped_refptr<CastEnvironment> cast_environment_;
};
TEST_F(ExternalVideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
task_runner_->RunTasks(); // Run the initializer on the correct thread.
VideoEncoder::FrameEncodedCallback frame_encoded_callback =
base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
test_video_encoder_callback_.get());
base::TimeTicks capture_time;
capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(true, 0, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback));
task_runner_->RunTasks();
for (int i = 0; i < 6; ++i) {
capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, i + 1, i,
capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback));
task_runner_->RunTasks();
}
}
} // namespace cast
} // namespace media
// 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 "media/cast/video_sender/mock_video_encoder_controller.h"
namespace media {
namespace cast {
MockVideoEncoderController::MockVideoEncoderController() {
}
MockVideoEncoderController::~MockVideoEncoderController() {
}
} // namespace cast
} // namespace media
// 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.
#ifndef MEDIA_CAST_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
#define MEDIA_CAST_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
#include "media/cast/cast_config.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
namespace cast {
class MockVideoEncoderController : public VideoEncoderController {
public:
MockVideoEncoderController();
virtual ~MockVideoEncoderController();
MOCK_METHOD1(SetBitRate, void(int new_bit_rate));
MOCK_METHOD1(SkipNextFrame, void(bool skip_next_frame));
MOCK_METHOD0(GenerateKeyFrame, void());
MOCK_METHOD1(LatestFrameIdToReference, void(uint32 frame_id));
MOCK_CONST_METHOD0(NumberOfSkippedFrames, int());
};
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_VIDEO_SENDER_MOCK_VIDEO_ENCODER_CONTROLLER_H_
...@@ -5,79 +5,55 @@ ...@@ -5,79 +5,55 @@
#ifndef MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_ #ifndef MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_
#define MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_ #define MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h" #include "base/time/time.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_config.h" #include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
namespace media {
class VideoFrame;
}
namespace media { namespace media {
namespace cast { namespace cast {
// This object is called external from the main cast thread and internally from // All these functions are called from the main cast thread.
// the video encoder thread. class VideoEncoder {
class VideoEncoder : public VideoEncoderController {
public: public:
typedef base::Callback<void(scoped_ptr<EncodedVideoFrame>, typedef base::Callback<void(scoped_ptr<EncodedVideoFrame>,
const base::TimeTicks&)> FrameEncodedCallback; const base::TimeTicks&)> FrameEncodedCallback;
VideoEncoder(scoped_refptr<CastEnvironment> cast_environment, virtual ~VideoEncoder() {}
const VideoSenderConfig& video_config,
uint8 max_unacked_frames);
virtual ~VideoEncoder();
// Called from the main cast thread. This function post the encode task to the
// video encoder thread;
// The video_frame must be valid until the closure callback is called. // The video_frame must be valid until the closure callback is called.
// The closure callback is called from the video encoder thread as soon as // The closure callback is called from the video encoder thread as soon as
// the encoder is done with the frame; it does not mean that the encoded frame // the encoder is done with the frame; it does not mean that the encoded frame
// has been sent out. // has been sent out.
// Once the encoded frame is ready the frame_encoded_callback is called. // Once the encoded frame is ready the frame_encoded_callback is called.
bool EncodeVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame, virtual bool EncodeVideoFrame(
const base::TimeTicks& capture_time,
const FrameEncodedCallback& frame_encoded_callback);
protected:
struct CodecDynamicConfig {
bool key_frame_requested;
uint32 latest_frame_id_to_reference;
int bit_rate;
};
// The actual encode, called from the video encoder thread.
void EncodeVideoFrameEncoderThread(
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time, const base::TimeTicks& capture_time,
const CodecDynamicConfig& dynamic_config, const FrameEncodedCallback& frame_encoded_callback) = 0;
const FrameEncodedCallback& frame_encoded_callback);
// Inform the encoder about the new target bit rate.
virtual void SetBitRate(int new_bit_rate) = 0;
// The following functions are called from the main cast thread. // Inform the encoder to not encode the next frame.
virtual void SetBitRate(int new_bit_rate) OVERRIDE; // Note: this setting is sticky and should last until called with false.
virtual void SkipNextFrame(bool skip_next_frame) OVERRIDE; virtual void SkipNextFrame(bool skip_next_frame) = 0;
virtual void GenerateKeyFrame() OVERRIDE;
virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
virtual int NumberOfSkippedFrames() const OVERRIDE;
private: // Inform the encoder to encode the next frame as a key frame.
friend class base::RefCountedThreadSafe<VideoEncoder>; virtual void GenerateKeyFrame() = 0;
const VideoSenderConfig video_config_; // Inform the encoder to only reference frames older or equal to frame_id;
scoped_refptr<CastEnvironment> cast_environment_; virtual void LatestFrameIdToReference(uint32 frame_id) = 0;
scoped_ptr<Vp8Encoder> vp8_encoder_;
CodecDynamicConfig dynamic_config_;
bool skip_next_frame_;
int skip_count_;
DISALLOW_COPY_AND_ASSIGN(VideoEncoder); // Query the codec about how many frames it has skipped due to slow ACK.
virtual int NumberOfSkippedFrames() const = 0;
}; };
} // namespace cast } // namespace cast
} // namespace media } // namespace media
#endif // MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_ #endif // MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_H_
// Copyright 2013 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -9,20 +9,22 @@ ...@@ -9,20 +9,22 @@
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/video_sender/video_encoder_impl.h"
namespace media { namespace media {
namespace cast { namespace cast {
void LogFrameEncodedEvent(CastEnvironment* const cast_environment, void LogFrameEncodedEvent(const base::TimeTicks& now,
CastEnvironment* const cast_environment,
const base::TimeTicks& capture_time) { const base::TimeTicks& capture_time) {
base::TimeTicks now = cast_environment->Clock()->NowTicks();
cast_environment->Logging()->InsertFrameEvent(now, kVideoFrameEncoded, cast_environment->Logging()->InsertFrameEvent(now, kVideoFrameEncoded,
GetVideoRtpTimestamp(capture_time), kFrameIdUnknown); GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
} }
VideoEncoder::VideoEncoder(scoped_refptr<CastEnvironment> cast_environment, VideoEncoderImpl::VideoEncoderImpl(
const VideoSenderConfig& video_config, scoped_refptr<CastEnvironment> cast_environment,
uint8 max_unacked_frames) const VideoSenderConfig& video_config,
uint8 max_unacked_frames)
: video_config_(video_config), : video_config_(video_config),
cast_environment_(cast_environment), cast_environment_(cast_environment),
skip_next_frame_(false), skip_next_frame_(false),
...@@ -38,9 +40,9 @@ VideoEncoder::VideoEncoder(scoped_refptr<CastEnvironment> cast_environment, ...@@ -38,9 +40,9 @@ VideoEncoder::VideoEncoder(scoped_refptr<CastEnvironment> cast_environment,
dynamic_config_.bit_rate = video_config.start_bitrate; dynamic_config_.bit_rate = video_config.start_bitrate;
} }
VideoEncoder::~VideoEncoder() {} VideoEncoderImpl::~VideoEncoderImpl() {}
bool VideoEncoder::EncodeVideoFrame( bool VideoEncoderImpl::EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time, const base::TimeTicks& capture_time,
const FrameEncodedCallback& frame_encoded_callback) { const FrameEncodedCallback& frame_encoded_callback) {
...@@ -49,6 +51,7 @@ bool VideoEncoder::EncodeVideoFrame( ...@@ -49,6 +51,7 @@ bool VideoEncoder::EncodeVideoFrame(
if (skip_next_frame_) { if (skip_next_frame_) {
++skip_count_; ++skip_count_;
skip_next_frame_ = false;
VLOG(1) << "Skip encoding frame"; VLOG(1) << "Skip encoding frame";
return false; return false;
} }
...@@ -57,7 +60,7 @@ bool VideoEncoder::EncodeVideoFrame( ...@@ -57,7 +60,7 @@ bool VideoEncoder::EncodeVideoFrame(
cast_environment_->Logging()->InsertFrameEvent(now, kVideoFrameSentToEncoder, cast_environment_->Logging()->InsertFrameEvent(now, kVideoFrameSentToEncoder,
GetVideoRtpTimestamp(capture_time), kFrameIdUnknown); GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
cast_environment_->PostTask(CastEnvironment::VIDEO_ENCODER, FROM_HERE, cast_environment_->PostTask(CastEnvironment::VIDEO_ENCODER, FROM_HERE,
base::Bind(&VideoEncoder::EncodeVideoFrameEncoderThread, base::Bind(&VideoEncoderImpl::EncodeVideoFrameEncoderThread,
base::Unretained(this), video_frame, capture_time, base::Unretained(this), video_frame, capture_time,
dynamic_config_, frame_encoded_callback)); dynamic_config_, frame_encoded_callback));
...@@ -65,7 +68,7 @@ bool VideoEncoder::EncodeVideoFrame( ...@@ -65,7 +68,7 @@ bool VideoEncoder::EncodeVideoFrame(
return true; return true;
} }
void VideoEncoder::EncodeVideoFrameEncoderThread( void VideoEncoderImpl::EncodeVideoFrameEncoderThread(
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time, const base::TimeTicks& capture_time,
const CodecDynamicConfig& dynamic_config, const CodecDynamicConfig& dynamic_config,
...@@ -81,8 +84,9 @@ void VideoEncoder::EncodeVideoFrameEncoderThread( ...@@ -81,8 +84,9 @@ void VideoEncoder::EncodeVideoFrameEncoderThread(
scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame()); scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
bool retval = vp8_encoder_->Encode(video_frame, encoded_frame.get()); bool retval = vp8_encoder_->Encode(video_frame, encoded_frame.get());
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
base::Bind(LogFrameEncodedEvent, cast_environment_, capture_time)); base::Bind(LogFrameEncodedEvent, now, cast_environment_, capture_time));
if (!retval) { if (!retval) {
VLOG(1) << "Encoding failed"; VLOG(1) << "Encoding failed";
...@@ -98,26 +102,26 @@ void VideoEncoder::EncodeVideoFrameEncoderThread( ...@@ -98,26 +102,26 @@ void VideoEncoder::EncodeVideoFrameEncoderThread(
} }
// Inform the encoder about the new target bit rate. // Inform the encoder about the new target bit rate.
void VideoEncoder::SetBitRate(int new_bit_rate) { void VideoEncoderImpl::SetBitRate(int new_bit_rate) {
dynamic_config_.bit_rate = new_bit_rate; dynamic_config_.bit_rate = new_bit_rate;
} }
// Inform the encoder to not encode the next frame. // Inform the encoder to not encode the next frame.
void VideoEncoder::SkipNextFrame(bool skip_next_frame) { void VideoEncoderImpl::SkipNextFrame(bool skip_next_frame) {
skip_next_frame_ = skip_next_frame; skip_next_frame_ = skip_next_frame;
} }
// Inform the encoder to encode the next frame as a key frame. // Inform the encoder to encode the next frame as a key frame.
void VideoEncoder::GenerateKeyFrame() { void VideoEncoderImpl::GenerateKeyFrame() {
dynamic_config_.key_frame_requested = true; dynamic_config_.key_frame_requested = true;
} }
// Inform the encoder to only reference frames older or equal to frame_id; // Inform the encoder to only reference frames older or equal to frame_id;
void VideoEncoder::LatestFrameIdToReference(uint32 frame_id) { void VideoEncoderImpl::LatestFrameIdToReference(uint32 frame_id) {
dynamic_config_.latest_frame_id_to_reference = frame_id; dynamic_config_.latest_frame_id_to_reference = frame_id;
} }
int VideoEncoder::NumberOfSkippedFrames() const { int VideoEncoderImpl::NumberOfSkippedFrames() const {
return skip_count_; return skip_count_;
} }
......
// 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_VIDEO_ENCODER_IMPL_H_
#define MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_IMPL_H_
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_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/video_encoder.h"
namespace media {
class VideoFrame;
namespace cast {
// This object is called external from the main cast thread and internally from
// the video encoder thread.
class VideoEncoderImpl : public VideoEncoder {
public:
typedef base::Callback<void(scoped_ptr<EncodedVideoFrame>,
const base::TimeTicks&)> FrameEncodedCallback;
VideoEncoderImpl(scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
uint8 max_unacked_frames);
virtual ~VideoEncoderImpl();
// Called from the main cast thread. This function post the encode task to the
// video encoder thread;
// The video_frame must be valid until the closure callback is called.
// The closure callback is called from the video encoder thread as soon as
// the encoder is done with the frame; it does not mean that the encoded frame
// has been sent out.
// Once the encoded frame is ready the frame_encoded_callback is called.
virtual bool EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
const FrameEncodedCallback& frame_encoded_callback) OVERRIDE;
// The following functions are called from the main cast thread.
virtual void SetBitRate(int new_bit_rate) OVERRIDE;
virtual void SkipNextFrame(bool skip_next_frame) OVERRIDE;
virtual void GenerateKeyFrame() OVERRIDE;
virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE;
virtual int NumberOfSkippedFrames() const OVERRIDE;
protected:
struct CodecDynamicConfig {
bool key_frame_requested;
uint32 latest_frame_id_to_reference;
int bit_rate;
};
// The actual encode, called from the video encoder thread.
void EncodeVideoFrameEncoderThread(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time,
const CodecDynamicConfig& dynamic_config,
const FrameEncodedCallback& frame_encoded_callback);
private:
const VideoSenderConfig video_config_;
scoped_refptr<CastEnvironment> cast_environment_;
scoped_ptr<Vp8Encoder> vp8_encoder_;
CodecDynamicConfig dynamic_config_;
bool skip_next_frame_;
int skip_count_;
DISALLOW_COPY_AND_ASSIGN(VideoEncoderImpl);
};
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_VIDEO_SENDER_VIDEO_ENCODER_IMPL_H_
// Copyright 2013 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/cast/test/fake_task_runner.h" #include "media/cast/test/fake_task_runner.h"
#include "media/cast/test/video_utility.h" #include "media/cast/test/video_utility.h"
#include "media/cast/video_sender/video_encoder.h" #include "media/cast/video_sender/video_encoder_impl.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
namespace media { namespace media {
...@@ -58,9 +58,9 @@ class TestVideoEncoderCallback : ...@@ -58,9 +58,9 @@ class TestVideoEncoderCallback :
}; };
} // namespace } // namespace
class VideoEncoderTest : public ::testing::Test { class VideoEncoderImplTest : public ::testing::Test {
protected: protected:
VideoEncoderTest() VideoEncoderImplTest()
: test_video_encoder_callback_(new TestVideoEncoderCallback()) { : test_video_encoder_callback_(new TestVideoEncoderCallback()) {
video_config_.sender_ssrc = 1; video_config_.sender_ssrc = 1;
video_config_.incoming_feedback_ssrc = 2; video_config_.incoming_feedback_ssrc = 2;
...@@ -82,7 +82,7 @@ class VideoEncoderTest : public ::testing::Test { ...@@ -82,7 +82,7 @@ class VideoEncoderTest : public ::testing::Test {
PopulateVideoFrame(video_frame_, 123); PopulateVideoFrame(video_frame_, 123);
} }
virtual ~VideoEncoderTest() {} virtual ~VideoEncoderImplTest() {}
virtual void SetUp() { virtual void SetUp() {
task_runner_ = new test::FakeTaskRunner(&testing_clock_); task_runner_ = new test::FakeTaskRunner(&testing_clock_);
...@@ -92,9 +92,8 @@ class VideoEncoderTest : public ::testing::Test { ...@@ -92,9 +92,8 @@ class VideoEncoderTest : public ::testing::Test {
} }
void Configure(uint8 max_unacked_frames) { void Configure(uint8 max_unacked_frames) {
video_encoder_.reset(new VideoEncoder(cast_environment_, video_config_, video_encoder_.reset(new VideoEncoderImpl(cast_environment_, video_config_,
max_unacked_frames)); max_unacked_frames));
video_encoder_controller_ = video_encoder_.get();
} }
base::SimpleTestTickClock testing_clock_; base::SimpleTestTickClock testing_clock_;
...@@ -102,13 +101,12 @@ class VideoEncoderTest : public ::testing::Test { ...@@ -102,13 +101,12 @@ class VideoEncoderTest : public ::testing::Test {
VideoSenderConfig video_config_; VideoSenderConfig video_config_;
scoped_refptr<test::FakeTaskRunner> task_runner_; scoped_refptr<test::FakeTaskRunner> task_runner_;
scoped_ptr<VideoEncoder> video_encoder_; scoped_ptr<VideoEncoder> video_encoder_;
VideoEncoderController* video_encoder_controller_;
scoped_refptr<media::VideoFrame> video_frame_; scoped_refptr<media::VideoFrame> video_frame_;
scoped_refptr<CastEnvironment> cast_environment_; scoped_refptr<CastEnvironment> cast_environment_;
}; };
TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) { TEST_F(VideoEncoderImplTest, EncodePattern30fpsRunningOutOfAck) {
Configure(3); Configure(3);
VideoEncoder::FrameEncodedCallback frame_encoded_callback = VideoEncoder::FrameEncodedCallback frame_encoded_callback =
...@@ -123,20 +121,20 @@ TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) { ...@@ -123,20 +121,20 @@ TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
task_runner_->RunTasks(); task_runner_->RunTasks();
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
video_encoder_controller_->LatestFrameIdToReference(0); video_encoder_->LatestFrameIdToReference(0);
test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
video_encoder_controller_->LatestFrameIdToReference(1); video_encoder_->LatestFrameIdToReference(1);
test_video_encoder_callback_->SetExpectedResult(false, 2, 1, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 2, 1, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(2); video_encoder_->LatestFrameIdToReference(2);
for (int i = 3; i < 6; ++i) { for (int i = 3; i < 6; ++i) {
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
...@@ -149,7 +147,7 @@ TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) { ...@@ -149,7 +147,7 @@ TEST_F(VideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
// TODO(pwestin): Re-enabled after redesign the encoder to control number of // TODO(pwestin): Re-enabled after redesign the encoder to control number of
// frames in flight. // frames in flight.
TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) { TEST_F(VideoEncoderImplTest,DISABLED_EncodePattern60fpsRunningOutOfAck) {
video_config_.max_number_of_video_buffers_used = 1; video_config_.max_number_of_video_buffers_used = 1;
Configure(6); Configure(6);
...@@ -164,21 +162,21 @@ TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) { ...@@ -164,21 +162,21 @@ TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) {
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(0); video_encoder_->LatestFrameIdToReference(0);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(1); video_encoder_->LatestFrameIdToReference(1);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(2); video_encoder_->LatestFrameIdToReference(2);
for (int i = 3; i < 9; ++i) { for (int i = 3; i < 9; ++i) {
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
...@@ -191,7 +189,8 @@ TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) { ...@@ -191,7 +189,8 @@ TEST_F(VideoEncoderTest,DISABLED_EncodePattern60fpsRunningOutOfAck) {
// TODO(pwestin): Re-enabled after redesign the encoder to control number of // TODO(pwestin): Re-enabled after redesign the encoder to control number of
// frames in flight. // frames in flight.
TEST_F(VideoEncoderTest, DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) { TEST_F(VideoEncoderImplTest,
DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) {
Configure(12); Configure(12);
base::TimeTicks capture_time; base::TimeTicks capture_time;
...@@ -205,35 +204,35 @@ TEST_F(VideoEncoderTest, DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) { ...@@ -205,35 +204,35 @@ TEST_F(VideoEncoderTest, DISABLED_EncodePattern60fps200msDelayRunningOutOfAck) {
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(0); video_encoder_->LatestFrameIdToReference(0);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 1, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(1); video_encoder_->LatestFrameIdToReference(1);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 2, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(2); video_encoder_->LatestFrameIdToReference(2);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 3, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 3, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(3); video_encoder_->LatestFrameIdToReference(3);
capture_time += base::TimeDelta::FromMilliseconds(33); capture_time += base::TimeDelta::FromMilliseconds(33);
test_video_encoder_callback_->SetExpectedResult(false, 4, 0, capture_time); test_video_encoder_callback_->SetExpectedResult(false, 4, 0, capture_time);
EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time, EXPECT_TRUE(video_encoder_->EncodeVideoFrame(video_frame_, capture_time,
frame_encoded_callback)); frame_encoded_callback));
task_runner_->RunTasks(); task_runner_->RunTasks();
video_encoder_controller_->LatestFrameIdToReference(4); video_encoder_->LatestFrameIdToReference(4);
for (int i = 5; i < 17; ++i) { for (int i = 5; i < 17; ++i) {
test_video_encoder_callback_->SetExpectedResult(false, i, 4, capture_time); test_video_encoder_callback_->SetExpectedResult(false, i, 4, capture_time);
......
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
#include "crypto/symmetric_key.h" #include "crypto/symmetric_key.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/transport/pacing/paced_sender.h" #include "media/cast/transport/pacing/paced_sender.h"
#include "media/cast/video_sender/video_encoder.h" #include "media/cast/video_sender/external_video_encoder.h"
#include "media/cast/video_sender/video_encoder_impl.h"
namespace media { namespace media {
namespace cast { namespace cast {
...@@ -53,7 +54,7 @@ class LocalRtpVideoSenderStatistics : public RtpSenderStatistics { ...@@ -53,7 +54,7 @@ class LocalRtpVideoSenderStatistics : public RtpSenderStatistics {
VideoSender::VideoSender( VideoSender::VideoSender(
scoped_refptr<CastEnvironment> cast_environment, scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config, const VideoSenderConfig& video_config,
VideoEncoderController* const video_encoder_controller, const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
transport::PacedPacketSender* const paced_packet_sender) transport::PacedPacketSender* const paced_packet_sender)
: rtp_max_delay_( : rtp_max_delay_(
base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)), base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)),
...@@ -84,12 +85,11 @@ VideoSender::VideoSender( ...@@ -84,12 +85,11 @@ VideoSender::VideoSender(
new LocalRtpVideoSenderStatistics(rtp_sender_.get())); new LocalRtpVideoSenderStatistics(rtp_sender_.get()));
if (video_config.use_external_encoder) { if (video_config.use_external_encoder) {
DCHECK(video_encoder_controller) << "Invalid argument"; video_encoder_.reset(new ExternalVideoEncoder(cast_environment,
video_encoder_controller_ = video_encoder_controller; video_config, gpu_factories));
} else { } else {
video_encoder_.reset(new VideoEncoder(cast_environment, video_config, video_encoder_.reset(new VideoEncoderImpl(cast_environment, video_config,
max_unacked_frames_)); max_unacked_frames_));
video_encoder_controller_ = video_encoder_.get();
} }
if (video_config.aes_iv_mask.size() == kAesKeySize && if (video_config.aes_iv_mask.size() == kAesKeySize &&
...@@ -103,7 +103,7 @@ VideoSender::VideoSender( ...@@ -103,7 +103,7 @@ VideoSender::VideoSender(
std::string()); std::string());
} else if (video_config.aes_iv_mask.size() != 0 || } else if (video_config.aes_iv_mask.size() != 0 ||
video_config.aes_key.size() != 0) { video_config.aes_key.size() != 0) {
DCHECK(false) << "Invalid crypto configuration"; NOTREACHED() << "Invalid crypto configuration";
} }
rtcp_.reset(new Rtcp( rtcp_.reset(new Rtcp(
...@@ -147,17 +147,6 @@ void VideoSender::InsertRawVideoFrame( ...@@ -147,17 +147,6 @@ void VideoSender::InsertRawVideoFrame(
} }
} }
void VideoSender::InsertCodedVideoFrame(const EncodedVideoFrame* encoded_frame,
const base::TimeTicks& capture_time,
const base::Closure callback) {
DCHECK(!video_encoder_.get()) << "Invalid state";
DCHECK(encoded_frame) << "Invalid argument";
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
SendEncodedVideoFrame(encoded_frame, capture_time);
cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
}
void VideoSender::SendEncodedVideoFrameMainThread( void VideoSender::SendEncodedVideoFrameMainThread(
scoped_ptr<EncodedVideoFrame> video_frame, scoped_ptr<EncodedVideoFrame> video_frame,
const base::TimeTicks& capture_time) { const base::TimeTicks& capture_time) {
...@@ -312,7 +301,7 @@ void VideoSender::ResendCheck() { ...@@ -312,7 +301,7 @@ void VideoSender::ResendCheck() {
if (time_since_last_send > rtp_max_delay_) { if (time_since_last_send > rtp_max_delay_) {
if (last_acked_frame_id_ == -1) { if (last_acked_frame_id_ == -1) {
// We have not received any ack, send a key frame. // We have not received any ack, send a key frame.
video_encoder_controller_->GenerateKeyFrame(); video_encoder_->GenerateKeyFrame();
last_acked_frame_id_ = -1; last_acked_frame_id_ = -1;
last_sent_frame_id_ = -1; last_sent_frame_id_ = -1;
UpdateFramesInFlight(); UpdateFramesInFlight();
...@@ -349,7 +338,7 @@ void VideoSender::ScheduleNextSkippedFramesCheck() { ...@@ -349,7 +338,7 @@ void VideoSender::ScheduleNextSkippedFramesCheck() {
void VideoSender::SkippedFramesCheck() { void VideoSender::SkippedFramesCheck() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
int skip_count = video_encoder_controller_->NumberOfSkippedFrames(); int skip_count = video_encoder_->NumberOfSkippedFrames();
if (skip_count - last_skip_count_ > if (skip_count - last_skip_count_ >
kSkippedFramesThreshold * max_frame_rate_) { kSkippedFramesThreshold * max_frame_rate_) {
// TODO(pwestin): Propagate this up to the application. // TODO(pwestin): Propagate this up to the application.
...@@ -381,14 +370,14 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -381,14 +370,14 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
int resend_frame = -1; int resend_frame = -1;
if (last_sent_frame_id_ == -1) return; if (last_sent_frame_id_ == -1) return;
video_encoder_controller_->LatestFrameIdToReference( video_encoder_->LatestFrameIdToReference(
cast_feedback.ack_frame_id_); cast_feedback.ack_frame_id_);
if (static_cast<uint32>(last_acked_frame_id_ + 1) == if (static_cast<uint32>(last_acked_frame_id_ + 1) ==
cast_feedback.ack_frame_id_) { cast_feedback.ack_frame_id_) {
uint32 new_bitrate = 0; uint32 new_bitrate = 0;
if (congestion_control_.OnAck(rtt, &new_bitrate)) { if (congestion_control_.OnAck(rtt, &new_bitrate)) {
video_encoder_controller_->SetBitRate(new_bitrate); video_encoder_->SetBitRate(new_bitrate);
} }
} }
if (static_cast<uint32>(last_acked_frame_id_) == cast_feedback.ack_frame_id_ if (static_cast<uint32>(last_acked_frame_id_) == cast_feedback.ack_frame_id_
...@@ -414,7 +403,7 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -414,7 +403,7 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
uint32 new_bitrate = 0; uint32 new_bitrate = 0;
if (congestion_control_.OnNack(rtt, &new_bitrate)) { if (congestion_control_.OnNack(rtt, &new_bitrate)) {
video_encoder_controller_->SetBitRate(new_bitrate); video_encoder_->SetBitRate(new_bitrate);
} }
} }
ReceivedAck(cast_feedback.ack_frame_id_); ReceivedAck(cast_feedback.ack_frame_id_);
...@@ -446,11 +435,11 @@ void VideoSender::UpdateFramesInFlight() { ...@@ -446,11 +435,11 @@ void VideoSender::UpdateFramesInFlight() {
VLOG(1) << "Frames in flight; last sent: " << last_sent_frame_id_ VLOG(1) << "Frames in flight; last sent: " << last_sent_frame_id_
<< " last acked:" << last_acked_frame_id_; << " last acked:" << last_acked_frame_id_;
if (frames_in_flight >= max_unacked_frames_) { if (frames_in_flight >= max_unacked_frames_) {
video_encoder_controller_->SkipNextFrame(true); video_encoder_->SkipNextFrame(true);
return; return;
} }
} }
video_encoder_controller_->SkipNextFrame(false); video_encoder_->SkipNextFrame(false);
} }
void VideoSender::ResendFrame(uint32 resend_frame_id) { void VideoSender::ResendFrame(uint32 resend_frame_id) {
......
...@@ -14,8 +14,11 @@ ...@@ -14,8 +14,11 @@
'<(DEPTH)/', '<(DEPTH)/',
], ],
'sources': [ 'sources': [
'external_video_encoder.h',
'external_video_encoder.cc',
'video_encoder.h', 'video_encoder.h',
'video_encoder.cc', 'video_encoder_impl.h',
'video_encoder_impl.cc',
'video_sender.h', 'video_sender.h',
'video_sender.cc', 'video_sender.cc',
], # source ], # source
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include "media/cast/congestion_control/congestion_control.h" #include "media/cast/congestion_control/congestion_control.h"
#include "media/cast/rtcp/rtcp.h" #include "media/cast/rtcp/rtcp.h"
#include "media/cast/transport/rtp_sender/rtp_sender.h" #include "media/cast/transport/rtp_sender/rtp_sender.h"
#include "media/filters/gpu_video_accelerator_factories.h"
#include "media/video/video_encode_accelerator.h"
namespace crypto { namespace crypto {
class Encryptor; class Encryptor;
...@@ -46,7 +48,7 @@ class VideoSender : public base::NonThreadSafe, ...@@ -46,7 +48,7 @@ class VideoSender : public base::NonThreadSafe,
public: public:
VideoSender(scoped_refptr<CastEnvironment> cast_environment, VideoSender(scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config, const VideoSenderConfig& video_config,
VideoEncoderController* const video_encoder_controller, const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
transport::PacedPacketSender* const paced_packet_sender); transport::PacedPacketSender* const paced_packet_sender);
virtual ~VideoSender(); virtual ~VideoSender();
...@@ -59,14 +61,6 @@ class VideoSender : public base::NonThreadSafe, ...@@ -59,14 +61,6 @@ class VideoSender : public base::NonThreadSafe,
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time); const base::TimeTicks& capture_time);
// The video_frame must be valid until the closure callback is called.
// The closure callback is called from the main thread as soon as
// the cast sender is done with the frame; it does not mean that the encoded
// frame has been sent out.
void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
const base::TimeTicks& capture_time,
const base::Closure callback);
// Only called from the main cast thread. // Only called from the main cast thread.
void IncomingRtcpPacket(const uint8* packet, size_t length, void IncomingRtcpPacket(const uint8* packet, size_t length,
const base::Closure callback); const base::Closure callback);
...@@ -123,7 +117,6 @@ class VideoSender : public base::NonThreadSafe, ...@@ -123,7 +117,6 @@ class VideoSender : public base::NonThreadSafe,
scoped_ptr<VideoEncoder> video_encoder_; scoped_ptr<VideoEncoder> video_encoder_;
scoped_ptr<Rtcp> rtcp_; scoped_ptr<Rtcp> rtcp_;
scoped_ptr<transport::RtpSender> rtp_sender_; scoped_ptr<transport::RtpSender> rtp_sender_;
VideoEncoderController* video_encoder_controller_;
uint8 max_unacked_frames_; uint8 max_unacked_frames_;
scoped_ptr<crypto::Encryptor> encryptor_; scoped_ptr<crypto::Encryptor> encryptor_;
scoped_ptr<crypto::SymmetricKey> encryption_key_; scoped_ptr<crypto::SymmetricKey> encryption_key_;
......
...@@ -9,11 +9,11 @@ ...@@ -9,11 +9,11 @@
#include "base/test/simple_test_tick_clock.h" #include "base/test/simple_test_tick_clock.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/cast/test/fake_gpu_video_accelerator_factories.h"
#include "media/cast/test/fake_task_runner.h" #include "media/cast/test/fake_task_runner.h"
#include "media/cast/test/video_utility.h" #include "media/cast/test/video_utility.h"
#include "media/cast/transport/pacing/mock_paced_packet_sender.h" #include "media/cast/transport/pacing/mock_paced_packet_sender.h"
#include "media/cast/transport/pacing/paced_sender.h" #include "media/cast/transport/pacing/paced_sender.h"
#include "media/cast/video_sender/mock_video_encoder_controller.h"
#include "media/cast/video_sender/video_sender.h" #include "media/cast/video_sender/video_sender.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -34,12 +34,13 @@ using testing::AtLeast; ...@@ -34,12 +34,13 @@ using testing::AtLeast;
namespace { namespace {
class PeerVideoSender : public VideoSender { class PeerVideoSender : public VideoSender {
public: public:
PeerVideoSender(scoped_refptr<CastEnvironment> cast_environment, PeerVideoSender(
const VideoSenderConfig& video_config, scoped_refptr<CastEnvironment> cast_environment,
VideoEncoderController* const video_encoder_controller, const VideoSenderConfig& video_config,
transport::PacedPacketSender* const paced_packet_sender) const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
: VideoSender(cast_environment, video_config, transport::PacedPacketSender* const paced_packet_sender)
video_encoder_controller, paced_packet_sender) { : VideoSender(cast_environment, video_config, gpu_factories,
paced_packet_sender) {
} }
using VideoSender::OnReceivedCastFeedback; using VideoSender::OnReceivedCastFeedback;
}; };
...@@ -73,7 +74,9 @@ class VideoSenderTest : public ::testing::Test { ...@@ -73,7 +74,9 @@ class VideoSenderTest : public ::testing::Test {
if (external) { if (external) {
video_sender_.reset(new PeerVideoSender(cast_environment_, video_sender_.reset(new PeerVideoSender(cast_environment_,
video_config, &mock_video_encoder_controller_, &mock_transport_)); video_config,
new test::FakeGpuVideoAcceleratorFactories(task_runner_),
&mock_transport_));
} else { } else {
video_sender_.reset(new PeerVideoSender(cast_environment_, video_config, video_sender_.reset(new PeerVideoSender(cast_environment_, video_config,
NULL, &mock_transport_)); NULL, &mock_transport_));
...@@ -96,7 +99,6 @@ class VideoSenderTest : public ::testing::Test { ...@@ -96,7 +99,6 @@ class VideoSenderTest : public ::testing::Test {
return video_frame; return video_frame;
} }
MockVideoEncoderController mock_video_encoder_controller_;
base::SimpleTestTickClock testing_clock_; base::SimpleTestTickClock testing_clock_;
transport::MockPacedPacketSender mock_transport_; transport::MockPacedPacketSender mock_transport_;
scoped_refptr<test::FakeTaskRunner> task_runner_; scoped_refptr<test::FakeTaskRunner> task_runner_;
...@@ -118,40 +120,27 @@ TEST_F(VideoSenderTest, BuiltInEncoder) { ...@@ -118,40 +120,27 @@ TEST_F(VideoSenderTest, BuiltInEncoder) {
TEST_F(VideoSenderTest, ExternalEncoder) { TEST_F(VideoSenderTest, ExternalEncoder) {
EXPECT_CALL(mock_transport_, SendPackets(_)).Times(1); EXPECT_CALL(mock_transport_, SendPackets(_)).Times(1);
EXPECT_CALL(mock_video_encoder_controller_, SkipNextFrame(false)).Times(1);
InitEncoder(true); InitEncoder(true);
task_runner_->RunTasks();
EncodedVideoFrame video_frame; scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
base::TimeTicks capture_time;
video_frame.codec = kVp8; base::TimeTicks capture_time;
video_frame.key_frame = true; video_sender_->InsertRawVideoFrame(video_frame, capture_time);
video_frame.frame_id = 0;
video_frame.last_referenced_frame_id = 0;
video_frame.data.insert(video_frame.data.begin(), 1000, kPixelValue);
video_sender_->InsertCodedVideoFrame(&video_frame, capture_time, task_runner_->RunTasks();
base::Bind(base::DoNothing));
} }
TEST_F(VideoSenderTest, RtcpTimer) { TEST_F(VideoSenderTest, RtcpTimer) {
EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1)); EXPECT_CALL(mock_transport_, SendPackets(_)).Times(AtLeast(1));
EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1); EXPECT_CALL(mock_transport_, SendRtcpPacket(_)).Times(1);
EXPECT_CALL(mock_video_encoder_controller_, InitEncoder(false);
SkipNextFrame(false)).Times(AtLeast(1));
InitEncoder(true);
EncodedVideoFrame video_frame;
base::TimeTicks capture_time;
video_frame.codec = kVp8; scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
video_frame.key_frame = true;
video_frame.frame_id = 0;
video_frame.last_referenced_frame_id = 0;
video_frame.data.insert(video_frame.data.begin(), 1000, kPixelValue);
video_sender_->InsertCodedVideoFrame(&video_frame, capture_time, base::TimeTicks capture_time;
base::Bind(base::DoNothing)); video_sender_->InsertRawVideoFrame(video_frame, capture_time);
task_runner_->RunTasks();
// Make sure that we send at least one RTCP packet. // Make sure that we send at least one RTCP packet.
base::TimeDelta max_rtcp_timeout = base::TimeDelta max_rtcp_timeout =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment