Commit 556813c9 authored by Christian Fremerey's avatar Christian Fremerey Committed by Commit Bot

[Media Recorder] Fix video freeze on short recording. Fix tab crash on initialize.

Fixes an issue where VideoTrackRecorder when used in combination with
VEAEncoder and very short recording durations (just 1 frame) would
occasionally hold on to video frames forever, causing frame buffers
from the buffer pool to become permanently blocked, eventually leading
to video capture freezing.

The cause was a circular ownership that would happen if only 1 frame
is recorded.

This CL additionally fixes a crash that could happen on initialization
of VEAEncoder. This issue was revealed during testing of the above fix.
Since it is not 100% if this crash was masked by the other issue, and
would only start occurring with the above fix, I am making a fix for that
part of this CL as well.

The cause for the crash issue was that class VEAEncoder, which uses
RefCountedThreadSafe<>, would post an asynchronous task from its
constructor. If this task would finish executing before the newly
constructed instance was assigned to a scoped_refptr<> by however
called the constructor, the instance would destroy itself, because
of the AddRef() and subsequent Release() done by the posting and
running of the asynchronous task.

The fix for this is to use a new method Initialize() instead of
posting a task from the constructor.

Bug: 859610
Change-Id: I6645aebfaa7659ab0ced0a20fae57eedfb9d84ab
Reviewed-on: https://chromium-review.googlesource.com/1145877
Commit-Queue: Christian Fremerey <chfremer@chromium.org>
Reviewed-by: default avatarEmircan Uysaler <emircan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577735}
parent 4a9ec046
...@@ -55,14 +55,14 @@ VEAEncoder::VEAEncoder( ...@@ -55,14 +55,14 @@ VEAEncoder::VEAEncoder(
DCHECK(gpu_factories_); DCHECK(gpu_factories_);
DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth); DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth);
DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight); DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight);
encoding_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner, this,
size));
} }
VEAEncoder::~VEAEncoder() { VEAEncoder::~VEAEncoder() {
if (encoding_task_runner_->BelongsToCurrentThread()) {
DestroyOnEncodingTaskRunner();
return;
}
base::WaitableEvent release_waiter( base::WaitableEvent release_waiter(
base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent::InitialState::NOT_SIGNALED);
...@@ -79,6 +79,13 @@ VEAEncoder::~VEAEncoder() { ...@@ -79,6 +79,13 @@ VEAEncoder::~VEAEncoder() {
release_waiter.Wait(); release_waiter.Wait();
} }
void VEAEncoder::Initialize(const gfx::Size& resolution) {
encoding_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner, this,
resolution));
}
void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/, void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
const gfx::Size& input_coded_size, const gfx::Size& input_coded_size,
size_t output_buffer_size) { size_t output_buffer_size) {
...@@ -265,7 +272,8 @@ void VEAEncoder::DestroyOnEncodingTaskRunner( ...@@ -265,7 +272,8 @@ void VEAEncoder::DestroyOnEncodingTaskRunner(
base::WaitableEvent* async_waiter) { base::WaitableEvent* async_waiter) {
DCHECK(encoding_task_runner_->BelongsToCurrentThread()); DCHECK(encoding_task_runner_->BelongsToCurrentThread());
video_encoder_.reset(); video_encoder_.reset();
async_waiter->Signal(); if (async_waiter)
async_waiter->Signal();
} }
} // namespace content } // namespace content
...@@ -57,12 +57,13 @@ class VEAEncoder final : public VideoTrackRecorder::Encoder, ...@@ -57,12 +57,13 @@ class VEAEncoder final : public VideoTrackRecorder::Encoder,
// VideoTrackRecorder::Encoder implementation. // VideoTrackRecorder::Encoder implementation.
~VEAEncoder() override; ~VEAEncoder() override;
void Initialize(const gfx::Size& resolution) override;
void EncodeOnEncodingTaskRunner(scoped_refptr<media::VideoFrame> frame, void EncodeOnEncodingTaskRunner(scoped_refptr<media::VideoFrame> frame,
base::TimeTicks capture_timestamp) override; base::TimeTicks capture_timestamp) override;
void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size); void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size);
void DestroyOnEncodingTaskRunner(base::WaitableEvent* async_waiter); void DestroyOnEncodingTaskRunner(base::WaitableEvent* async_waiter = nullptr);
media::GpuVideoAcceleratorFactories* const gpu_factories_; media::GpuVideoAcceleratorFactories* const gpu_factories_;
......
...@@ -163,6 +163,23 @@ media::VideoCodecProfile CodecEnumerator::CodecIdToVEAProfile(CodecId codec) { ...@@ -163,6 +163,23 @@ media::VideoCodecProfile CodecEnumerator::CodecIdToVEAProfile(CodecId codec) {
} // anonymous namespace } // anonymous namespace
VideoTrackRecorder::Counter::Counter() : count_(0u), weak_factory_(this) {}
VideoTrackRecorder::Counter::~Counter() = default;
void VideoTrackRecorder::Counter::IncreaseCount() {
count_++;
}
void VideoTrackRecorder::Counter::DecreaseCount() {
count_--;
}
base::WeakPtr<VideoTrackRecorder::Counter>
VideoTrackRecorder::Counter::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
VideoTrackRecorder::Encoder::Encoder( VideoTrackRecorder::Encoder::Encoder(
const OnEncodedVideoCB& on_encoded_video_callback, const OnEncodedVideoCB& on_encoded_video_callback,
int32_t bits_per_second, int32_t bits_per_second,
...@@ -173,7 +190,7 @@ VideoTrackRecorder::Encoder::Encoder( ...@@ -173,7 +190,7 @@ VideoTrackRecorder::Encoder::Encoder(
paused_(false), paused_(false),
on_encoded_video_callback_(on_encoded_video_callback), on_encoded_video_callback_(on_encoded_video_callback),
bits_per_second_(bits_per_second), bits_per_second_(bits_per_second),
num_frames_in_encode_(0) { num_frames_in_encode_(std::make_unique<VideoTrackRecorder::Counter>()) {
DCHECK(!on_encoded_video_callback_.is_null()); DCHECK(!on_encoded_video_callback_.is_null());
if (encoding_task_runner_) if (encoding_task_runner_)
return; return;
...@@ -184,6 +201,10 @@ VideoTrackRecorder::Encoder::Encoder( ...@@ -184,6 +201,10 @@ VideoTrackRecorder::Encoder::Encoder(
VideoTrackRecorder::Encoder::~Encoder() { VideoTrackRecorder::Encoder::~Encoder() {
main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release()); main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release());
if (origin_task_runner_ && !origin_task_runner_->BelongsToCurrentThread()) {
origin_task_runner_->DeleteSoon(FROM_HERE,
std::move(num_frames_in_encode_));
}
} }
void VideoTrackRecorder::Encoder::StartFrameEncode( void VideoTrackRecorder::Encoder::StartFrameEncode(
...@@ -204,7 +225,7 @@ void VideoTrackRecorder::Encoder::StartFrameEncode( ...@@ -204,7 +225,7 @@ void VideoTrackRecorder::Encoder::StartFrameEncode(
return; return;
} }
if (num_frames_in_encode_ > kMaxNumberOfFramesInEncode) { if (num_frames_in_encode_->count() > kMaxNumberOfFramesInEncode) {
DLOG(WARNING) << "Too many frames are queued up. Dropping this one."; DLOG(WARNING) << "Too many frames are queued up. Dropping this one.";
return; return;
} }
...@@ -226,9 +247,13 @@ void VideoTrackRecorder::Encoder::StartFrameEncode( ...@@ -226,9 +247,13 @@ void VideoTrackRecorder::Encoder::StartFrameEncode(
video_frame, video_frame->format(), video_frame->visible_rect(), video_frame, video_frame->format(), video_frame->visible_rect(),
video_frame->natural_size()); video_frame->natural_size());
} }
wrapped_frame->AddDestructionObserver(media::BindToCurrentLoop(base::Bind( wrapped_frame->AddDestructionObserver(media::BindToCurrentLoop(
&VideoTrackRecorder::Encoder::FrameReleased, this, video_frame))); base::BindOnce(&VideoTrackRecorder::Counter::DecreaseCount,
++num_frames_in_encode_; num_frames_in_encode_->GetWeakPtr())));
wrapped_frame->AddDestructionObserver(
base::BindOnce([](const scoped_refptr<VideoFrame>& video_frame) {},
std::move(video_frame)));
num_frames_in_encode_->IncreaseCount();
encoding_task_runner_->PostTask( encoding_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Encoder::EncodeOnEncodingTaskRunner, this, FROM_HERE, base::BindOnce(&Encoder::EncodeOnEncodingTaskRunner, this,
...@@ -349,12 +374,6 @@ bool VideoTrackRecorder::Encoder::CanEncodeAlphaChannel() { ...@@ -349,12 +374,6 @@ bool VideoTrackRecorder::Encoder::CanEncodeAlphaChannel() {
return false; return false;
} }
void VideoTrackRecorder::Encoder::FrameReleased(
const scoped_refptr<VideoFrame>& frame) {
DCHECK(origin_task_runner_->BelongsToCurrentThread());
--num_frames_in_encode_;
}
// static // static
VideoTrackRecorder::CodecId VideoTrackRecorder::GetPreferredCodecId() { VideoTrackRecorder::CodecId VideoTrackRecorder::GetPreferredCodecId() {
return GetCodecEnumerator()->GetPreferredCodecId(); return GetCodecEnumerator()->GetPreferredCodecId();
...@@ -377,7 +396,7 @@ VideoTrackRecorder::VideoTrackRecorder( ...@@ -377,7 +396,7 @@ VideoTrackRecorder::VideoTrackRecorder(
int32_t bits_per_second, int32_t bits_per_second,
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
: track_(track), : track_(track),
paused_before_init_(false), should_pause_encoder_on_initialization_(false),
main_task_runner_(std::move(main_task_runner)), main_task_runner_(std::move(main_task_runner)),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
...@@ -407,7 +426,7 @@ void VideoTrackRecorder::Pause() { ...@@ -407,7 +426,7 @@ void VideoTrackRecorder::Pause() {
if (encoder_) if (encoder_)
encoder_->SetPaused(true); encoder_->SetPaused(true);
else else
paused_before_init_ = true; should_pause_encoder_on_initialization_ = true;
} }
void VideoTrackRecorder::Resume() { void VideoTrackRecorder::Resume() {
...@@ -415,7 +434,7 @@ void VideoTrackRecorder::Resume() { ...@@ -415,7 +434,7 @@ void VideoTrackRecorder::Resume() {
if (encoder_) if (encoder_)
encoder_->SetPaused(false); encoder_->SetPaused(false);
else else
paused_before_init_ = false; should_pause_encoder_on_initialization_ = false;
} }
void VideoTrackRecorder::OnVideoFrameForTesting( void VideoTrackRecorder::OnVideoFrameForTesting(
...@@ -478,9 +497,10 @@ void VideoTrackRecorder::InitializeEncoder( ...@@ -478,9 +497,10 @@ void VideoTrackRecorder::InitializeEncoder(
NOTREACHED() << "Unsupported codec " << static_cast<int>(codec); NOTREACHED() << "Unsupported codec " << static_cast<int>(codec);
} }
} }
encoder_->Initialize(input_size);
if (paused_before_init_) if (should_pause_encoder_on_initialization_)
encoder_->SetPaused(paused_before_init_); encoder_->SetPaused(should_pause_encoder_on_initialization_);
// StartFrameEncode() will be called on Render IO thread. // StartFrameEncode() will be called on Render IO thread.
MediaStreamVideoSink::ConnectToTrack( MediaStreamVideoSink::ConnectToTrack(
......
...@@ -69,6 +69,22 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink { ...@@ -69,6 +69,22 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
bool is_key_frame)>; bool is_key_frame)>;
using OnErrorCB = base::Closure; using OnErrorCB = base::Closure;
// Wraps a counter in a class in order to enable use of base::WeakPtr<>.
// See https://crbug.com/859610 for why this was added.
class Counter {
public:
Counter();
~Counter();
uint32_t count() const { return count_; }
void IncreaseCount();
void DecreaseCount();
base::WeakPtr<Counter> GetWeakPtr();
private:
uint32_t count_;
base::WeakPtrFactory<Counter> weak_factory_;
};
// Base class to describe a generic Encoder, encapsulating all actual encoder // Base class to describe a generic Encoder, encapsulating all actual encoder
// (re)configurations, encoding and delivery of received frames. This class is // (re)configurations, encoding and delivery of received frames. This class is
// ref-counted to allow the MediaStreamVideoTrack to hold a reference to it // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it
...@@ -91,6 +107,11 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink { ...@@ -91,6 +107,11 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner = scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner =
nullptr); nullptr);
// This must be called exactly once after the constructor and before any of
// the other public methods. Subclasses may choose to override this to
// perform initialization work that cannot be done in the constructor.
virtual void Initialize(const gfx::Size& resolution) {}
// Start encoding |frame|, returning via |on_encoded_video_callback_|. This // Start encoding |frame|, returning via |on_encoded_video_callback_|. This
// call will also trigger an encode configuration upon first frame arrival // call will also trigger an encode configuration upon first frame arrival
// or parameter change, and an EncodeOnEncodingTaskRunner() to actually // or parameter change, and an EncodeOnEncodingTaskRunner() to actually
...@@ -118,6 +139,13 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink { ...@@ -118,6 +139,13 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
friend class base::RefCountedThreadSafe<Encoder>; friend class base::RefCountedThreadSafe<Encoder>;
friend class VideoTrackRecorderTest; friend class VideoTrackRecorderTest;
// This destructor may run on either |main_task_runner|,
// |encoding_task_runner|, or |origin_task_runner_|. Main ownership lies
// with VideoTrackRecorder. Shared ownership is handed out to
// asynchronous tasks running on |encoding_task_runner| for encoding. Shared
// ownership is also handed out to a MediaStreamVideoTrack which pushes
// frames on |origin_task_runner_|. Each of these may end up being the last
// reference.
virtual ~Encoder(); virtual ~Encoder();
virtual void EncodeOnEncodingTaskRunner( virtual void EncodeOnEncodingTaskRunner(
...@@ -150,7 +178,8 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink { ...@@ -150,7 +178,8 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
const int32_t bits_per_second_; const int32_t bits_per_second_;
// Number of frames that we keep the reference alive for encode. // Number of frames that we keep the reference alive for encode.
uint32_t num_frames_in_encode_; // Operated and released exclusively on |origin_task_runner_|.
std::unique_ptr<Counter> num_frames_in_encode_;
// Used to retrieve incoming opaque VideoFrames (i.e. VideoFrames backed by // Used to retrieve incoming opaque VideoFrames (i.e. VideoFrames backed by
// textures). Created on-demand on |main_task_runner_|. // textures). Created on-demand on |main_task_runner_|.
...@@ -203,8 +232,7 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink { ...@@ -203,8 +232,7 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
base::TimeTicks capture_time)> base::TimeTicks capture_time)>
initialize_encoder_callback_; initialize_encoder_callback_;
// Used to track the paused state during the initialization process. bool should_pause_encoder_on_initialization_;
bool paused_before_init_;
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
......
...@@ -137,7 +137,7 @@ class VideoTrackRecorderTest ...@@ -137,7 +137,7 @@ class VideoTrackRecorderTest
} }
uint32_t NumFramesInEncode() { uint32_t NumFramesInEncode() {
return video_track_recorder_->encoder_->num_frames_in_encode_; return video_track_recorder_->encoder_->num_frames_in_encode_->count();
} }
// A ChildProcess is needed to fool the Tracks and Sources into believing they // A ChildProcess is needed to fool the Tracks and Sources into believing they
......
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