Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Unified Diff: webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc

Issue 1952443002: [H264][Simulcast] Implement the simulcast logic for h264 encoder Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fix a few issues with the patch Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
index 18eccb25a5d4c1950935b54a060b03ce8006250b..6da71c9bae757a46807b3ef4a5f18775f4dbeb30 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
@@ -11,8 +11,10 @@
#include "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h"
+#include <algorithm>
#include <limits>
+#include "webrtc/common_video/libyuv/include/scaler.h"
#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"
@@ -21,6 +23,7 @@
#include "webrtc/base/logging.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/system_wrappers/include/metrics.h"
+#include "webrtc/video_frame.h"
namespace webrtc {
@@ -50,6 +53,78 @@ int NumberOfThreads(int width, int height, int number_of_cores) {
return 1;
}
+std::vector<int> GetStreamBitratesKbps(const VideoCodec& codec,
+ int bitrate_to_allocate_kbps) {
+ if (codec.numberOfSimulcastStreams <= 1) {
+ return std::vector<int>(1, bitrate_to_allocate_kbps);
+ }
+
+ std::vector<int> bitrates_kbps(codec.numberOfSimulcastStreams);
+ // Allocate min -> target bitrates as long as we have bitrate to spend.
+ size_t last_active_stream = 0;
+ for (size_t i = 0; i < static_cast<size_t>(codec.numberOfSimulcastStreams) &&
+ bitrate_to_allocate_kbps >=
+ static_cast<int>(codec.simulcastStream[i].minBitrate);
+ ++i) {
+ last_active_stream = i;
+ int allocated_bitrate_kbps =
+ std::min(static_cast<int>(codec.simulcastStream[i].targetBitrate),
+ bitrate_to_allocate_kbps);
+ bitrates_kbps[i] = allocated_bitrate_kbps;
+ bitrate_to_allocate_kbps -= allocated_bitrate_kbps;
+ }
+
+ // Spend additional bits on the highest-quality active layer, up to max
+ // bitrate.
+ // TODO(pbos): Consider spending additional bits on last_active_stream-1 down
+ // to 0 and not just the top layer when we have additional bitrate to spend.
+ int allocated_bitrate_kbps = std::min(
+ static_cast<int>(codec.simulcastStream[last_active_stream].maxBitrate -
+ bitrates_kbps[last_active_stream]),
+ bitrate_to_allocate_kbps);
+ bitrates_kbps[last_active_stream] += allocated_bitrate_kbps;
+ bitrate_to_allocate_kbps -= allocated_bitrate_kbps;
+
+ // Make sure we can always send something. Suspending below min bitrate is
+ // controlled outside the codec implementation and is not overriden by this.
+ if (bitrates_kbps[0] < static_cast<int>(codec.simulcastStream[0].minBitrate))
+ bitrates_kbps[0] = static_cast<int>(codec.simulcastStream[0].minBitrate);
+
+ return bitrates_kbps;
+}
+
+uint32_t SumStreamMaxBitrate(int streams, const VideoCodec& codec) {
+ uint32_t bitrate_sum = 0;
+ for (int i = 0; i < streams; ++i) {
+ bitrate_sum += codec.simulcastStream[i].maxBitrate;
+ }
+ return bitrate_sum;
+}
+
+int NumberOfStreams(const VideoCodec& codec) {
+ int streams =
+ codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
+ uint32_t simulcast_max_bitrate = SumStreamMaxBitrate(streams, codec);
+ if (simulcast_max_bitrate == 0) {
+ streams = 1;
+ }
+ return streams;
+}
+
+bool ValidSimulcastResolutions(const VideoCodec& codec, int num_streams) {
+ if (codec.width != codec.simulcastStream[num_streams - 1].width ||
+ codec.height != codec.simulcastStream[num_streams - 1].height) {
+ return false;
+ }
+ for (int i = 0; i < num_streams; ++i) {
+ if (codec.width * codec.simulcastStream[i].height !=
+ codec.height * codec.simulcastStream[i].width) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
static FrameType EVideoFrameType_to_FrameType(EVideoFrameType type) {
@@ -148,16 +223,29 @@ static void RtpFragmentize(EncodedImage* encoded_image,
}
H264EncoderImpl::H264EncoderImpl()
- : openh264_encoder_(nullptr),
- encoded_image_callback_(nullptr),
+ : encoded_image_callback_(nullptr),
has_reported_init_(false),
- has_reported_error_(false) {
+ has_reported_error_(false),
+ key_frame_request_(kMaxSimulcastStreams, false) {
+ encoded_images_.reserve(kMaxSimulcastStreams);
+ encoded_image_buffers_.reserve(kMaxSimulcastStreams);
+ send_streams_.reserve(kMaxSimulcastStreams);
+ encoders_.reserve(kMaxSimulcastStreams);
+ scaled_input_frames_.reserve(kMaxSimulcastStreams);
}
H264EncoderImpl::~H264EncoderImpl() {
Release();
}
+void H264EncoderImpl::SetStreamState(bool send_stream, int stream_idx) {
+ if (send_stream && !send_streams_[stream_idx]) {
+ // Need a key frame if we have not sent this stream before.
+ key_frame_request_[stream_idx] = true;
+ }
+ send_streams_[stream_idx] = send_stream;
+}
+
int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
size_t /*max_payload_size*/) {
@@ -181,23 +269,27 @@ int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings,
ReportError();
return release_ret;
}
- RTC_DCHECK(!openh264_encoder_);
- // Create encoder.
- if (WelsCreateSVCEncoder(&openh264_encoder_) != 0) {
- // Failed to create encoder.
- LOG(LS_ERROR) << "Failed to create OpenH264 encoder";
- RTC_DCHECK(!openh264_encoder_);
- ReportError();
- return WEBRTC_VIDEO_CODEC_ERROR;
+ int number_of_streams = NumberOfStreams(*codec_settings);
+ bool doing_simulcast = (number_of_streams > 1);
+
+ if (doing_simulcast &&
+ !ValidSimulcastResolutions(*codec_settings, number_of_streams)) {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
- RTC_DCHECK(openh264_encoder_);
- if (kOpenH264EncoderDetailedLogging) {
- int trace_level = WELS_LOG_DETAIL;
- openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL,
- &trace_level);
+ // Code expects simulcastStream resolutions to be correct, make sure they are
+ // filled even when there are no simulcast layers.
+ if (codec_settings->numberOfSimulcastStreams == 0) {
+ codec_settings_.simulcastStream[0].width = codec_settings->width;
+ codec_settings_.simulcastStream[0].height = codec_settings->height;
}
- // else WELS_LOG_DEFAULT is used by default.
+
+ encoded_images_.resize(number_of_streams);
+ encoded_image_buffers_.resize(number_of_streams);
+ encoders_.resize(number_of_streams);
+ scaled_input_frames_.resize(number_of_streams);
+ key_frame_request_.resize(number_of_streams);
+ std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
codec_settings_ = *codec_settings;
if (codec_settings_.targetBitrate == 0)
@@ -208,86 +300,127 @@ int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings,
// memset(&p, 0, sizeof(SEncParamBase)) used in Initialize, and SEncParamExt
// which is a superset of SEncParamBase (cleared with GetDefaultParams) used
// in InitializeExt.
- SEncParamExt init_params;
- openh264_encoder_->GetDefaultParams(&init_params);
- if (codec_settings_.mode == kRealtimeVideo) {
- init_params.iUsageType = CAMERA_VIDEO_REAL_TIME;
- } else if (codec_settings_.mode == kScreensharing) {
- init_params.iUsageType = SCREEN_CONTENT_REAL_TIME;
- } else {
- ReportError();
- return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
- }
- init_params.iPicWidth = codec_settings_.width;
- init_params.iPicHeight = codec_settings_.height;
- // |init_params| uses bit/s, |codec_settings_| uses kbit/s.
- init_params.iTargetBitrate = codec_settings_.targetBitrate * 1000;
- init_params.iMaxBitrate = codec_settings_.maxBitrate * 1000;
- // Rate Control mode
- init_params.iRCMode = RC_BITRATE_MODE;
- init_params.fMaxFrameRate = static_cast<float>(codec_settings_.maxFramerate);
-
- // The following parameters are extension parameters (they're in SEncParamExt,
- // not in SEncParamBase).
- init_params.bEnableFrameSkip =
- codec_settings_.codecSpecific.H264.frameDroppingOn;
- // |uiIntraPeriod| - multiple of GOP size
- // |keyFrameInterval| - number of frames
- init_params.uiIntraPeriod =
- codec_settings_.codecSpecific.H264.keyFrameInterval;
- init_params.uiMaxNalSize = 0;
- // Threading model: use auto.
- // 0: auto (dynamic imp. internal encoder)
- // 1: single thread (default value)
- // >1: number of threads
- init_params.iMultipleThreadIdc = NumberOfThreads(init_params.iPicWidth,
- init_params.iPicHeight,
- number_of_cores);
- // The base spatial layer 0 is the only one we use.
- init_params.sSpatialLayers[0].iVideoWidth = init_params.iPicWidth;
- init_params.sSpatialLayers[0].iVideoHeight = init_params.iPicHeight;
- init_params.sSpatialLayers[0].fFrameRate = init_params.fMaxFrameRate;
- init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate;
- init_params.sSpatialLayers[0].iMaxSpatialBitrate = init_params.iMaxBitrate;
- // Slice num according to number of threads.
- init_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
-
- // Initialize.
- if (openh264_encoder_->InitializeExt(&init_params) != 0) {
- LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder";
- Release();
- ReportError();
- return WEBRTC_VIDEO_CODEC_ERROR;
+
+ for (int i = 0; i < number_of_streams; ++i) {
+ // Create encoder.
+ if (WelsCreateSVCEncoder(&encoders_[i]) != 0) {
+ // Failed to create encoder.
+ LOG(LS_ERROR) << "Failed to create OpenH264 encoder";
+ RTC_DCHECK(!encoders_[i]);
+ ReportError();
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ RTC_DCHECK(encoders_[i]);
+ if (kOpenH264EncoderDetailedLogging) {
+ int trace_level = WELS_LOG_DETAIL;
+ encoders_[i]->SetOption(ENCODER_OPTION_TRACE_LEVEL, &trace_level);
+ }
+ SEncParamExt init_params;
+ memset(&init_params, 0, sizeof(SEncParamExt));
+ encoders_[i]->GetDefaultParams(&init_params);
+ if (codec_settings_.mode == kRealtimeVideo) {
+ init_params.iUsageType = CAMERA_VIDEO_REAL_TIME;
+ } else if (codec_settings_.mode == kScreensharing) {
+ init_params.iUsageType = SCREEN_CONTENT_REAL_TIME;
+ } else {
+ return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+ }
+ init_params.iPicWidth = codec_settings_.simulcastStream[i].width;
+ init_params.iPicHeight = codec_settings_.simulcastStream[i].height;
+ // |init_params| uses bit/s, |codec_settings_| uses kbit/s.
+ init_params.iTargetBitrate = codec_settings_.startBitrate * 1000;
+ init_params.iMaxBitrate = codec_settings_.maxBitrate * 1000;
+ // Rate Control mode
+ init_params.iRCMode = RC_BITRATE_MODE;
+ init_params.fMaxFrameRate =
+ static_cast<float>(codec_settings_.maxFramerate);
+
+ // The following parameters are extension parameters
+ // (they're in SEncParamExt, not in SEncParamBase).
+ init_params.bEnableFrameSkip =
+ codec_settings_.codecSpecific.H264.frameDroppingOn;
+ // |uiIntraPeriod| - multiple of GOP size
+ // |keyFrameInterval| - number of frames
+ init_params.uiIntraPeriod =
+ codec_settings_.codecSpecific.H264.keyFrameInterval;
+ init_params.uiMaxNalSize = 0;
+ init_params.iComplexityMode = ECOMPLEXITY_MODE::LOW_COMPLEXITY;
+
+ // Threading model: use auto.
+ // 0: auto (dynamic imp. internal encoder)
+ // 1: single thread (default value)
+ // >1: number of threads
+ init_params.iMultipleThreadIdc = NumberOfThreads(
+ init_params.iPicWidth, init_params.iPicHeight, number_of_cores);
+ // The base spatial layer 0 is the only one we use.
+ init_params.sSpatialLayers[0].iVideoWidth = init_params.iPicWidth;
+ init_params.sSpatialLayers[0].iVideoHeight = init_params.iPicHeight;
+ init_params.sSpatialLayers[0].fFrameRate = init_params.fMaxFrameRate;
+ init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate;
+ init_params.sSpatialLayers[0].iMaxSpatialBitrate = init_params.iMaxBitrate;
+
+ // Slice num according to number of threads.
+ init_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
+ // Initialize.
+ if (encoders_[i]->InitializeExt(&init_params) != 0) {
+ LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder";
+ Release();
+ ReportError();
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ int video_format = EVideoFormatType::videoFormatI420;
+ encoders_[i]->SetOption(ENCODER_OPTION_DATAFORMAT, &video_format);
+ // Initialize encoded image. Default buffer size: size of unencoded data.
+ // allocate memory for encoded image
+ if (encoded_images_[i]._buffer != NULL) {
+ delete[] encoded_images_[i]._buffer;
+ }
+ encoded_images_[i]._size =
+ CalcBufferSize(kI420, codec_settings->simulcastStream[i].width,
+ codec_settings->simulcastStream[i].height);
+ encoded_images_[i]._buffer = new uint8_t[encoded_images_[i]._size];
+ encoded_image_buffers_[i].reset(encoded_images_[i]._buffer);
+ encoded_images_[i]._completeFrame = true;
+ encoded_images_[i]._encodedWidth = 0;
+ encoded_images_[i]._encodedHeight = 0;
+ encoded_images_[i]._length = 0;
+
+ // Initialize scaled input frames.
+ scaled_input_frames_[i] = *new VideoFrame();
+ scaled_input_frames_[i].CreateEmptyFrame(
+ codec_settings->simulcastStream[i].width,
+ codec_settings->simulcastStream[i].height,
+ CalculateYStrideSize(codec_settings->simulcastStream[i].width,
+ codec_settings->simulcastStream[i].height),
+ CalculateUVStrideSize(codec_settings->simulcastStream[i].width,
+ codec_settings->simulcastStream[i].height),
+ CalculateUVStrideSize(codec_settings->simulcastStream[i].width,
+ codec_settings->simulcastStream[i].height));
}
- int video_format = EVideoFormatType::videoFormatI420;
- openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT,
- &video_format);
-
- // Initialize encoded image. Default buffer size: size of unencoded data.
- encoded_image_._size = CalcBufferSize(
- kI420, codec_settings_.width, codec_settings_.height);
- encoded_image_._buffer = new uint8_t[encoded_image_._size];
- encoded_image_buffer_.reset(encoded_image_._buffer);
- encoded_image_._completeFrame = true;
- encoded_image_._encodedWidth = 0;
- encoded_image_._encodedHeight = 0;
- encoded_image_._length = 0;
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t H264EncoderImpl::Release() {
- if (openh264_encoder_) {
- int uninit_ret = openh264_encoder_->Uninitialize();
- if (uninit_ret != 0) {
- LOG(LS_WARNING) << "OpenH264 encoder's Uninitialize() returned "
- << "unsuccessful: " << uninit_ret;
+ while (!encoders_.empty()) {
+ ISVCEncoder* openh264_encoder = encoders_.back();
+ if (openh264_encoder) {
+ int uninit_ret = openh264_encoder->Uninitialize();
+ if (uninit_ret != 0) {
+ LOG(LS_WARNING) << "OpenH264 encoder's Uninitialize() returned "
+ << "unsuccessful: " << uninit_ret;
+ }
+ WelsDestroySVCEncoder(openh264_encoder);
+ openh264_encoder = nullptr;
+ encoders_.pop_back();
+ EncodedImage encoded_image = encoded_images_.back();
+ if (encoded_image._buffer != nullptr) {
+ encoded_image._buffer = nullptr;
+ encoded_image_buffers_.back().reset();
+ }
+ encoded_images_.pop_back();
+ encoded_image_buffers_.pop_back();
}
- WelsDestroySVCEncoder(openh264_encoder_);
- openh264_encoder_ = nullptr;
- }
- if (encoded_image_._buffer != nullptr) {
- encoded_image_._buffer = nullptr;
- encoded_image_buffer_.reset();
}
return WEBRTC_VIDEO_CODEC_OK;
}
@@ -302,21 +435,45 @@ int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) {
if (bitrate <= 0 || framerate <= 0) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
+ if (codec_settings_.maxBitrate > 0 && bitrate > codec_settings_.maxBitrate) {
+ bitrate = codec_settings_.maxBitrate;
+ }
+ if (bitrate < codec_settings_.minBitrate) {
+ bitrate = codec_settings_.minBitrate;
+ }
+ if (codec_settings_.numberOfSimulcastStreams > 0 &&
+ bitrate < codec_settings_.simulcastStream[0].minBitrate) {
+ bitrate = codec_settings_.simulcastStream[0].minBitrate;
+ }
codec_settings_.targetBitrate = bitrate;
codec_settings_.maxFramerate = framerate;
- SBitrateInfo target_bitrate;
- memset(&target_bitrate, 0, sizeof(SBitrateInfo));
- target_bitrate.iLayer = SPATIAL_LAYER_ALL,
- target_bitrate.iBitrate = codec_settings_.targetBitrate * 1000;
- openh264_encoder_->SetOption(ENCODER_OPTION_BITRATE,
- &target_bitrate);
- float max_framerate = static_cast<float>(codec_settings_.maxFramerate);
- openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE,
- &max_framerate);
+ std::vector<int> stream_bitrates =
+ GetStreamBitratesKbps(codec_settings_, bitrate);
+ for (size_t i = 0; i < encoders_.size(); ++i) {
+ SetStreamState(stream_bitrates[i] > 0, i);
+ if (send_streams_[i]) {
+ SBitrateInfo target_bitrate;
+ memset(&target_bitrate, 0, sizeof(SBitrateInfo));
+ target_bitrate.iLayer = SPATIAL_LAYER_ALL,
+ target_bitrate.iBitrate = stream_bitrates[i] * 1000; // bps
+ encoders_[i]->SetOption(ENCODER_OPTION_BITRATE, &target_bitrate);
+ float max_framerate = static_cast<float>(framerate);
+ encoders_[i]->SetOption(ENCODER_OPTION_FRAME_RATE, &max_framerate);
+ }
+ }
return WEBRTC_VIDEO_CODEC_OK;
}
+void H264EncoderImpl::Scale(const VideoFrame& input_frame,
+ VideoFrame* output_frame) {
+ Scaler scaler;
+ scaler.Set(input_frame.width(), input_frame.height(), output_frame->width(),
+ output_frame->height(), webrtc::kI420, webrtc::kI420,
+ webrtc::kScaleBilinear);
+ scaler.Scale(input_frame, output_frame);
+}
+
int32_t H264EncoderImpl::Encode(
const VideoFrame& frame, const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) {
@@ -334,88 +491,111 @@ int32_t H264EncoderImpl::Encode(
ReportError();
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
- if (frame.width() != codec_settings_.width ||
- frame.height() != codec_settings_.height) {
- LOG(LS_WARNING) << "Encoder initialized for " << codec_settings_.width
- << "x" << codec_settings_.height << " but trying to encode "
- << frame.width() << "x" << frame.height() << " frame.";
- ReportError();
- return WEBRTC_VIDEO_CODEC_ERR_SIZE;
- }
- bool force_key_frame = false;
- if (frame_types != nullptr) {
- // We only support a single stream.
- RTC_DCHECK_EQ(frame_types->size(), static_cast<size_t>(1));
- // Skip frame?
- if ((*frame_types)[0] == kEmptyFrame) {
- return WEBRTC_VIDEO_CODEC_OK;
+ std::vector<bool> force_key_frame;
+ force_key_frame.resize(encoders_.size());
+ std::fill(force_key_frame.begin(), force_key_frame.end(), false);
+ for (size_t i = 0; i < key_frame_request_.size() && i < send_streams_.size();
+ ++i) {
+ if (key_frame_request_[i] && send_streams_[i]) {
+ force_key_frame[i] = true;
}
- // Force key frame?
- force_key_frame = (*frame_types)[0] == kVideoFrameKey;
}
- if (force_key_frame) {
- // API doc says ForceIntraFrame(false) does nothing, but calling this
- // function forces a key frame regardless of the |bIDR| argument's value.
- // (If every frame is a key frame we get lag/delays.)
- openh264_encoder_->ForceIntraFrame(true);
+ if (frame_types) {
+ for (size_t i = 0; i < frame_types->size(); ++i) {
+ if ((*frame_types)[i] == kVideoFrameKey) {
+ force_key_frame[i] = true;
+ }
+ }
}
- // EncodeFrame input.
- SSourcePicture picture;
- memset(&picture, 0, sizeof(SSourcePicture));
- picture.iPicWidth = frame.width();
- picture.iPicHeight = frame.height();
- picture.iColorFormat = EVideoFormatType::videoFormatI420;
- picture.uiTimeStamp = frame.ntp_time_ms();
- picture.iStride[0] = frame.stride(kYPlane);
- picture.iStride[1] = frame.stride(kUPlane);
- picture.iStride[2] = frame.stride(kVPlane);
- picture.pData[0] = const_cast<uint8_t*>(frame.buffer(kYPlane));
- picture.pData[1] = const_cast<uint8_t*>(frame.buffer(kUPlane));
- picture.pData[2] = const_cast<uint8_t*>(frame.buffer(kVPlane));
-
- // EncodeFrame output.
- SFrameBSInfo info;
- memset(&info, 0, sizeof(SFrameBSInfo));
-
- // Encode!
- int enc_ret = openh264_encoder_->EncodeFrame(&picture, &info);
- if (enc_ret != 0) {
- LOG(LS_ERROR) << "OpenH264 frame encoding failed, EncodeFrame returned "
- << enc_ret << ".";
- ReportError();
- return WEBRTC_VIDEO_CODEC_ERROR;
- }
+ for (size_t i = 0; i < encoders_.size(); ++i) {
+ if (!send_streams_[i] || (*frame_types)[i] == kEmptyFrame) {
+ continue;
+ }
+ // Scale input to match encode dimensions
+ Scale(frame, &scaled_input_frames_[i]);
+
+ if (scaled_input_frames_[i].width() !=
+ codec_settings_.simulcastStream[i].width ||
+ scaled_input_frames_[i].height() !=
+ codec_settings_.simulcastStream[i].height) {
+ LOG(LS_ERROR) << "Encoder initialized for "
+ << codec_settings_.simulcastStream[i].width << "x"
+ << codec_settings_.simulcastStream[i].height
+ << " but trying to encode "
+ << scaled_input_frames_[i].width() << "x"
+ << scaled_input_frames_[i].height() << " frame.";
+ ReportError();
+ return WEBRTC_VIDEO_CODEC_ERR_SIZE;
+ }
+
+ SSourcePicture picture;
+ memset(&picture, 0, sizeof(SSourcePicture));
+ picture.iPicWidth = scaled_input_frames_[i].width();
+ picture.iPicHeight = scaled_input_frames_[i].height();
+ picture.iColorFormat = EVideoFormatType::videoFormatI420;
+ picture.uiTimeStamp = frame.ntp_time_ms();
+ picture.iStride[0] = scaled_input_frames_[i].stride(kYPlane);
+ picture.iStride[1] = scaled_input_frames_[i].stride(kUPlane);
+ picture.iStride[2] = scaled_input_frames_[i].stride(kVPlane);
+ picture.pData[0] =
+ const_cast<uint8_t*>(scaled_input_frames_[i].buffer(kYPlane));
+ picture.pData[1] =
+ const_cast<uint8_t*>(scaled_input_frames_[i].buffer(kUPlane));
+ picture.pData[2] =
+ const_cast<uint8_t*>(scaled_input_frames_[i].buffer(kVPlane));
+ if (force_key_frame[i]) {
+ // API doc says ForceIntraFrame(false) does nothing, but calling this
+ // function forces a key frame regardless of the |bIDR| argument's value.
+ // (If every frame is a key frame we get lag/delays.)
+ encoders_[i]->ForceIntraFrame(true);
+ std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
+ }
+ // EncodeFrame output.
+ SFrameBSInfo info;
+ memset(&info, 0, sizeof(SFrameBSInfo));
+ int enc_ret = encoders_[i]->EncodeFrame(&picture, &info);
+ if (enc_ret != 0) {
+ LOG(LS_ERROR) << "OpenH264 frame encoding failed, EncodeFrame returned "
+ << enc_ret << ".";
+ ReportError();
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
- encoded_image_._encodedWidth = frame.width();
- encoded_image_._encodedHeight = frame.height();
- encoded_image_._timeStamp = frame.timestamp();
- encoded_image_.ntp_time_ms_ = frame.ntp_time_ms();
- encoded_image_.capture_time_ms_ = frame.render_time_ms();
- encoded_image_.rotation_ = frame.rotation();
- encoded_image_._frameType = EVideoFrameType_to_FrameType(info.eFrameType);
-
- // Split encoded image up into fragments. This also updates |encoded_image_|.
- RTPFragmentationHeader frag_header;
- RtpFragmentize(&encoded_image_, &encoded_image_buffer_, frame, &info,
- &frag_header);
-
- // Encoder can skip frames to save bandwidth in which case
- // |encoded_image_._length| == 0.
- if (encoded_image_._length > 0) {
- // Deliver encoded image.
- CodecSpecificInfo codec_specific;
- codec_specific.codecType = kVideoCodecH264;
- encoded_image_callback_->Encoded(encoded_image_,
- &codec_specific,
- &frag_header);
+ encoded_images_[i]._encodedWidth = codec_settings_.simulcastStream[i].width;
+ encoded_images_[i]._encodedHeight =
+ codec_settings_.simulcastStream[i].height;
+ encoded_images_[i]._timeStamp = frame.timestamp();
+ encoded_images_[i].ntp_time_ms_ = frame.ntp_time_ms();
+ encoded_images_[i].capture_time_ms_ = frame.render_time_ms();
+ encoded_images_[i]._frameType =
+ EVideoFrameType_to_FrameType(info.eFrameType);
+ // Split encoded image up into fragments. This also updates
+ // |encoded_image_|.
+ RTPFragmentationHeader frag_header;
+ RtpFragmentize(&encoded_images_[i], &encoded_image_buffers_[i], frame,
+ &info, &frag_header);
+ if (encoded_images_[i]._length > 0) {
+ // Deliver encoded image.
+ CodecSpecificInfo codec_specific;
+ CodecSpecificInfoH264* h264Info = &(codec_specific.codecSpecific.H264);
+ h264Info->simulcastIdx = i;
+ codec_specific.codecType = kVideoCodecH264;
+ encoded_image_callback_->Encoded(encoded_images_[i], &codec_specific,
+ &frag_header);
+ }
}
return WEBRTC_VIDEO_CODEC_OK;
}
bool H264EncoderImpl::IsInitialized() const {
- return openh264_encoder_ != nullptr;
+ for (auto openh264_encoder : encoders_) {
+ if (openh264_encoder == nullptr) {
+ return false;
+ }
+ }
+ return true;
}
void H264EncoderImpl::ReportInit() {

Powered by Google App Engine
This is Rietveld 408576698