| Index: content/renderer/media_recorder/video_track_recorder.cc
|
| diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
|
| index 4d327e7f28a525e3c62b9e5ebffdc5a39395bec4..24c1ab0e5838ae4a701d9920b1154acd268d469f 100644
|
| --- a/content/renderer/media_recorder/video_track_recorder.cc
|
| +++ b/content/renderer/media_recorder/video_track_recorder.cc
|
| @@ -10,14 +10,14 @@
|
| #include "base/logging.h"
|
| #include "base/macros.h"
|
| #include "base/memory/ptr_util.h"
|
| -#include "base/sys_info.h"
|
| #include "base/task_runner_util.h"
|
| #include "base/threading/thread.h"
|
| #include "base/threading/thread_task_runner_handle.h"
|
| #include "base/time/time.h"
|
| -#include "base/trace_event/trace_event.h"
|
| #include "cc/paint/skia_paint_canvas.h"
|
| #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
|
| +#include "content/renderer/media_recorder/vea_encoder.h"
|
| +#include "content/renderer/media_recorder/vpx_encoder.h"
|
| #include "content/renderer/render_thread_impl.h"
|
| #include "media/base/bind_to_current_loop.h"
|
| #include "media/base/video_frame.h"
|
| @@ -30,21 +30,10 @@
|
| #include "ui/gfx/geometry/size.h"
|
|
|
| #if BUILDFLAG(RTC_USE_H264)
|
| -#include "third_party/openh264/src/codec/api/svc/codec_api.h"
|
| -#include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
|
| -#include "third_party/openh264/src/codec/api/svc/codec_def.h"
|
| +#include "content/renderer/media_recorder/h264_encoder.h"
|
| #endif // #if BUILDFLAG(RTC_USE_H264)
|
|
|
| -extern "C" {
|
| -// VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
|
| -// backwards compatibility for legacy applications using the library.
|
| -#define VPX_CODEC_DISABLE_COMPAT 1
|
| -#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
|
| -#include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
|
| -}
|
| -
|
| using media::VideoFrame;
|
| -using media::VideoFrameMetadata;
|
| using video_track_recorder::kVEAEncoderMinResolutionWidth;
|
| using video_track_recorder::kVEAEncoderMinResolutionHeight;
|
|
|
| @@ -52,13 +41,6 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -// HW encoders expect a nonzero bitrate, so |kVEADefaultBitratePerPixel| is used
|
| -// to estimate bits per second for ~30 fps with ~1/16 compression rate.
|
| -const int kVEADefaultBitratePerPixel = 2;
|
| -// Number of output buffers used to copy the encoded data coming from HW
|
| -// encoders.
|
| -const int kVEAEncoderOutputBufferCount = 4;
|
| -
|
| using CodecId = VideoTrackRecorder::CodecId;
|
|
|
| static const struct {
|
| @@ -160,92 +142,26 @@ media::VideoCodecProfile CodecEnumerator::CodecIdToVEAProfile(CodecId codec) {
|
|
|
| } // anonymous namespace
|
|
|
| -// Base class to describe a generic Encoder, encapsulating all actual encoder
|
| -// (re)configurations, encoding and delivery of received frames. This class is
|
| -// ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via
|
| -// the callback that MediaStreamVideoSink passes along) and to jump back and
|
| -// forth to an internal encoder thread. Moreover, this class:
|
| -// - is created on its parent's thread (usually the main Render thread),
|
| -// that is, |main_task_runner_|.
|
| -// - receives VideoFrames on |origin_task_runner_| and runs OnEncodedVideoCB on
|
| -// that thread as well. This task runner is cached on first frame arrival, and
|
| -// is supposed to be the render IO thread (but this is not enforced);
|
| -// - uses an internal |encoding_task_runner_| for actual encoder interactions,
|
| -// namely configuration, encoding (which might take some time) and destruction.
|
| -// This task runner can be passed on the creation. If nothing is passed, a new
|
| -// encoding thread is created and used.
|
| -class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> {
|
| - public:
|
| - Encoder(const OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second,
|
| - scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner =
|
| - nullptr)
|
| - : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| - encoding_task_runner_(encoding_task_runner),
|
| - paused_(false),
|
| - on_encoded_video_callback_(on_encoded_video_callback),
|
| - bits_per_second_(bits_per_second) {
|
| - DCHECK(!on_encoded_video_callback_.is_null());
|
| - if (encoding_task_runner_)
|
| - return;
|
| - encoding_thread_.reset(new base::Thread("EncodingThread"));
|
| - encoding_thread_->Start();
|
| - encoding_task_runner_ = encoding_thread_->task_runner();
|
| - }
|
| -
|
| - // Start encoding |frame|, returning via |on_encoded_video_callback_|. This
|
| - // call will also trigger an encode configuration upon first frame arrival
|
| - // or parameter change, and an EncodeOnEncodingTaskRunner() to actually
|
| - // encode the frame. If the |frame|'s data is not directly available (e.g.
|
| - // it's a texture) then RetrieveFrameOnMainThread() is called, and if even
|
| - // that fails, black frames are sent instead.
|
| - void StartFrameEncode(const scoped_refptr<VideoFrame>& frame,
|
| - base::TimeTicks capture_timestamp);
|
| - void RetrieveFrameOnMainThread(const scoped_refptr<VideoFrame>& video_frame,
|
| - base::TimeTicks capture_timestamp);
|
| -
|
| - void SetPaused(bool paused);
|
| - virtual bool CanEncodeAlphaChannel() { return false; }
|
| -
|
| - protected:
|
| - friend class base::RefCountedThreadSafe<Encoder>;
|
| - virtual ~Encoder() {
|
| - main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release());
|
| - }
|
| -
|
| - virtual void EncodeOnEncodingTaskRunner(
|
| - scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) = 0;
|
| -
|
| - // Used to shutdown properly on the same thread we were created.
|
| - const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
|
| -
|
| - // Task runner where frames to encode and reply callbacks must happen.
|
| - scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
|
| -
|
| - // Task runner where encoding interactions happen.
|
| - scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner_;
|
| -
|
| - // Optional thread for encoding. Active for the lifetime of VpxEncoder.
|
| - std::unique_ptr<base::Thread> encoding_thread_;
|
| -
|
| - // While |paused_|, frames are not encoded. Used only from |encoding_thread_|.
|
| - bool paused_;
|
| -
|
| - // This callback should be exercised on IO thread.
|
| - const OnEncodedVideoCB on_encoded_video_callback_;
|
| -
|
| - // Target bitrate for video encoding. If 0, a standard bitrate is used.
|
| - const int32_t bits_per_second_;
|
| -
|
| - // Used to retrieve incoming opaque VideoFrames (i.e. VideoFrames backed by
|
| - // textures). Created on-demand on |main_task_runner_|.
|
| - std::unique_ptr<media::SkCanvasVideoRenderer> video_renderer_;
|
| - SkBitmap bitmap_;
|
| - std::unique_ptr<cc::PaintCanvas> canvas_;
|
| +VideoTrackRecorder::Encoder::Encoder(
|
| + const OnEncodedVideoCB& on_encoded_video_callback,
|
| + int32_t bits_per_second,
|
| + scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner)
|
| + : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + encoding_task_runner_(encoding_task_runner),
|
| + paused_(false),
|
| + on_encoded_video_callback_(on_encoded_video_callback),
|
| + bits_per_second_(bits_per_second) {
|
| + DCHECK(!on_encoded_video_callback_.is_null());
|
| + if (encoding_task_runner_)
|
| + return;
|
| + encoding_thread_.reset(new base::Thread("EncodingThread"));
|
| + encoding_thread_->Start();
|
| + encoding_task_runner_ = encoding_thread_->task_runner();
|
| +}
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(Encoder);
|
| -};
|
| +VideoTrackRecorder::Encoder::~Encoder() {
|
| + main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release());
|
| +}
|
|
|
| void VideoTrackRecorder::Encoder::StartFrameEncode(
|
| const scoped_refptr<VideoFrame>& video_frame,
|
| @@ -361,31 +277,8 @@ void VideoTrackRecorder::Encoder::RetrieveFrameOnMainThread(
|
| capture_timestamp));
|
| }
|
|
|
| -void VideoTrackRecorder::Encoder::SetPaused(bool paused) {
|
| - if (!encoding_task_runner_->BelongsToCurrentThread()) {
|
| - encoding_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&Encoder::SetPaused, this, paused));
|
| - return;
|
| - }
|
| - paused_ = paused;
|
| -}
|
| -
|
| -namespace {
|
| -
|
| -// Originally from remoting/codec/scoped_vpx_codec.h.
|
| -// TODO(mcasas): Refactor into a common location.
|
| -struct VpxCodecDeleter {
|
| - void operator()(vpx_codec_ctx_t* codec) {
|
| - if (!codec)
|
| - return;
|
| - vpx_codec_err_t ret = vpx_codec_destroy(codec);
|
| - CHECK_EQ(ret, VPX_CODEC_OK);
|
| - delete codec;
|
| - }
|
| -};
|
| -typedef std::unique_ptr<vpx_codec_ctx_t, VpxCodecDeleter> ScopedVpxCodecCtxPtr;
|
| -
|
| -static void OnFrameEncodeCompleted(
|
| +// static
|
| +void VideoTrackRecorder::Encoder::OnFrameEncodeCompleted(
|
| const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
|
| const media::WebmMuxer::VideoParameters& params,
|
| std::unique_ptr<std::string> data,
|
| @@ -398,831 +291,18 @@ static void OnFrameEncodeCompleted(
|
| capture_timestamp, keyframe);
|
| }
|
|
|
| -static int GetNumberOfThreadsForEncoding() {
|
| - // Do not saturate CPU utilization just for encoding. On a lower-end system
|
| - // with only 1 or 2 cores, use only one thread for encoding. On systems with
|
| - // more cores, allow half of the cores to be used for encoding.
|
| - return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2);
|
| -}
|
| -
|
| -// Class encapsulating VideoEncodeAccelerator interactions.
|
| -// This class is created and destroyed in its owner thread. All other methods
|
| -// operate on the task runner pointed by GpuFactories.
|
| -class VEAEncoder final : public VideoTrackRecorder::Encoder,
|
| - public media::VideoEncodeAccelerator::Client {
|
| - public:
|
| - VEAEncoder(
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second,
|
| - media::VideoCodecProfile codec,
|
| - const gfx::Size& size);
|
| -
|
| - // media::VideoEncodeAccelerator::Client implementation.
|
| - void RequireBitstreamBuffers(unsigned int input_count,
|
| - const gfx::Size& input_coded_size,
|
| - size_t output_buffer_size) override;
|
| - void BitstreamBufferReady(int32_t bitstream_buffer_id,
|
| - size_t payload_size,
|
| - bool key_frame,
|
| - base::TimeDelta timestamp) override;
|
| - void NotifyError(media::VideoEncodeAccelerator::Error error) override;
|
| -
|
| - private:
|
| - using VideoFrameAndTimestamp =
|
| - std::pair<scoped_refptr<media::VideoFrame>, base::TimeTicks>;
|
| - using VideoParamsAndTimestamp =
|
| - std::pair<media::WebmMuxer::VideoParameters, base::TimeTicks>;
|
| -
|
| - void UseOutputBitstreamBufferId(int32_t bitstream_buffer_id);
|
| - void FrameFinished(std::unique_ptr<base::SharedMemory> shm);
|
| -
|
| - // VideoTrackRecorder::Encoder implementation.
|
| - ~VEAEncoder() override;
|
| - void EncodeOnEncodingTaskRunner(scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) override;
|
| -
|
| - void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size);
|
| -
|
| - void DestroyOnEncodingTaskRunner(base::WaitableEvent* async_waiter);
|
| -
|
| - media::GpuVideoAcceleratorFactories* const gpu_factories_;
|
| -
|
| - const media::VideoCodecProfile codec_;
|
| -
|
| - // The underlying VEA to perform encoding on.
|
| - std::unique_ptr<media::VideoEncodeAccelerator> video_encoder_;
|
| -
|
| - // Shared memory buffers for output with the VEA.
|
| - std::vector<std::unique_ptr<base::SharedMemory>> output_buffers_;
|
| -
|
| - // Shared memory buffers for output with the VEA as FIFO.
|
| - std::queue<std::unique_ptr<base::SharedMemory>> input_buffers_;
|
| -
|
| - // Tracks error status.
|
| - bool error_notified_;
|
| -
|
| - // Tracks the last frame that we delay the encode.
|
| - std::unique_ptr<VideoFrameAndTimestamp> last_frame_;
|
| -
|
| - // Size used to initialize encoder.
|
| - gfx::Size input_visible_size_;
|
| -
|
| - // Coded size that encoder requests as input.
|
| - gfx::Size vea_requested_input_coded_size_;
|
| -
|
| - // Frames and corresponding timestamps in encode as FIFO.
|
| - std::queue<VideoParamsAndTimestamp> frames_in_encode_;
|
| -};
|
| -
|
| -// Class encapsulating all libvpx interactions for VP8/VP9 encoding.
|
| -class VpxEncoder final : public VideoTrackRecorder::Encoder {
|
| - public:
|
| - static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread,
|
| - ScopedVpxCodecCtxPtr encoder);
|
| -
|
| - VpxEncoder(
|
| - bool use_vp9,
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second);
|
| -
|
| - private:
|
| - // VideoTrackRecorder::Encoder implementation.
|
| - ~VpxEncoder() override;
|
| - void EncodeOnEncodingTaskRunner(scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) override;
|
| - bool CanEncodeAlphaChannel() override { return true; }
|
| -
|
| - void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size,
|
| - vpx_codec_enc_cfg_t* codec_config,
|
| - ScopedVpxCodecCtxPtr* encoder);
|
| - void DoEncode(vpx_codec_ctx_t* const encoder,
|
| - const gfx::Size& frame_size,
|
| - uint8_t* const data,
|
| - uint8_t* const y_plane,
|
| - int y_stride,
|
| - uint8_t* const u_plane,
|
| - int u_stride,
|
| - uint8_t* const v_plane,
|
| - int v_stride,
|
| - const base::TimeDelta& duration,
|
| - bool force_keyframe,
|
| - std::string* const output_data,
|
| - bool* const keyframe);
|
| -
|
| - // Returns true if |codec_config| has been filled in at least once.
|
| - bool IsInitialized(const vpx_codec_enc_cfg_t& codec_config) const;
|
| -
|
| - // Estimate the frame duration from |frame| and |last_frame_timestamp_|.
|
| - base::TimeDelta EstimateFrameDuration(const scoped_refptr<VideoFrame>& frame);
|
| -
|
| - // Force usage of VP9 for encoding, instead of VP8 which is the default.
|
| - const bool use_vp9_;
|
| -
|
| - // VPx internal objects: configuration and encoder. |encoder_| is a special
|
| - // scoped pointer to guarantee proper destruction, particularly when
|
| - // reconfiguring due to parameters change. Only used on |encoding_thread_|.
|
| - vpx_codec_enc_cfg_t codec_config_;
|
| - ScopedVpxCodecCtxPtr encoder_;
|
| -
|
| - vpx_codec_enc_cfg_t alpha_codec_config_;
|
| - ScopedVpxCodecCtxPtr alpha_encoder_;
|
| -
|
| - std::vector<uint8_t> alpha_dummy_planes_;
|
| - size_t v_plane_offset_;
|
| - size_t u_plane_stride_;
|
| - size_t v_plane_stride_;
|
| - bool last_frame_had_alpha_ = false;
|
| -
|
| - // The |VideoFrame::timestamp()| of the last encoded frame. This is used to
|
| - // predict the duration of the next frame. Only used on |encoding_thread_|.
|
| - base::TimeDelta last_frame_timestamp_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(VpxEncoder);
|
| -};
|
| -
|
| -#if BUILDFLAG(RTC_USE_H264)
|
| -
|
| -struct ISVCEncoderDeleter {
|
| - void operator()(ISVCEncoder* codec) {
|
| - if (!codec)
|
| - return;
|
| - const int uninit_ret = codec->Uninitialize();
|
| - CHECK_EQ(cmResultSuccess, uninit_ret);
|
| - WelsDestroySVCEncoder(codec);
|
| - }
|
| -};
|
| -typedef std::unique_ptr<ISVCEncoder, ISVCEncoderDeleter> ScopedISVCEncoderPtr;
|
| -
|
| -// Class encapsulating all openh264 interactions for H264 encoding.
|
| -class H264Encoder final : public VideoTrackRecorder::Encoder {
|
| - public:
|
| - static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread,
|
| - ScopedISVCEncoderPtr encoder);
|
| -
|
| - H264Encoder(
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second);
|
| -
|
| - private:
|
| - // VideoTrackRecorder::Encoder implementation.
|
| - ~H264Encoder() override;
|
| - void EncodeOnEncodingTaskRunner(scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) override;
|
| -
|
| - void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size);
|
| -
|
| - // |openh264_encoder_| is a special scoped pointer to guarantee proper
|
| - // destruction, also when reconfiguring due to parameters change. Only used on
|
| - // |encoding_thread_|.
|
| - gfx::Size configured_size_;
|
| - ScopedISVCEncoderPtr openh264_encoder_;
|
| -
|
| - // The |VideoFrame::timestamp()| of the first received frame. Only used on
|
| - // |encoding_thread_|.
|
| - base::TimeTicks first_frame_timestamp_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(H264Encoder);
|
| -};
|
| -
|
| -#endif // #if BUILDFLAG(RTC_USE_H264)
|
| -
|
| -VEAEncoder::VEAEncoder(
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second,
|
| - media::VideoCodecProfile codec,
|
| - const gfx::Size& size)
|
| - : Encoder(on_encoded_video_callback,
|
| - bits_per_second > 0 ? bits_per_second
|
| - : size.GetArea() * kVEADefaultBitratePerPixel,
|
| - RenderThreadImpl::current()->GetGpuFactories()->GetTaskRunner()),
|
| - gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
|
| - codec_(codec),
|
| - error_notified_(false) {
|
| - DCHECK(gpu_factories_);
|
| - DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth);
|
| - DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight);
|
| -
|
| - encoding_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner,
|
| - this, size));
|
| -}
|
| -
|
| -VEAEncoder::~VEAEncoder() {
|
| - base::WaitableEvent release_waiter(
|
| - base::WaitableEvent::ResetPolicy::MANUAL,
|
| - base::WaitableEvent::InitialState::NOT_SIGNALED);
|
| - // base::Unretained is safe because the class will be alive until
|
| - // |release_waiter| is signaled.
|
| - // TODO(emircan): Consider refactoring media::VideoEncodeAccelerator to avoid
|
| - // using naked pointers and using DeleteSoon() here, see
|
| - // http://crbug.com/701627.
|
| - // It is currently unsafe because |video_encoder_| might be in use on another
|
| - // function on |encoding_task_runner_|, see http://crbug.com/701030.
|
| - encoding_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&VEAEncoder::DestroyOnEncodingTaskRunner,
|
| - base::Unretained(this), &release_waiter));
|
| - release_waiter.Wait();
|
| -}
|
| -
|
| -void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
|
| - const gfx::Size& input_coded_size,
|
| - size_t output_buffer_size) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - vea_requested_input_coded_size_ = input_coded_size;
|
| - output_buffers_.clear();
|
| - std::queue<std::unique_ptr<base::SharedMemory>>().swap(input_buffers_);
|
| -
|
| - for (int i = 0; i < kVEAEncoderOutputBufferCount; ++i) {
|
| - std::unique_ptr<base::SharedMemory> shm =
|
| - gpu_factories_->CreateSharedMemory(output_buffer_size);
|
| - if (shm)
|
| - output_buffers_.push_back(base::WrapUnique(shm.release()));
|
| - }
|
| -
|
| - for (size_t i = 0; i < output_buffers_.size(); ++i)
|
| - UseOutputBitstreamBufferId(i);
|
| -}
|
| -
|
| -void VEAEncoder::BitstreamBufferReady(int32_t bitstream_buffer_id,
|
| - size_t payload_size,
|
| - bool keyframe,
|
| - base::TimeDelta timestamp) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - base::SharedMemory* output_buffer =
|
| - output_buffers_[bitstream_buffer_id].get();
|
| -
|
| - std::unique_ptr<std::string> data(new std::string);
|
| - data->append(reinterpret_cast<char*>(output_buffer->memory()), payload_size);
|
| -
|
| - const auto front_frame = frames_in_encode_.front();
|
| - frames_in_encode_.pop();
|
| - origin_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
|
| - front_frame.first, base::Passed(&data), nullptr,
|
| - front_frame.second, keyframe));
|
| - UseOutputBitstreamBufferId(bitstream_buffer_id);
|
| -}
|
| -
|
| -void VEAEncoder::NotifyError(media::VideoEncodeAccelerator::Error error) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - // TODO(emircan): Notify the owner via a callback.
|
| - error_notified_ = true;
|
| -}
|
| -
|
| -void VEAEncoder::UseOutputBitstreamBufferId(int32_t bitstream_buffer_id) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
|
| - bitstream_buffer_id, output_buffers_[bitstream_buffer_id]->handle(),
|
| - output_buffers_[bitstream_buffer_id]->mapped_size()));
|
| -}
|
| -
|
| -void VEAEncoder::FrameFinished(std::unique_ptr<base::SharedMemory> shm) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - input_buffers_.push(std::move(shm));
|
| -}
|
| -
|
| -void VEAEncoder::EncodeOnEncodingTaskRunner(
|
| - scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - if (input_visible_size_ != frame->visible_rect().size() && video_encoder_)
|
| - video_encoder_.reset();
|
| -
|
| - if (!video_encoder_)
|
| - ConfigureEncoderOnEncodingTaskRunner(frame->visible_rect().size());
|
| -
|
| - if (error_notified_) {
|
| - DVLOG(3) << "An error occurred in VEA encoder";
|
| - return;
|
| - }
|
| -
|
| - // Drop frames if there is no output buffers available.
|
| - if (output_buffers_.empty()) {
|
| - // TODO(emircan): Investigate if resetting encoder would help.
|
| - DVLOG(3) << "Might drop frame.";
|
| - last_frame_.reset(
|
| - new std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>(
|
| - frame, capture_timestamp));
|
| - return;
|
| - }
|
| -
|
| - // If first frame hasn't been encoded, do it first.
|
| - if (last_frame_) {
|
| - std::unique_ptr<VideoFrameAndTimestamp> last_frame(last_frame_.release());
|
| - EncodeOnEncodingTaskRunner(last_frame->first, last_frame->second);
|
| - }
|
| -
|
| - // Lower resolutions may fall back to SW encoder in some platforms, i.e. Mac.
|
| - // In that case, the encoder expects more frames before returning result.
|
| - // Therefore, a copy is necessary to release the current frame.
|
| - // Only STORAGE_SHMEM backed frames can be shared with GPU process, therefore
|
| - // a copy is required for other storage types.
|
| - scoped_refptr<media::VideoFrame> video_frame = frame;
|
| - if (video_frame->storage_type() != VideoFrame::STORAGE_SHMEM ||
|
| - vea_requested_input_coded_size_ != frame->coded_size() ||
|
| - input_visible_size_.width() < kVEAEncoderMinResolutionWidth ||
|
| - input_visible_size_.height() < kVEAEncoderMinResolutionHeight) {
|
| - // Create SharedMemory backed input buffers as necessary. These SharedMemory
|
| - // instances will be shared with GPU process.
|
| - std::unique_ptr<base::SharedMemory> input_buffer;
|
| - const size_t desired_mapped_size = media::VideoFrame::AllocationSize(
|
| - media::PIXEL_FORMAT_I420, vea_requested_input_coded_size_);
|
| - if (input_buffers_.empty()) {
|
| - input_buffer = gpu_factories_->CreateSharedMemory(desired_mapped_size);
|
| - } else {
|
| - do {
|
| - input_buffer = std::move(input_buffers_.front());
|
| - input_buffers_.pop();
|
| - } while (!input_buffers_.empty() &&
|
| - input_buffer->mapped_size() < desired_mapped_size);
|
| - if (!input_buffer || input_buffer->mapped_size() < desired_mapped_size)
|
| - return;
|
| - }
|
| -
|
| - video_frame = media::VideoFrame::WrapExternalSharedMemory(
|
| - media::PIXEL_FORMAT_I420, vea_requested_input_coded_size_,
|
| - gfx::Rect(input_visible_size_), input_visible_size_,
|
| - reinterpret_cast<uint8_t*>(input_buffer->memory()),
|
| - input_buffer->mapped_size(), input_buffer->handle(), 0,
|
| - frame->timestamp());
|
| - video_frame->AddDestructionObserver(media::BindToCurrentLoop(
|
| - base::Bind(&VEAEncoder::FrameFinished, this,
|
| - base::Passed(std::move(input_buffer)))));
|
| - libyuv::I420Copy(frame->visible_data(media::VideoFrame::kYPlane),
|
| - frame->stride(media::VideoFrame::kYPlane),
|
| - frame->visible_data(media::VideoFrame::kUPlane),
|
| - frame->stride(media::VideoFrame::kUPlane),
|
| - frame->visible_data(media::VideoFrame::kVPlane),
|
| - frame->stride(media::VideoFrame::kVPlane),
|
| - video_frame->visible_data(media::VideoFrame::kYPlane),
|
| - video_frame->stride(media::VideoFrame::kYPlane),
|
| - video_frame->visible_data(media::VideoFrame::kUPlane),
|
| - video_frame->stride(media::VideoFrame::kUPlane),
|
| - video_frame->visible_data(media::VideoFrame::kVPlane),
|
| - video_frame->stride(media::VideoFrame::kVPlane),
|
| - input_visible_size_.width(), input_visible_size_.height());
|
| - }
|
| - frames_in_encode_.push(std::make_pair(
|
| - media::WebmMuxer::VideoParameters(frame), capture_timestamp));
|
| -
|
| - video_encoder_->Encode(video_frame, false);
|
| -}
|
| -
|
| -void VEAEncoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
|
| - DCHECK_GT(bits_per_second_, 0);
|
| -
|
| - input_visible_size_ = size;
|
| - video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator();
|
| - if (!video_encoder_ ||
|
| - !video_encoder_->Initialize(media::PIXEL_FORMAT_I420, input_visible_size_,
|
| - codec_, bits_per_second_, this)) {
|
| - NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
|
| - }
|
| -}
|
| -
|
| -void VEAEncoder::DestroyOnEncodingTaskRunner(
|
| - base::WaitableEvent* async_waiter) {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - video_encoder_.reset();
|
| - async_waiter->Signal();
|
| -}
|
| -
|
| -// static
|
| -void VpxEncoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread,
|
| - ScopedVpxCodecCtxPtr encoder) {
|
| - DCHECK(encoding_thread->IsRunning());
|
| - encoding_thread->Stop();
|
| - // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope.
|
| -}
|
| -
|
| -VpxEncoder::VpxEncoder(
|
| - bool use_vp9,
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second)
|
| - : Encoder(on_encoded_video_callback, bits_per_second),
|
| - use_vp9_(use_vp9) {
|
| - codec_config_.g_timebase.den = 0; // Not initialized.
|
| - alpha_codec_config_.g_timebase.den = 0; // Not initialized.
|
| - DCHECK(encoding_thread_->IsRunning());
|
| -}
|
| -
|
| -VpxEncoder::~VpxEncoder() {
|
| - main_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&VpxEncoder::ShutdownEncoder,
|
| - base::Passed(&encoding_thread_),
|
| - base::Passed(&encoder_)));
|
| -}
|
| -
|
| -void VpxEncoder::EncodeOnEncodingTaskRunner(
|
| - scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) {
|
| - TRACE_EVENT0("video", "VpxEncoder::EncodeOnEncodingTaskRunner");
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - const gfx::Size frame_size = frame->visible_rect().size();
|
| - const base::TimeDelta duration = EstimateFrameDuration(frame);
|
| - const media::WebmMuxer::VideoParameters video_params(frame);
|
| -
|
| - if (!IsInitialized(codec_config_) ||
|
| - gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) {
|
| - ConfigureEncoderOnEncodingTaskRunner(frame_size, &codec_config_, &encoder_);
|
| - }
|
| -
|
| - const bool frame_has_alpha = frame->format() == media::PIXEL_FORMAT_YV12A;
|
| - if (frame_has_alpha && (!IsInitialized(alpha_codec_config_) ||
|
| - gfx::Size(alpha_codec_config_.g_w,
|
| - alpha_codec_config_.g_h) != frame_size)) {
|
| - ConfigureEncoderOnEncodingTaskRunner(frame_size, &alpha_codec_config_,
|
| - &alpha_encoder_);
|
| - u_plane_stride_ = media::VideoFrame::RowBytes(
|
| - VideoFrame::kUPlane, frame->format(), frame_size.width());
|
| - v_plane_stride_ = media::VideoFrame::RowBytes(
|
| - VideoFrame::kVPlane, frame->format(), frame_size.width());
|
| - v_plane_offset_ = media::VideoFrame::PlaneSize(
|
| - frame->format(), VideoFrame::kUPlane, frame_size)
|
| - .GetArea();
|
| - alpha_dummy_planes_.resize(
|
| - v_plane_offset_ + media::VideoFrame::PlaneSize(
|
| - frame->format(), VideoFrame::kVPlane, frame_size)
|
| - .GetArea());
|
| - // It is more expensive to encode 0x00, so use 0x80 instead.
|
| - std::fill(alpha_dummy_planes_.begin(), alpha_dummy_planes_.end(), 0x80);
|
| - }
|
| - // If we introduced a new alpha frame, force keyframe.
|
| - const bool force_keyframe = frame_has_alpha && !last_frame_had_alpha_;
|
| - last_frame_had_alpha_ = frame_has_alpha;
|
| -
|
| - std::unique_ptr<std::string> data(new std::string);
|
| - bool keyframe = false;
|
| - DoEncode(encoder_.get(), frame_size, frame->data(VideoFrame::kYPlane),
|
| - frame->visible_data(VideoFrame::kYPlane),
|
| - frame->stride(VideoFrame::kYPlane),
|
| - frame->visible_data(VideoFrame::kUPlane),
|
| - frame->stride(VideoFrame::kUPlane),
|
| - frame->visible_data(VideoFrame::kVPlane),
|
| - frame->stride(VideoFrame::kVPlane), duration, force_keyframe,
|
| - data.get(), &keyframe);
|
| -
|
| - std::unique_ptr<std::string> alpha_data(new std::string);
|
| - if (frame_has_alpha) {
|
| - bool alpha_keyframe = false;
|
| - DoEncode(alpha_encoder_.get(), frame_size, frame->data(VideoFrame::kAPlane),
|
| - frame->visible_data(VideoFrame::kAPlane),
|
| - frame->stride(VideoFrame::kAPlane), alpha_dummy_planes_.data(),
|
| - u_plane_stride_, alpha_dummy_planes_.data() + v_plane_offset_,
|
| - v_plane_stride_, duration, keyframe, alpha_data.get(),
|
| - &alpha_keyframe);
|
| - DCHECK_EQ(keyframe, alpha_keyframe);
|
| - }
|
| - frame = nullptr;
|
| -
|
| - origin_task_runner_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
|
| - video_params, base::Passed(&data), base::Passed(&alpha_data),
|
| - capture_timestamp, keyframe));
|
| -}
|
| -
|
| -void VpxEncoder::DoEncode(vpx_codec_ctx_t* const encoder,
|
| - const gfx::Size& frame_size,
|
| - uint8_t* const data,
|
| - uint8_t* const y_plane,
|
| - int y_stride,
|
| - uint8_t* const u_plane,
|
| - int u_stride,
|
| - uint8_t* const v_plane,
|
| - int v_stride,
|
| - const base::TimeDelta& duration,
|
| - bool force_keyframe,
|
| - std::string* const output_data,
|
| - bool* const keyframe) {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - vpx_image_t vpx_image;
|
| - vpx_image_t* const result =
|
| - vpx_img_wrap(&vpx_image, VPX_IMG_FMT_I420, frame_size.width(),
|
| - frame_size.height(), 1 /* align */, data);
|
| - DCHECK_EQ(result, &vpx_image);
|
| - vpx_image.planes[VPX_PLANE_Y] = y_plane;
|
| - vpx_image.planes[VPX_PLANE_U] = u_plane;
|
| - vpx_image.planes[VPX_PLANE_V] = v_plane;
|
| - vpx_image.stride[VPX_PLANE_Y] = y_stride;
|
| - vpx_image.stride[VPX_PLANE_U] = u_stride;
|
| - vpx_image.stride[VPX_PLANE_V] = v_stride;
|
| -
|
| - const vpx_codec_flags_t flags = force_keyframe ? VPX_EFLAG_FORCE_KF : 0;
|
| - // Encode the frame. The presentation time stamp argument here is fixed to
|
| - // zero to force the encoder to base its single-frame bandwidth calculations
|
| - // entirely on |predicted_frame_duration|.
|
| - const vpx_codec_err_t ret =
|
| - vpx_codec_encode(encoder, &vpx_image, 0 /* pts */,
|
| - duration.InMicroseconds(), flags, VPX_DL_REALTIME);
|
| - DCHECK_EQ(ret, VPX_CODEC_OK)
|
| - << vpx_codec_err_to_string(ret) << ", #" << vpx_codec_error(encoder)
|
| - << " -" << vpx_codec_error_detail(encoder);
|
| -
|
| - *keyframe = false;
|
| - vpx_codec_iter_t iter = NULL;
|
| - const vpx_codec_cx_pkt_t* pkt = NULL;
|
| - while ((pkt = vpx_codec_get_cx_data(encoder, &iter)) != NULL) {
|
| - if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
|
| - continue;
|
| - output_data->assign(static_cast<char*>(pkt->data.frame.buf),
|
| - pkt->data.frame.sz);
|
| - *keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
| - break;
|
| - }
|
| -}
|
| -
|
| -void VpxEncoder::ConfigureEncoderOnEncodingTaskRunner(
|
| - const gfx::Size& size,
|
| - vpx_codec_enc_cfg_t* codec_config,
|
| - ScopedVpxCodecCtxPtr* encoder) {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - if (IsInitialized(*codec_config)) {
|
| - // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less-
|
| - // than-or-equal than the old size, in terms of area, the existing encoder
|
| - // instance could be reused after changing |codec_config->{g_w,g_h}|.
|
| - DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: "
|
| - << gfx::Size(codec_config->g_w, codec_config->g_h).ToString()
|
| - << " --> " << size.ToString() << (use_vp9_ ? " vp9" : " vp8");
|
| - encoder->reset();
|
| - }
|
| -
|
| - const vpx_codec_iface_t* codec_interface =
|
| - use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
|
| - vpx_codec_err_t result = vpx_codec_enc_config_default(
|
| - codec_interface, codec_config, 0 /* reserved */);
|
| - DCHECK_EQ(VPX_CODEC_OK, result);
|
| -
|
| - DCHECK_EQ(320u, codec_config->g_w);
|
| - DCHECK_EQ(240u, codec_config->g_h);
|
| - DCHECK_EQ(256u, codec_config->rc_target_bitrate);
|
| - // Use the selected bitrate or adjust default bit rate to account for the
|
| - // actual size. Note: |rc_target_bitrate| units are kbit per second.
|
| - if (bits_per_second_ > 0) {
|
| - codec_config->rc_target_bitrate = bits_per_second_ / 1000;
|
| - } else {
|
| - codec_config->rc_target_bitrate = size.GetArea() *
|
| - codec_config->rc_target_bitrate /
|
| - codec_config->g_w / codec_config->g_h;
|
| - }
|
| - // Both VP8/VP9 configuration should be Variable BitRate by default.
|
| - DCHECK_EQ(VPX_VBR, codec_config->rc_end_usage);
|
| - if (use_vp9_) {
|
| - // Number of frames to consume before producing output.
|
| - codec_config->g_lag_in_frames = 0;
|
| -
|
| - // DCHECK that the profile selected by default is I420 (magic number 0).
|
| - DCHECK_EQ(0u, codec_config->g_profile);
|
| - } else {
|
| - // VP8 always produces frames instantaneously.
|
| - DCHECK_EQ(0u, codec_config->g_lag_in_frames);
|
| - }
|
| -
|
| - DCHECK(size.width());
|
| - DCHECK(size.height());
|
| - codec_config->g_w = size.width();
|
| - codec_config->g_h = size.height();
|
| - codec_config->g_pass = VPX_RC_ONE_PASS;
|
| -
|
| - // Timebase is the smallest interval used by the stream, can be set to the
|
| - // frame rate or to e.g. microseconds.
|
| - codec_config->g_timebase.num = 1;
|
| - codec_config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
|
| -
|
| - // Let the encoder decide where to place the Keyframes, between min and max.
|
| - // In VPX_KF_AUTO mode libvpx will sometimes emit keyframes regardless of min/
|
| - // max distance out of necessity.
|
| - // Note that due to http://crbug.com/440223, it might be necessary to force a
|
| - // key frame after 10,000frames since decoding fails after 30,000 non-key
|
| - // frames.
|
| - // Forcing a keyframe in regular intervals also allows seeking in the
|
| - // resulting recording with decent performance.
|
| - codec_config->kf_mode = VPX_KF_AUTO;
|
| - codec_config->kf_min_dist = 0;
|
| - codec_config->kf_max_dist = 100;
|
| -
|
| - codec_config->g_threads = GetNumberOfThreadsForEncoding();
|
| -
|
| - // Number of frames to consume before producing output.
|
| - codec_config->g_lag_in_frames = 0;
|
| -
|
| - encoder->reset(new vpx_codec_ctx_t);
|
| - const vpx_codec_err_t ret = vpx_codec_enc_init(
|
| - encoder->get(), codec_interface, codec_config, 0 /* flags */);
|
| - DCHECK_EQ(VPX_CODEC_OK, ret);
|
| -
|
| - if (use_vp9_) {
|
| - // Values of VP8E_SET_CPUUSED greater than 0 will increase encoder speed at
|
| - // the expense of quality up to a maximum value of 8 for VP9, by tuning the
|
| - // target time spent encoding the frame. Go from 8 to 5 (values for real
|
| - // time encoding) depending on the amount of cores available in the system.
|
| - const int kCpuUsed =
|
| - std::max(5, 8 - base::SysInfo::NumberOfProcessors() / 2);
|
| - result = vpx_codec_control(encoder->get(), VP8E_SET_CPUUSED, kCpuUsed);
|
| - DLOG_IF(WARNING, VPX_CODEC_OK != result) << "VP8E_SET_CPUUSED failed";
|
| - }
|
| -}
|
| -
|
| -bool VpxEncoder::IsInitialized(const vpx_codec_enc_cfg_t& codec_config) const {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - return codec_config.g_timebase.den != 0;
|
| -}
|
| -
|
| -base::TimeDelta VpxEncoder::EstimateFrameDuration(
|
| - const scoped_refptr<VideoFrame>& frame) {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - using base::TimeDelta;
|
| - TimeDelta predicted_frame_duration;
|
| - if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION,
|
| - &predicted_frame_duration) ||
|
| - predicted_frame_duration <= TimeDelta()) {
|
| - // The source of the video frame did not provide the frame duration. Use
|
| - // the actual amount of time between the current and previous frame as a
|
| - // prediction for the next frame's duration.
|
| - // TODO(mcasas): This duration estimation could lead to artifacts if the
|
| - // cadence of the received stream is compromised (e.g. camera freeze, pause,
|
| - // remote packet loss). Investigate using GetFrameRate() in this case.
|
| - predicted_frame_duration = frame->timestamp() - last_frame_timestamp_;
|
| - }
|
| - last_frame_timestamp_ = frame->timestamp();
|
| - // Make sure |predicted_frame_duration| is in a safe range of values.
|
| - const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8);
|
| - const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1);
|
| - return std::min(kMaxFrameDuration, std::max(predicted_frame_duration,
|
| - kMinFrameDuration));
|
| -}
|
| -
|
| -#if BUILDFLAG(RTC_USE_H264)
|
| -
|
| -// static
|
| -void H264Encoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread,
|
| - ScopedISVCEncoderPtr encoder) {
|
| - DCHECK(encoding_thread->IsRunning());
|
| - encoding_thread->Stop();
|
| - // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope.
|
| -}
|
| -
|
| -H264Encoder::H264Encoder(
|
| - const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
|
| - int32_t bits_per_second)
|
| - : Encoder(on_encoded_video_callback, bits_per_second) {
|
| - DCHECK(encoding_thread_->IsRunning());
|
| -}
|
| -
|
| -H264Encoder::~H264Encoder() {
|
| - main_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&H264Encoder::ShutdownEncoder,
|
| - base::Passed(&encoding_thread_),
|
| - base::Passed(&openh264_encoder_)));
|
| -}
|
| -
|
| -void H264Encoder::EncodeOnEncodingTaskRunner(
|
| - scoped_refptr<VideoFrame> frame,
|
| - base::TimeTicks capture_timestamp) {
|
| - TRACE_EVENT0("video", "H264Encoder::EncodeOnEncodingTaskRunner");
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| -
|
| - const gfx::Size frame_size = frame->visible_rect().size();
|
| - if (!openh264_encoder_ || configured_size_ != frame_size) {
|
| - ConfigureEncoderOnEncodingTaskRunner(frame_size);
|
| - first_frame_timestamp_ = capture_timestamp;
|
| - }
|
| -
|
| - SSourcePicture picture = {};
|
| - picture.iPicWidth = frame_size.width();
|
| - picture.iPicHeight = frame_size.height();
|
| - picture.iColorFormat = EVideoFormatType::videoFormatI420;
|
| - picture.uiTimeStamp =
|
| - (capture_timestamp - first_frame_timestamp_).InMilliseconds();
|
| - picture.iStride[0] = frame->stride(VideoFrame::kYPlane);
|
| - picture.iStride[1] = frame->stride(VideoFrame::kUPlane);
|
| - picture.iStride[2] = frame->stride(VideoFrame::kVPlane);
|
| - picture.pData[0] = frame->visible_data(VideoFrame::kYPlane);
|
| - picture.pData[1] = frame->visible_data(VideoFrame::kUPlane);
|
| - picture.pData[2] = frame->visible_data(VideoFrame::kVPlane);
|
| -
|
| - SFrameBSInfo info = {};
|
| - if (openh264_encoder_->EncodeFrame(&picture, &info) != cmResultSuccess) {
|
| - NOTREACHED() << "OpenH264 encoding failed";
|
| +void VideoTrackRecorder::Encoder::SetPaused(bool paused) {
|
| + if (!encoding_task_runner_->BelongsToCurrentThread()) {
|
| + encoding_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&Encoder::SetPaused, this, paused));
|
| return;
|
| }
|
| - const media::WebmMuxer::VideoParameters video_params(frame);
|
| - frame = nullptr;
|
| -
|
| - std::unique_ptr<std::string> data(new std::string);
|
| - const uint8_t kNALStartCode[4] = {0, 0, 0, 1};
|
| - for (int layer = 0; layer < info.iLayerNum; ++layer) {
|
| - const SLayerBSInfo& layerInfo = info.sLayerInfo[layer];
|
| - // Iterate NAL units making up this layer, noting fragments.
|
| - size_t layer_len = 0;
|
| - for (int nal = 0; nal < layerInfo.iNalCount; ++nal) {
|
| - // The following DCHECKs make sure that the header of each NAL unit is OK.
|
| - DCHECK_GE(layerInfo.pNalLengthInByte[nal], 4);
|
| - DCHECK_EQ(kNALStartCode[0], layerInfo.pBsBuf[layer_len+0]);
|
| - DCHECK_EQ(kNALStartCode[1], layerInfo.pBsBuf[layer_len+1]);
|
| - DCHECK_EQ(kNALStartCode[2], layerInfo.pBsBuf[layer_len+2]);
|
| - DCHECK_EQ(kNALStartCode[3], layerInfo.pBsBuf[layer_len+3]);
|
| -
|
| - layer_len += layerInfo.pNalLengthInByte[nal];
|
| - }
|
| - // Copy the entire layer's data (including NAL start codes).
|
| - data->append(reinterpret_cast<char*>(layerInfo.pBsBuf), layer_len);
|
| - }
|
| -
|
| - const bool is_key_frame = info.eFrameType == videoFrameTypeIDR;
|
| - origin_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
|
| - video_params, base::Passed(&data), nullptr,
|
| - capture_timestamp, is_key_frame));
|
| + paused_ = paused;
|
| }
|
|
|
| -void H264Encoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
|
| - DCHECK(encoding_task_runner_->BelongsToCurrentThread());
|
| - ISVCEncoder* temp_encoder = nullptr;
|
| - if (WelsCreateSVCEncoder(&temp_encoder) != 0) {
|
| - NOTREACHED() << "Failed to create OpenH264 encoder";
|
| - return;
|
| - }
|
| - openh264_encoder_.reset(temp_encoder);
|
| - configured_size_ = size;
|
| -
|
| -#if DCHECK_IS_ON()
|
| - int trace_level = WELS_LOG_INFO;
|
| - openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, &trace_level);
|
| -#endif
|
| -
|
| - SEncParamExt init_params;
|
| - openh264_encoder_->GetDefaultParams(&init_params);
|
| - init_params.iUsageType = CAMERA_VIDEO_REAL_TIME;
|
| -
|
| - DCHECK_EQ(AUTO_REF_PIC_COUNT, init_params.iNumRefFrame);
|
| - DCHECK(!init_params.bSimulcastAVC);
|
| -
|
| - init_params.uiIntraPeriod = 100; // Same as for VpxEncoder.
|
| - init_params.iPicWidth = size.width();
|
| - init_params.iPicHeight = size.height();
|
| -
|
| - DCHECK_EQ(RC_QUALITY_MODE, init_params.iRCMode);
|
| - DCHECK_EQ(0, init_params.iPaddingFlag);
|
| - DCHECK_EQ(UNSPECIFIED_BIT_RATE, init_params.iTargetBitrate);
|
| - DCHECK_EQ(UNSPECIFIED_BIT_RATE, init_params.iMaxBitrate);
|
| - if (bits_per_second_ > 0) {
|
| - init_params.iRCMode = RC_BITRATE_MODE;
|
| - init_params.iTargetBitrate = bits_per_second_;
|
| - } else {
|
| - init_params.iRCMode = RC_OFF_MODE;
|
| - }
|
| -
|
| - // Threading model: Set to 1 due to https://crbug.com/583348.
|
| - init_params.iMultipleThreadIdc = 1;
|
| -
|
| - // TODO(mcasas): consider reducing complexity if there are few CPUs available.
|
| - init_params.iComplexityMode = MEDIUM_COMPLEXITY;
|
| - DCHECK(!init_params.bEnableDenoise);
|
| - DCHECK(init_params.bEnableFrameSkip);
|
| -
|
| - // The base spatial layer 0 is the only one we use.
|
| - DCHECK_EQ(1, init_params.iSpatialLayerNum);
|
| - init_params.sSpatialLayers[0].iVideoWidth = init_params.iPicWidth;
|
| - init_params.sSpatialLayers[0].iVideoHeight = init_params.iPicHeight;
|
| - init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate;
|
| -
|
| - // When uiSliceMode = SM_FIXEDSLCNUM_SLICE, uiSliceNum = 0 means auto design
|
| - // it with cpu core number.
|
| - // TODO(sprang): Set to 0 when we understand why the rate controller borks
|
| - // when uiSliceNum > 1. See https://github.com/cisco/openh264/issues/2591
|
| - init_params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1;
|
| - init_params.sSpatialLayers[0].sSliceArgument.uiSliceMode =
|
| - SM_FIXEDSLCNUM_SLICE;
|
| -
|
| - if (openh264_encoder_->InitializeExt(&init_params) != cmResultSuccess) {
|
| - NOTREACHED() << "Failed to initialize OpenH264 encoder";
|
| - return;
|
| - }
|
| -
|
| - int pixel_format = EVideoFormatType::videoFormatI420;
|
| - openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, &pixel_format);
|
| +bool VideoTrackRecorder::Encoder::CanEncodeAlphaChannel() {
|
| + return false;
|
| }
|
| -#endif //#if BUILDFLAG(RTC_USE_H264)
|
| -
|
| -} // anonymous namespace
|
|
|
| // static
|
| VideoTrackRecorder::CodecId VideoTrackRecorder::GetPreferredCodecId() {
|
| @@ -1337,9 +417,4 @@ void VideoTrackRecorder::InitializeEncoder(
|
| false);
|
| }
|
|
|
| -bool VideoTrackRecorder::CanEncodeAlphaChannelForTesting() {
|
| - DCHECK(encoder_);
|
| - return encoder_->CanEncodeAlphaChannel();
|
| -}
|
| -
|
| } // namespace content
|
|
|