Refactor FakeEncoder to avoid writing to a const EncodedImage

Subclasses of FakeEncoder need to fill out the CodecSpecificInfo and
RTPFragmentationHeader, and they also write to the encoded data of the
EncodedImage. This used to be done by subclasses chaining onto the
parent's OnEncodedImage callback, but that's not so nice, since the
EncodedImage argument is passed as a const ref there.

This change introduces a protected method EncodeHook for this purpose.
FakeEncoder calls this prior to calling OnEncodedImage, with non-const
pointers.

In addition, change FakeEncoder to use EncodedImage::Allocate, rather
than explicit new and delete.

Bug: webrtc:9378
Change-Id: Ie8182d1d5224aa3b7f15905612f6dbcebef0a555
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/125880
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26988}
This commit is contained in:
Niels Möller
2019-03-06 10:09:47 +01:00
committed by Commit Bot
parent 9df335374a
commit d738071e63
5 changed files with 82 additions and 96 deletions

View File

@ -616,6 +616,7 @@ rtc_source_set("fake_video_codecs") {
"../rtc_base:rtc_task_queue",
"../rtc_base:sequenced_task_checker",
"../system_wrappers",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:optional",
]
}

View File

@ -16,6 +16,7 @@
#include <memory>
#include <string>
#include "absl/memory/memory.h"
#include "api/video/video_content_type.h"
#include "common_types.h" // NOLINT(build/include)
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
@ -80,7 +81,7 @@ int32_t FakeEncoder::InitEncode(const VideoCodec* config,
}
int32_t FakeEncoder::Encode(const VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
const CodecSpecificInfo* /*codec_specific_info*/,
const std::vector<FrameType>* frame_types) {
unsigned char max_framerate;
unsigned char num_simulcast_streams;
@ -121,16 +122,14 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
continue;
}
CodecSpecificInfo specifics;
specifics.codecType = kVideoCodecGeneric;
std::unique_ptr<uint8_t[]> encoded_buffer(
new uint8_t[frame_info.layers[i].size]);
EncodedImage encoded;
encoded.Allocate(frame_info.layers[i].size);
encoded.set_size(frame_info.layers[i].size);
// Fill the buffer with arbitrary data. Write someting to make Asan happy.
memset(encoded_buffer.get(), 9, frame_info.layers[i].size);
memset(encoded.data(), 9, frame_info.layers[i].size);
// Write a counter to the image to make each frame unique.
WriteCounter(encoded_buffer.get() + frame_info.layers[i].size - 4, counter);
EncodedImage encoded(encoded_buffer.get(), frame_info.layers[i].size,
frame_info.layers[i].size);
WriteCounter(encoded.data() + frame_info.layers[i].size - 4, counter);
encoded.SetTimestamp(input_image.timestamp());
encoded.capture_time_ms_ = input_image.render_time_ms();
encoded._frameType =
@ -142,14 +141,25 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
? VideoContentType::SCREENSHARE
: VideoContentType::UNSPECIFIED;
encoded.SetSpatialIndex(i);
if (callback->OnEncodedImage(encoded, &specifics, nullptr).error !=
EncodedImageCallback::Result::OK) {
CodecSpecificInfo codec_specific;
std::unique_ptr<RTPFragmentationHeader> fragmentation =
EncodeHook(&encoded, &codec_specific);
if (callback->OnEncodedImage(encoded, &codec_specific, fragmentation.get())
.error != EncodedImageCallback::Result::OK) {
return -1;
}
}
return 0;
}
std::unique_ptr<RTPFragmentationHeader> FakeEncoder::EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific) {
codec_specific->codecType = kVideoCodecGeneric;
return nullptr;
}
FakeEncoder::FrameInfo FakeEncoder::NextFrame(
const std::vector<FrameType>* frame_types,
bool keyframe,
@ -271,77 +281,65 @@ int FakeEncoder::GetConfiguredInputFramerate() const {
}
FakeH264Encoder::FakeH264Encoder(Clock* clock)
: FakeEncoder(clock), callback_(nullptr), idr_counter_(0) {
FakeEncoder::RegisterEncodeCompleteCallback(this);
}
: FakeEncoder(clock), idr_counter_(0) {}
int32_t FakeH264Encoder::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
rtc::CritScope cs(&local_crit_sect_);
callback_ = callback;
return 0;
}
EncodedImageCallback::Result FakeH264Encoder::OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragments) {
std::unique_ptr<RTPFragmentationHeader> FakeH264Encoder::EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific) {
const size_t kSpsSize = 8;
const size_t kPpsSize = 11;
const int kIdrFrequency = 10;
EncodedImageCallback* callback;
int current_idr_counter;
{
rtc::CritScope cs(&local_crit_sect_);
callback = callback_;
current_idr_counter = idr_counter_;
++idr_counter_;
}
RTPFragmentationHeader fragmentation;
auto fragmentation = absl::make_unique<RTPFragmentationHeader>();
if (current_idr_counter % kIdrFrequency == 0 &&
encoded_image.size() > kSpsSize + kPpsSize + 1) {
encoded_image->size() > kSpsSize + kPpsSize + 1) {
const size_t kNumSlices = 3;
fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
fragmentation.fragmentationOffset[0] = 0;
fragmentation.fragmentationLength[0] = kSpsSize;
fragmentation.fragmentationOffset[1] = kSpsSize;
fragmentation.fragmentationLength[1] = kPpsSize;
fragmentation.fragmentationOffset[2] = kSpsSize + kPpsSize;
fragmentation.fragmentationLength[2] =
encoded_image.size() - (kSpsSize + kPpsSize);
fragmentation->VerifyAndAllocateFragmentationHeader(kNumSlices);
fragmentation->fragmentationOffset[0] = 0;
fragmentation->fragmentationLength[0] = kSpsSize;
fragmentation->fragmentationOffset[1] = kSpsSize;
fragmentation->fragmentationLength[1] = kPpsSize;
fragmentation->fragmentationOffset[2] = kSpsSize + kPpsSize;
fragmentation->fragmentationLength[2] =
encoded_image->size() - (kSpsSize + kPpsSize);
const size_t kSpsNalHeader = 0x67;
const size_t kPpsNalHeader = 0x68;
const size_t kIdrNalHeader = 0x65;
encoded_image.buffer()[fragmentation.fragmentationOffset[0]] =
encoded_image->data()[fragmentation->fragmentationOffset[0]] =
kSpsNalHeader;
encoded_image.buffer()[fragmentation.fragmentationOffset[1]] =
encoded_image->data()[fragmentation->fragmentationOffset[1]] =
kPpsNalHeader;
encoded_image.buffer()[fragmentation.fragmentationOffset[2]] =
encoded_image->data()[fragmentation->fragmentationOffset[2]] =
kIdrNalHeader;
} else {
const size_t kNumSlices = 1;
fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
fragmentation.fragmentationOffset[0] = 0;
fragmentation.fragmentationLength[0] = encoded_image.size();
fragmentation->VerifyAndAllocateFragmentationHeader(kNumSlices);
fragmentation->fragmentationOffset[0] = 0;
fragmentation->fragmentationLength[0] = encoded_image->size();
const size_t kNalHeader = 0x41;
encoded_image.buffer()[fragmentation.fragmentationOffset[0]] = kNalHeader;
encoded_image->data()[fragmentation->fragmentationOffset[0]] = kNalHeader;
}
uint8_t value = 0;
int fragment_counter = 0;
for (size_t i = 0; i < encoded_image.size(); ++i) {
if (fragment_counter == fragmentation.fragmentationVectorSize ||
i != fragmentation.fragmentationOffset[fragment_counter]) {
encoded_image.buffer()[i] = value++;
for (size_t i = 0; i < encoded_image->size(); ++i) {
if (fragment_counter == fragmentation->fragmentationVectorSize ||
i != fragmentation->fragmentationOffset[fragment_counter]) {
encoded_image->data()[i] = value++;
} else {
++fragment_counter;
}
}
CodecSpecificInfo specifics;
specifics.codecType = kVideoCodecH264;
specifics.codecSpecific.H264.packetization_mode =
codec_specific->codecType = kVideoCodecH264;
codec_specific->codecSpecific.H264.packetization_mode =
H264PacketizationMode::NonInterleaved;
RTC_DCHECK(callback);
return callback->OnEncodedImage(encoded_image, &specifics, &fragmentation);
return fragmentation;
}
DelayedEncoder::DelayedEncoder(Clock* clock, int delay_ms)

View File

@ -79,6 +79,13 @@ class FakeEncoder : public VideoEncoder {
SimulcastStream simulcast_streams[kMaxSimulcastStreams],
int framerate);
// Called before the frame is passed to callback_->OnEncodedImage, to let
// subclasses fill out codec_specific, possibly modify encodedImage.
// Returns an RTPFragmentationHeader, if needed by the codec.
virtual std::unique_ptr<RTPFragmentationHeader> EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific);
FrameInfo last_frame_info_ RTC_GUARDED_BY(crit_sect_);
Clock* const clock_;
@ -97,20 +104,16 @@ class FakeEncoder : public VideoEncoder {
size_t debt_bytes_;
};
class FakeH264Encoder : public FakeEncoder, public EncodedImageCallback {
class FakeH264Encoder : public FakeEncoder {
public:
explicit FakeH264Encoder(Clock* clock);
virtual ~FakeH264Encoder() = default;
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override;
Result OnEncodedImage(const EncodedImage& encodedImage,
const CodecSpecificInfo* codecSpecificInfo,
const RTPFragmentationHeader* fragments) override;
private:
EncodedImageCallback* callback_ RTC_GUARDED_BY(local_crit_sect_);
std::unique_ptr<RTPFragmentationHeader> EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific) override;
int idr_counter_ RTC_GUARDED_BY(local_crit_sect_);
rtc::CriticalSection local_crit_sect_;
};

View File

@ -45,19 +45,10 @@ namespace webrtc {
namespace test {
FakeVP8Encoder::FakeVP8Encoder(Clock* clock)
: FakeEncoder(clock), callback_(nullptr) {
FakeEncoder::RegisterEncodeCompleteCallback(this);
FakeVP8Encoder::FakeVP8Encoder(Clock* clock) : FakeEncoder(clock) {
sequence_checker_.Detach();
}
int32_t FakeVP8Encoder::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
callback_ = callback;
return 0;
}
int32_t FakeVP8Encoder::InitEncode(const VideoCodec* config,
int32_t number_of_cores,
size_t max_payload_size) {
@ -112,25 +103,22 @@ void FakeVP8Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
timestamp, size_bytes, frame_type == kVideoFrameKey, -1, codec_specific);
}
EncodedImageCallback::Result FakeVP8Encoder::OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragments) {
std::unique_ptr<RTPFragmentationHeader> FakeVP8Encoder::EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
uint8_t stream_idx = encoded_image.SpatialIndex().value_or(0);
CodecSpecificInfo overrided_specific_info;
temporal_layers_[stream_idx]->UpdateLayerConfig(encoded_image.Timestamp());
PopulateCodecSpecific(&overrided_specific_info, encoded_image.size(),
encoded_image._frameType, stream_idx,
encoded_image.Timestamp());
uint8_t stream_idx = encoded_image->SpatialIndex().value_or(0);
temporal_layers_[stream_idx]->UpdateLayerConfig(encoded_image->Timestamp());
PopulateCodecSpecific(codec_specific, encoded_image->size(),
encoded_image->_frameType, stream_idx,
encoded_image->Timestamp());
// Write width and height to the payload the same way as the real encoder
// does.
WriteFakeVp8(encoded_image.buffer(), encoded_image._encodedWidth,
encoded_image._encodedHeight,
encoded_image._frameType == kVideoFrameKey);
return callback_->OnEncodedImage(encoded_image, &overrided_specific_info,
fragments);
WriteFakeVp8(encoded_image->data(), encoded_image->_encodedWidth,
encoded_image->_encodedHeight,
encoded_image->_frameType == kVideoFrameKey);
return nullptr;
}
VideoEncoder::EncoderInfo FakeVP8Encoder::GetEncoderInfo() const {

View File

@ -31,24 +31,17 @@
namespace webrtc {
namespace test {
class FakeVP8Encoder : public FakeEncoder, public EncodedImageCallback {
class FakeVP8Encoder : public FakeEncoder {
public:
explicit FakeVP8Encoder(Clock* clock);
virtual ~FakeVP8Encoder() = default;
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override;
int32_t InitEncode(const VideoCodec* config,
int32_t number_of_cores,
size_t max_payload_size) override;
int32_t Release() override;
Result OnEncodedImage(const EncodedImage& encodedImage,
const CodecSpecificInfo* codecSpecificInfo,
const RTPFragmentationHeader* fragments) override;
EncoderInfo GetEncoderInfo() const override;
private:
@ -59,8 +52,11 @@ class FakeVP8Encoder : public FakeEncoder, public EncodedImageCallback {
int stream_idx,
uint32_t timestamp);
std::unique_ptr<RTPFragmentationHeader> EncodeHook(
EncodedImage* encoded_image,
CodecSpecificInfo* codec_specific) override;
rtc::SequencedTaskChecker sequence_checker_;
EncodedImageCallback* callback_ RTC_GUARDED_BY(sequence_checker_);
std::vector<std::unique_ptr<Vp8TemporalLayers>> temporal_layers_
RTC_GUARDED_BY(sequence_checker_);