Break FrameConfig out of Vp8TemporalLayers
FrameConfig is not specific to temporal layers. Anything that can control referenced/updated buffers could potentially use it. Bug: webrtc:10259 Change-Id: I04ed177ee884693798c3b69e35fd4255ce1e9062 Reviewed-on: https://webrtc-review.googlesource.com/c/120355 Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Elad Alon <eladalon@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26448}
This commit is contained in:
@ -28,12 +28,15 @@ rtc_source_set("video_codecs_api") {
|
|||||||
"video_encoder_config.cc",
|
"video_encoder_config.cc",
|
||||||
"video_encoder_config.h",
|
"video_encoder_config.h",
|
||||||
"video_encoder_factory.h",
|
"video_encoder_factory.h",
|
||||||
|
"vp8_frame_config.cc",
|
||||||
|
"vp8_frame_config.h",
|
||||||
"vp8_temporal_layers.h",
|
"vp8_temporal_layers.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"..:scoped_refptr",
|
"..:scoped_refptr",
|
||||||
"../..:webrtc_common",
|
"../..:webrtc_common",
|
||||||
|
"../../modules/video_coding:codec_globals_headers",
|
||||||
"../../rtc_base:checks",
|
"../../rtc_base:checks",
|
||||||
"../../rtc_base:rtc_base_approved",
|
"../../rtc_base:rtc_base_approved",
|
||||||
"../../rtc_base/system:rtc_export",
|
"../../rtc_base/system:rtc_export",
|
||||||
|
77
api/video_codecs/vp8_frame_config.cc
Normal file
77
api/video_codecs/vp8_frame_config.cc
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
|
|
||||||
|
#include "modules/video_coding/codecs/interface/common_constants.h"
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
Vp8FrameConfig::Vp8FrameConfig() : Vp8FrameConfig(kNone, kNone, kNone, false) {}
|
||||||
|
|
||||||
|
Vp8FrameConfig::Vp8FrameConfig(BufferFlags last,
|
||||||
|
BufferFlags golden,
|
||||||
|
BufferFlags arf)
|
||||||
|
: Vp8FrameConfig(last, golden, arf, false) {}
|
||||||
|
|
||||||
|
Vp8FrameConfig::Vp8FrameConfig(BufferFlags last,
|
||||||
|
BufferFlags golden,
|
||||||
|
BufferFlags arf,
|
||||||
|
FreezeEntropy)
|
||||||
|
: Vp8FrameConfig(last, golden, arf, true) {}
|
||||||
|
|
||||||
|
Vp8FrameConfig::Vp8FrameConfig(BufferFlags last,
|
||||||
|
BufferFlags golden,
|
||||||
|
BufferFlags arf,
|
||||||
|
bool freeze_entropy)
|
||||||
|
: drop_frame(last == BufferFlags::kNone && golden == BufferFlags::kNone &&
|
||||||
|
arf == BufferFlags::kNone),
|
||||||
|
last_buffer_flags(last),
|
||||||
|
golden_buffer_flags(golden),
|
||||||
|
arf_buffer_flags(arf),
|
||||||
|
encoder_layer_id(0),
|
||||||
|
packetizer_temporal_idx(kNoTemporalIdx),
|
||||||
|
layer_sync(false),
|
||||||
|
freeze_entropy(freeze_entropy),
|
||||||
|
first_reference(Vp8BufferReference::kNone),
|
||||||
|
second_reference(Vp8BufferReference::kNone) {}
|
||||||
|
|
||||||
|
bool Vp8FrameConfig::References(Buffer buffer) const {
|
||||||
|
switch (buffer) {
|
||||||
|
case Buffer::kLast:
|
||||||
|
return (last_buffer_flags & kReference) != 0;
|
||||||
|
case Buffer::kGolden:
|
||||||
|
return (golden_buffer_flags & kReference) != 0;
|
||||||
|
case Buffer::kArf:
|
||||||
|
return (arf_buffer_flags & kReference) != 0;
|
||||||
|
case Buffer::kCount:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vp8FrameConfig::Updates(Buffer buffer) const {
|
||||||
|
switch (buffer) {
|
||||||
|
case Buffer::kLast:
|
||||||
|
return (last_buffer_flags & kUpdate) != 0;
|
||||||
|
case Buffer::kGolden:
|
||||||
|
return (golden_buffer_flags & kUpdate) != 0;
|
||||||
|
case Buffer::kArf:
|
||||||
|
return (arf_buffer_flags & kUpdate) != 0;
|
||||||
|
case Buffer::kCount:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
92
api/video_codecs/vp8_frame_config.h
Normal file
92
api/video_codecs/vp8_frame_config.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef API_VIDEO_CODECS_VP8_FRAME_CONFIG_H_
|
||||||
|
#define API_VIDEO_CODECS_VP8_FRAME_CONFIG_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
struct Vp8FrameConfig {
|
||||||
|
enum BufferFlags : int {
|
||||||
|
kNone = 0,
|
||||||
|
kReference = 1,
|
||||||
|
kUpdate = 2,
|
||||||
|
kReferenceAndUpdate = kReference | kUpdate,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FreezeEntropy { kFreezeEntropy };
|
||||||
|
|
||||||
|
// Defined bit-maskable reference to the three buffers available in VP8.
|
||||||
|
enum class Vp8BufferReference : uint8_t {
|
||||||
|
kNone = 0,
|
||||||
|
kLast = 1,
|
||||||
|
kGolden = 2,
|
||||||
|
kAltref = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
Vp8FrameConfig();
|
||||||
|
|
||||||
|
Vp8FrameConfig(BufferFlags last, BufferFlags golden, BufferFlags arf);
|
||||||
|
Vp8FrameConfig(BufferFlags last,
|
||||||
|
BufferFlags golden,
|
||||||
|
BufferFlags arf,
|
||||||
|
FreezeEntropy);
|
||||||
|
|
||||||
|
enum class Buffer : int { kLast = 0, kGolden = 1, kArf = 2, kCount };
|
||||||
|
|
||||||
|
bool References(Buffer buffer) const;
|
||||||
|
|
||||||
|
bool Updates(Buffer buffer) const;
|
||||||
|
|
||||||
|
bool drop_frame;
|
||||||
|
BufferFlags last_buffer_flags;
|
||||||
|
BufferFlags golden_buffer_flags;
|
||||||
|
BufferFlags arf_buffer_flags;
|
||||||
|
|
||||||
|
// The encoder layer ID is used to utilize the correct bitrate allocator
|
||||||
|
// inside the encoder. It does not control references nor determine which
|
||||||
|
// "actual" temporal layer this is. The packetizer temporal index determines
|
||||||
|
// which layer the encoded frame should be packetized into.
|
||||||
|
// Normally these are the same, but current temporal-layer strategies for
|
||||||
|
// screenshare use one bitrate allocator for all layers, but attempt to
|
||||||
|
// packetize / utilize references to split a stream into multiple layers,
|
||||||
|
// with different quantizer settings, to hit target bitrate.
|
||||||
|
// TODO(sprang): Screenshare layers are being reconsidered at the time of
|
||||||
|
// writing, we might be able to remove this distinction, and have a temporal
|
||||||
|
// layer imply both (the normal case).
|
||||||
|
int encoder_layer_id;
|
||||||
|
// TODO(eladalon/sprang): Move out of this class.
|
||||||
|
int packetizer_temporal_idx;
|
||||||
|
|
||||||
|
// TODO(eladalon/sprang): Move out of this class.
|
||||||
|
bool layer_sync;
|
||||||
|
|
||||||
|
bool freeze_entropy;
|
||||||
|
|
||||||
|
// Indicates in which order the encoder should search the reference buffers
|
||||||
|
// when doing motion prediction. Set to kNone to use unspecified order. Any
|
||||||
|
// buffer indicated here must not have the corresponding no_ref bit set.
|
||||||
|
// If all three buffers can be reference, the one not listed here should be
|
||||||
|
// searched last.
|
||||||
|
Vp8BufferReference first_reference;
|
||||||
|
Vp8BufferReference second_reference;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vp8FrameConfig(BufferFlags last,
|
||||||
|
BufferFlags golden,
|
||||||
|
BufferFlags arf,
|
||||||
|
bool freeze_entropy);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // API_VIDEO_CODECS_VP8_FRAME_CONFIG_H_
|
@ -14,7 +14,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "rtc_base/checks.h"
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -72,111 +72,16 @@ struct Vp8EncoderConfig {
|
|||||||
uint32_t rc_max_quantizer;
|
uint32_t rc_max_quantizer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Defined bit-maskable reference to the three buffers available in VP8.
|
|
||||||
enum class Vp8BufferReference : uint8_t {
|
|
||||||
kNone = 0,
|
|
||||||
kLast = 1,
|
|
||||||
kGolden = 2,
|
|
||||||
kAltref = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
// This interface defines a way of getting the encoder settings needed to
|
// This interface defines a way of getting the encoder settings needed to
|
||||||
// realize a temporal layer structure.
|
// realize a temporal layer structure.
|
||||||
class Vp8TemporalLayers {
|
class Vp8TemporalLayers {
|
||||||
public:
|
public:
|
||||||
enum BufferFlags : int {
|
|
||||||
kNone = 0,
|
|
||||||
kReference = 1,
|
|
||||||
kUpdate = 2,
|
|
||||||
kReferenceAndUpdate = kReference | kUpdate,
|
|
||||||
};
|
|
||||||
enum FreezeEntropy { kFreezeEntropy };
|
|
||||||
|
|
||||||
struct FrameConfig {
|
|
||||||
FrameConfig();
|
|
||||||
|
|
||||||
FrameConfig(BufferFlags last, BufferFlags golden, BufferFlags arf);
|
|
||||||
FrameConfig(BufferFlags last,
|
|
||||||
BufferFlags golden,
|
|
||||||
BufferFlags arf,
|
|
||||||
FreezeEntropy);
|
|
||||||
|
|
||||||
enum class Buffer : int { kLast = 0, kGolden = 1, kArf = 2, kCount };
|
|
||||||
|
|
||||||
bool References(Buffer buffer) const {
|
|
||||||
switch (buffer) {
|
|
||||||
case Buffer::kLast:
|
|
||||||
return (last_buffer_flags & kReference) != 0;
|
|
||||||
case Buffer::kGolden:
|
|
||||||
return (golden_buffer_flags & kReference) != 0;
|
|
||||||
case Buffer::kArf:
|
|
||||||
return (arf_buffer_flags & kReference) != 0;
|
|
||||||
case Buffer::kCount:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
RTC_NOTREACHED();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Updates(Buffer buffer) const {
|
|
||||||
switch (buffer) {
|
|
||||||
case Buffer::kLast:
|
|
||||||
return (last_buffer_flags & kUpdate) != 0;
|
|
||||||
case Buffer::kGolden:
|
|
||||||
return (golden_buffer_flags & kUpdate) != 0;
|
|
||||||
case Buffer::kArf:
|
|
||||||
return (arf_buffer_flags & kUpdate) != 0;
|
|
||||||
case Buffer::kCount:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
RTC_NOTREACHED();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool drop_frame;
|
|
||||||
BufferFlags last_buffer_flags;
|
|
||||||
BufferFlags golden_buffer_flags;
|
|
||||||
BufferFlags arf_buffer_flags;
|
|
||||||
|
|
||||||
// The encoder layer ID is used to utilize the correct bitrate allocator
|
|
||||||
// inside the encoder. It does not control references nor determine which
|
|
||||||
// "actual" temporal layer this is. The packetizer temporal index determines
|
|
||||||
// which layer the encoded frame should be packetized into.
|
|
||||||
// Normally these are the same, but current temporal-layer strategies for
|
|
||||||
// screenshare use one bitrate allocator for all layers, but attempt to
|
|
||||||
// packetize / utilize references to split a stream into multiple layers,
|
|
||||||
// with different quantizer settings, to hit target bitrate.
|
|
||||||
// TODO(pbos): Screenshare layers are being reconsidered at the time of
|
|
||||||
// writing, we might be able to remove this distinction, and have a temporal
|
|
||||||
// layer imply both (the normal case).
|
|
||||||
int encoder_layer_id;
|
|
||||||
int packetizer_temporal_idx;
|
|
||||||
|
|
||||||
bool layer_sync;
|
|
||||||
|
|
||||||
bool freeze_entropy;
|
|
||||||
|
|
||||||
// Indicates in which order the encoder should search the reference buffers
|
|
||||||
// when doing motion prediction. Set to kNone to use unspecified order. Any
|
|
||||||
// buffer indicated here must not have the corresponding no_ref bit set.
|
|
||||||
// If all three buffers can be reference, the one not listed here should be
|
|
||||||
// searched last.
|
|
||||||
Vp8BufferReference first_reference;
|
|
||||||
Vp8BufferReference second_reference;
|
|
||||||
|
|
||||||
private:
|
|
||||||
FrameConfig(BufferFlags last,
|
|
||||||
BufferFlags golden,
|
|
||||||
BufferFlags arf,
|
|
||||||
bool freeze_entropy);
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~Vp8TemporalLayers() = default;
|
virtual ~Vp8TemporalLayers() = default;
|
||||||
|
|
||||||
// If this method returns true, the encoder is free to drop frames for
|
// If this method returns true, the encoder is free to drop frames for
|
||||||
// instance in an effort to uphold encoding bitrate.
|
// instance in an effort to uphold encoding bitrate.
|
||||||
// If this return false, the encoder must not drop any frames unless:
|
// If this return false, the encoder must not drop any frames unless:
|
||||||
// 1. Requested to do so via FrameConfig.drop_frame
|
// 1. Requested to do so via Vp8FrameConfig.drop_frame
|
||||||
// 2. The frame to be encoded is requested to be a keyframe
|
// 2. The frame to be encoded is requested to be a keyframe
|
||||||
// 3. The encoded detected a large overshoot and decided to drop and then
|
// 3. The encoded detected a large overshoot and decided to drop and then
|
||||||
// re-encode the image at a low bitrate. In this case the encoder should
|
// re-encode the image at a low bitrate. In this case the encoder should
|
||||||
@ -202,7 +107,7 @@ class Vp8TemporalLayers {
|
|||||||
// The timestamp uses a 90kHz RTP clock.
|
// The timestamp uses a 90kHz RTP clock.
|
||||||
// After calling this method, first call the actual encoder with the provided
|
// After calling this method, first call the actual encoder with the provided
|
||||||
// frame configuration, and then OnEncodeDone() below.
|
// frame configuration, and then OnEncodeDone() below.
|
||||||
virtual FrameConfig UpdateLayerConfig(uint32_t rtp_timestamp) = 0;
|
virtual Vp8FrameConfig UpdateLayerConfig(uint32_t rtp_timestamp) = 0;
|
||||||
|
|
||||||
// Called after the encode step is done. |rtp_timestamp| must match the
|
// Called after the encode step is done. |rtp_timestamp| must match the
|
||||||
// parameter use in the UpdateLayerConfig() call.
|
// parameter use in the UpdateLayerConfig() call.
|
||||||
|
@ -26,52 +26,27 @@
|
|||||||
#include "system_wrappers/include/field_trial.h"
|
#include "system_wrappers/include/field_trial.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
using Buffer = Vp8TemporalLayers::FrameConfig::Buffer;
|
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig::FrameConfig()
|
|
||||||
: FrameConfig(kNone, kNone, kNone, false) {}
|
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig::FrameConfig(
|
|
||||||
Vp8TemporalLayers::BufferFlags last,
|
|
||||||
Vp8TemporalLayers::BufferFlags golden,
|
|
||||||
Vp8TemporalLayers::BufferFlags arf)
|
|
||||||
: FrameConfig(last, golden, arf, false) {}
|
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig::FrameConfig(
|
|
||||||
Vp8TemporalLayers::BufferFlags last,
|
|
||||||
Vp8TemporalLayers::BufferFlags golden,
|
|
||||||
Vp8TemporalLayers::BufferFlags arf,
|
|
||||||
FreezeEntropy)
|
|
||||||
: FrameConfig(last, golden, arf, true) {}
|
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig::FrameConfig(
|
|
||||||
Vp8TemporalLayers::BufferFlags last,
|
|
||||||
Vp8TemporalLayers::BufferFlags golden,
|
|
||||||
Vp8TemporalLayers::BufferFlags arf,
|
|
||||||
bool freeze_entropy)
|
|
||||||
: drop_frame(last == Vp8TemporalLayers::kNone &&
|
|
||||||
golden == Vp8TemporalLayers::kNone &&
|
|
||||||
arf == Vp8TemporalLayers::kNone),
|
|
||||||
last_buffer_flags(last),
|
|
||||||
golden_buffer_flags(golden),
|
|
||||||
arf_buffer_flags(arf),
|
|
||||||
encoder_layer_id(0),
|
|
||||||
packetizer_temporal_idx(kNoTemporalIdx),
|
|
||||||
layer_sync(false),
|
|
||||||
freeze_entropy(freeze_entropy),
|
|
||||||
first_reference(Vp8BufferReference::kNone),
|
|
||||||
second_reference(Vp8BufferReference::kNone) {}
|
|
||||||
|
|
||||||
DefaultTemporalLayers::PendingFrame::PendingFrame() = default;
|
DefaultTemporalLayers::PendingFrame::PendingFrame() = default;
|
||||||
DefaultTemporalLayers::PendingFrame::PendingFrame(
|
DefaultTemporalLayers::PendingFrame::PendingFrame(
|
||||||
bool expired,
|
bool expired,
|
||||||
uint8_t updated_buffers_mask,
|
uint8_t updated_buffers_mask,
|
||||||
const FrameConfig& frame_config)
|
const Vp8FrameConfig& frame_config)
|
||||||
: expired(expired),
|
: expired(expired),
|
||||||
updated_buffer_mask(updated_buffers_mask),
|
updated_buffer_mask(updated_buffers_mask),
|
||||||
frame_config(frame_config) {}
|
frame_config(frame_config) {}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
using Buffer = Vp8FrameConfig::Buffer;
|
||||||
|
using BufferFlags = Vp8FrameConfig::BufferFlags;
|
||||||
|
using FreezeEntropy = Vp8FrameConfig::FreezeEntropy;
|
||||||
|
using Vp8BufferReference = Vp8FrameConfig::Vp8BufferReference;
|
||||||
|
|
||||||
|
constexpr BufferFlags kNone = BufferFlags::kNone;
|
||||||
|
constexpr BufferFlags kReference = BufferFlags::kReference;
|
||||||
|
constexpr BufferFlags kUpdate = BufferFlags::kUpdate;
|
||||||
|
constexpr BufferFlags kReferenceAndUpdate = BufferFlags::kReferenceAndUpdate;
|
||||||
|
constexpr FreezeEntropy kFreezeEntropy = FreezeEntropy::kFreezeEntropy;
|
||||||
|
|
||||||
static constexpr uint8_t kUninitializedPatternIndex =
|
static constexpr uint8_t kUninitializedPatternIndex =
|
||||||
std::numeric_limits<uint8_t>::max();
|
std::numeric_limits<uint8_t>::max();
|
||||||
static constexpr std::array<Vp8BufferReference, 3> kAllBuffers = {
|
static constexpr std::array<Vp8BufferReference, 3> kAllBuffers = {
|
||||||
@ -110,15 +85,15 @@ std::vector<unsigned int> GetTemporalIds(size_t num_layers) {
|
|||||||
return {0};
|
return {0};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t GetUpdatedBuffers(const Vp8TemporalLayers::FrameConfig& config) {
|
uint8_t GetUpdatedBuffers(const Vp8FrameConfig& config) {
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
if (config.last_buffer_flags & Vp8TemporalLayers::BufferFlags::kUpdate) {
|
if (config.last_buffer_flags & BufferFlags::kUpdate) {
|
||||||
flags |= static_cast<uint8_t>(Vp8BufferReference::kLast);
|
flags |= static_cast<uint8_t>(Vp8BufferReference::kLast);
|
||||||
}
|
}
|
||||||
if (config.golden_buffer_flags & Vp8TemporalLayers::BufferFlags::kUpdate) {
|
if (config.golden_buffer_flags & BufferFlags::kUpdate) {
|
||||||
flags |= static_cast<uint8_t>(Vp8BufferReference::kGolden);
|
flags |= static_cast<uint8_t>(Vp8BufferReference::kGolden);
|
||||||
}
|
}
|
||||||
if (config.arf_buffer_flags & Vp8TemporalLayers::BufferFlags::kUpdate) {
|
if (config.arf_buffer_flags & BufferFlags::kUpdate) {
|
||||||
flags |= static_cast<uint8_t>(Vp8BufferReference::kAltref);
|
flags |= static_cast<uint8_t>(Vp8BufferReference::kAltref);
|
||||||
}
|
}
|
||||||
return flags;
|
return flags;
|
||||||
@ -126,10 +101,10 @@ uint8_t GetUpdatedBuffers(const Vp8TemporalLayers::FrameConfig& config) {
|
|||||||
|
|
||||||
// Find the set of buffers that are never updated by the given pattern.
|
// Find the set of buffers that are never updated by the given pattern.
|
||||||
std::set<Vp8BufferReference> FindKfBuffers(
|
std::set<Vp8BufferReference> FindKfBuffers(
|
||||||
const std::vector<Vp8TemporalLayers::FrameConfig>& frame_configs) {
|
const std::vector<Vp8FrameConfig>& frame_configs) {
|
||||||
std::set<Vp8BufferReference> kf_buffers(kAllBuffers.begin(),
|
std::set<Vp8BufferReference> kf_buffers(kAllBuffers.begin(),
|
||||||
kAllBuffers.end());
|
kAllBuffers.end());
|
||||||
for (Vp8TemporalLayers::FrameConfig config : frame_configs) {
|
for (Vp8FrameConfig config : frame_configs) {
|
||||||
// Get bit-masked set of update buffers for this frame config.
|
// Get bit-masked set of update buffers for this frame config.
|
||||||
uint8_t updated_buffers = GetUpdatedBuffers(config);
|
uint8_t updated_buffers = GetUpdatedBuffers(config);
|
||||||
for (Vp8BufferReference buffer : kAllBuffers) {
|
for (Vp8BufferReference buffer : kAllBuffers) {
|
||||||
@ -142,8 +117,8 @@ std::set<Vp8BufferReference> FindKfBuffers(
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<Vp8TemporalLayers::FrameConfig>
|
std::vector<Vp8FrameConfig> DefaultTemporalLayers::GetTemporalPattern(
|
||||||
DefaultTemporalLayers::GetTemporalPattern(size_t num_layers) {
|
size_t num_layers) {
|
||||||
// For indexing in the patterns described below (which temporal layers they
|
// For indexing in the patterns described below (which temporal layers they
|
||||||
// belong to), see the diagram above.
|
// belong to), see the diagram above.
|
||||||
// Layer sync is done similarly for all patterns (except single stream) and
|
// Layer sync is done similarly for all patterns (except single stream) and
|
||||||
@ -160,7 +135,7 @@ DefaultTemporalLayers::GetTemporalPattern(size_t num_layers) {
|
|||||||
switch (num_layers) {
|
switch (num_layers) {
|
||||||
case 1:
|
case 1:
|
||||||
// All frames reference all buffers and the 'last' buffer is updated.
|
// All frames reference all buffers and the 'last' buffer is updated.
|
||||||
return {FrameConfig(kReferenceAndUpdate, kReference, kReference)};
|
return {Vp8FrameConfig(kReferenceAndUpdate, kReference, kReference)};
|
||||||
case 2:
|
case 2:
|
||||||
// All layers can reference but not update the 'alt' buffer, this means
|
// All layers can reference but not update the 'alt' buffer, this means
|
||||||
// that the 'alt' buffer reference is effectively the last keyframe.
|
// that the 'alt' buffer reference is effectively the last keyframe.
|
||||||
@ -172,24 +147,24 @@ DefaultTemporalLayers::GetTemporalPattern(size_t num_layers) {
|
|||||||
// / / / /
|
// / / / /
|
||||||
// 0---0---0---0 ...
|
// 0---0---0---0 ...
|
||||||
return {
|
return {
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kUpdate, kReference),
|
Vp8FrameConfig(kReference, kUpdate, kReference),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
||||||
} else {
|
} else {
|
||||||
// "Default" 8-frame pattern:
|
// "Default" 8-frame pattern:
|
||||||
// 1---1---1---1 1---1---1---1 ...
|
// 1---1---1---1 1---1---1---1 ...
|
||||||
// / / / / / / / /
|
// / / / / / / / /
|
||||||
// 0---0---0---0---0---0---0---0 ...
|
// 0---0---0---0---0---0---0---0 ...
|
||||||
return {
|
return {
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kUpdate, kReference),
|
Vp8FrameConfig(kReference, kUpdate, kReference),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
Vp8FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
Vp8FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) {
|
if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) {
|
||||||
@ -210,10 +185,10 @@ DefaultTemporalLayers::GetTemporalPattern(size_t num_layers) {
|
|||||||
// TL2 references both 'last' & 'golden' and references and updates
|
// TL2 references both 'last' & 'golden' and references and updates
|
||||||
// 'arf'.
|
// 'arf'.
|
||||||
return {
|
return {
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
||||||
FrameConfig(kReference, kNone, kUpdate),
|
Vp8FrameConfig(kReference, kNone, kUpdate),
|
||||||
FrameConfig(kReference, kUpdate, kNone),
|
Vp8FrameConfig(kReference, kUpdate, kNone),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
||||||
} else {
|
} else {
|
||||||
// All layers can reference but not update the 'alt' buffer, this means
|
// All layers can reference but not update the 'alt' buffer, this means
|
||||||
// that the 'alt' buffer reference is effectively the last keyframe.
|
// that the 'alt' buffer reference is effectively the last keyframe.
|
||||||
@ -221,42 +196,43 @@ DefaultTemporalLayers::GetTemporalPattern(size_t num_layers) {
|
|||||||
// TL1 also references 'last' and references and updates 'golden'.
|
// TL1 also references 'last' and references and updates 'golden'.
|
||||||
// TL2 references both 'last' and 'golden' but updates no buffer.
|
// TL2 references both 'last' and 'golden' but updates no buffer.
|
||||||
return {
|
return {
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kNone, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kNone, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kUpdate, kReference),
|
Vp8FrameConfig(kReference, kUpdate, kReference),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kReference),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
Vp8FrameConfig(kReference, kReferenceAndUpdate, kReference),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
// TL0 references and updates only the 'last' buffer.
|
// TL0 references and updates only the 'last' buffer.
|
||||||
// TL1 references 'last' and updates and references 'golden'.
|
// TL1 references 'last' and updates and references 'golden'.
|
||||||
// TL2 references 'last' and 'golden', and references and updates 'arf'.
|
// TL2 references 'last' and 'golden', and references and updates 'arf'.
|
||||||
// TL3 references all buffers but update none of them.
|
// TL3 references all buffers but update none of them.
|
||||||
return {FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
return {
|
||||||
FrameConfig(kReference, kNone, kNone, kFreezeEntropy),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
||||||
FrameConfig(kReference, kNone, kUpdate),
|
Vp8FrameConfig(kReference, kNone, kNone, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kNone, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kNone, kUpdate),
|
||||||
FrameConfig(kReference, kUpdate, kNone),
|
Vp8FrameConfig(kReference, kNone, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kUpdate, kNone),
|
||||||
FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
||||||
FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReferenceAndUpdate, kNone, kNone),
|
||||||
FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
||||||
FrameConfig(kReference, kReferenceAndUpdate, kNone),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
Vp8FrameConfig(kReference, kReferenceAndUpdate, kNone),
|
||||||
FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy),
|
||||||
FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
Vp8FrameConfig(kReference, kReference, kReferenceAndUpdate),
|
||||||
|
Vp8FrameConfig(kReference, kReference, kReference, kFreezeEntropy)};
|
||||||
default:
|
default:
|
||||||
RTC_NOTREACHED();
|
RTC_NOTREACHED();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RTC_NOTREACHED();
|
RTC_NOTREACHED();
|
||||||
return {FrameConfig(kNone, kNone, kNone)};
|
return {Vp8FrameConfig(kNone, kNone, kNone)};
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers)
|
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers)
|
||||||
@ -326,7 +302,7 @@ bool DefaultTemporalLayers::UpdateConfiguration(Vp8EncoderConfig* cfg) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DefaultTemporalLayers::IsSyncFrame(const FrameConfig& config) const {
|
bool DefaultTemporalLayers::IsSyncFrame(const Vp8FrameConfig& config) const {
|
||||||
// Since we always assign TL0 to 'last' in these patterns, we can infer layer
|
// Since we always assign TL0 to 'last' in these patterns, we can infer layer
|
||||||
// sync by checking if temporal id > 0 and we only reference TL0 or buffers
|
// sync by checking if temporal id > 0 and we only reference TL0 or buffers
|
||||||
// containing the last key-frame.
|
// containing the last key-frame.
|
||||||
@ -354,13 +330,12 @@ bool DefaultTemporalLayers::IsSyncFrame(const FrameConfig& config) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig DefaultTemporalLayers::UpdateLayerConfig(
|
Vp8FrameConfig DefaultTemporalLayers::UpdateLayerConfig(uint32_t timestamp) {
|
||||||
uint32_t timestamp) {
|
|
||||||
RTC_DCHECK_GT(num_layers_, 0);
|
RTC_DCHECK_GT(num_layers_, 0);
|
||||||
RTC_DCHECK_LT(0, temporal_pattern_.size());
|
RTC_DCHECK_LT(0, temporal_pattern_.size());
|
||||||
|
|
||||||
pattern_idx_ = (pattern_idx_ + 1) % temporal_pattern_.size();
|
pattern_idx_ = (pattern_idx_ + 1) % temporal_pattern_.size();
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = temporal_pattern_[pattern_idx_];
|
Vp8FrameConfig tl_config = temporal_pattern_[pattern_idx_];
|
||||||
tl_config.encoder_layer_id = tl_config.packetizer_temporal_idx =
|
tl_config.encoder_layer_id = tl_config.packetizer_temporal_idx =
|
||||||
temporal_ids_[pattern_idx_ % temporal_ids_.size()];
|
temporal_ids_[pattern_idx_ % temporal_ids_.size()];
|
||||||
|
|
||||||
@ -428,7 +403,7 @@ void DefaultTemporalLayers::ValidateReferences(BufferFlags* flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultTemporalLayers::UpdateSearchOrder(FrameConfig* config) {
|
void DefaultTemporalLayers::UpdateSearchOrder(Vp8FrameConfig* config) {
|
||||||
// Figure out which of the buffers we can reference, and order them so that
|
// Figure out which of the buffers we can reference, and order them so that
|
||||||
// the most recently refreshed is first. Otherwise prioritize last first,
|
// the most recently refreshed is first. Otherwise prioritize last first,
|
||||||
// golden second, and altref third.
|
// golden second, and altref third.
|
||||||
@ -592,7 +567,7 @@ DefaultTemporalLayersChecker::~DefaultTemporalLayersChecker() = default;
|
|||||||
|
|
||||||
bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
||||||
bool frame_is_keyframe,
|
bool frame_is_keyframe,
|
||||||
const Vp8TemporalLayers::FrameConfig& frame_config) {
|
const Vp8FrameConfig& frame_config) {
|
||||||
if (!TemporalLayersChecker::CheckTemporalConfig(frame_is_keyframe,
|
if (!TemporalLayersChecker::CheckTemporalConfig(frame_is_keyframe,
|
||||||
frame_config)) {
|
frame_config)) {
|
||||||
return false;
|
return false;
|
||||||
@ -642,8 +617,7 @@ bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
|||||||
temporal_ids_[pattern_idx_] != kNoTemporalIdx;
|
temporal_ids_[pattern_idx_] != kNoTemporalIdx;
|
||||||
std::vector<int> dependencies;
|
std::vector<int> dependencies;
|
||||||
|
|
||||||
if (frame_config.last_buffer_flags &
|
if (frame_config.last_buffer_flags & BufferFlags::kReference) {
|
||||||
Vp8TemporalLayers::BufferFlags::kReference) {
|
|
||||||
uint8_t referenced_layer = temporal_ids_[last_.pattern_idx];
|
uint8_t referenced_layer = temporal_ids_[last_.pattern_idx];
|
||||||
if (referenced_layer > 0) {
|
if (referenced_layer > 0) {
|
||||||
need_sync = false;
|
need_sync = false;
|
||||||
@ -658,8 +632,7 @@ bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_config.arf_buffer_flags &
|
if (frame_config.arf_buffer_flags & BufferFlags::kReference) {
|
||||||
Vp8TemporalLayers::BufferFlags::kReference) {
|
|
||||||
uint8_t referenced_layer = temporal_ids_[arf_.pattern_idx];
|
uint8_t referenced_layer = temporal_ids_[arf_.pattern_idx];
|
||||||
if (referenced_layer > 0) {
|
if (referenced_layer > 0) {
|
||||||
need_sync = false;
|
need_sync = false;
|
||||||
@ -674,8 +647,7 @@ bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_config.golden_buffer_flags &
|
if (frame_config.golden_buffer_flags & BufferFlags::kReference) {
|
||||||
Vp8TemporalLayers::BufferFlags::kReference) {
|
|
||||||
uint8_t referenced_layer = temporal_ids_[golden_.pattern_idx];
|
uint8_t referenced_layer = temporal_ids_[golden_.pattern_idx];
|
||||||
if (referenced_layer > 0) {
|
if (referenced_layer > 0) {
|
||||||
need_sync = false;
|
need_sync = false;
|
||||||
@ -711,19 +683,17 @@ bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_config.last_buffer_flags &
|
if (frame_config.last_buffer_flags & BufferFlags::kUpdate) {
|
||||||
Vp8TemporalLayers::BufferFlags::kUpdate) {
|
|
||||||
last_.is_updated_this_cycle = true;
|
last_.is_updated_this_cycle = true;
|
||||||
last_.pattern_idx = pattern_idx_;
|
last_.pattern_idx = pattern_idx_;
|
||||||
last_.is_keyframe = false;
|
last_.is_keyframe = false;
|
||||||
}
|
}
|
||||||
if (frame_config.arf_buffer_flags & Vp8TemporalLayers::BufferFlags::kUpdate) {
|
if (frame_config.arf_buffer_flags & BufferFlags::kUpdate) {
|
||||||
arf_.is_updated_this_cycle = true;
|
arf_.is_updated_this_cycle = true;
|
||||||
arf_.pattern_idx = pattern_idx_;
|
arf_.pattern_idx = pattern_idx_;
|
||||||
arf_.is_keyframe = false;
|
arf_.is_keyframe = false;
|
||||||
}
|
}
|
||||||
if (frame_config.golden_buffer_flags &
|
if (frame_config.golden_buffer_flags & BufferFlags::kUpdate) {
|
||||||
Vp8TemporalLayers::BufferFlags::kUpdate) {
|
|
||||||
golden_.is_updated_this_cycle = true;
|
golden_.is_updated_this_cycle = true;
|
||||||
golden_.pattern_idx = pattern_idx_;
|
golden_.pattern_idx = pattern_idx_;
|
||||||
golden_.is_keyframe = false;
|
golden_.is_keyframe = false;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "api/video_codecs/vp8_temporal_layers.h"
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
||||||
#include "modules/video_coding/codecs/vp8/include/temporal_layers_checker.h"
|
#include "modules/video_coding/codecs/vp8/include/temporal_layers_checker.h"
|
||||||
#include "modules/video_coding/include/video_codec_interface.h"
|
#include "modules/video_coding/include/video_codec_interface.h"
|
||||||
@ -36,7 +37,7 @@ class DefaultTemporalLayers : public Vp8TemporalLayers {
|
|||||||
|
|
||||||
// Returns the recommended VP8 encode flags needed. May refresh the decoder
|
// Returns the recommended VP8 encode flags needed. May refresh the decoder
|
||||||
// and/or update the reference buffers.
|
// and/or update the reference buffers.
|
||||||
Vp8TemporalLayers::FrameConfig UpdateLayerConfig(uint32_t timestamp) override;
|
Vp8FrameConfig UpdateLayerConfig(uint32_t timestamp) override;
|
||||||
|
|
||||||
// New target bitrate, per temporal layer.
|
// New target bitrate, per temporal layer.
|
||||||
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
|
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
|
||||||
@ -52,17 +53,17 @@ class DefaultTemporalLayers : public Vp8TemporalLayers {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t kKeyframeBuffer = std::numeric_limits<size_t>::max();
|
static constexpr size_t kKeyframeBuffer = std::numeric_limits<size_t>::max();
|
||||||
static std::vector<Vp8TemporalLayers::FrameConfig> GetTemporalPattern(
|
static std::vector<Vp8FrameConfig> GetTemporalPattern(size_t num_layers);
|
||||||
size_t num_layers);
|
bool IsSyncFrame(const Vp8FrameConfig& config) const;
|
||||||
bool IsSyncFrame(const FrameConfig& config) const;
|
void ValidateReferences(Vp8FrameConfig::BufferFlags* flags,
|
||||||
void ValidateReferences(BufferFlags* flags, Vp8BufferReference ref) const;
|
Vp8FrameConfig::Vp8BufferReference ref) const;
|
||||||
void UpdateSearchOrder(FrameConfig* config);
|
void UpdateSearchOrder(Vp8FrameConfig* config);
|
||||||
|
|
||||||
const size_t num_layers_;
|
const size_t num_layers_;
|
||||||
const std::vector<unsigned int> temporal_ids_;
|
const std::vector<unsigned int> temporal_ids_;
|
||||||
const std::vector<Vp8TemporalLayers::FrameConfig> temporal_pattern_;
|
const std::vector<Vp8FrameConfig> temporal_pattern_;
|
||||||
// Set of buffers that are never updated except by keyframes.
|
// Set of buffers that are never updated except by keyframes.
|
||||||
const std::set<Vp8BufferReference> kf_buffers_;
|
const std::set<Vp8FrameConfig::Vp8BufferReference> kf_buffers_;
|
||||||
|
|
||||||
uint8_t pattern_idx_;
|
uint8_t pattern_idx_;
|
||||||
// Updated cumulative bitrates, per temporal layer.
|
// Updated cumulative bitrates, per temporal layer.
|
||||||
@ -72,7 +73,7 @@ class DefaultTemporalLayers : public Vp8TemporalLayers {
|
|||||||
PendingFrame();
|
PendingFrame();
|
||||||
PendingFrame(bool expired,
|
PendingFrame(bool expired,
|
||||||
uint8_t updated_buffers_mask,
|
uint8_t updated_buffers_mask,
|
||||||
const FrameConfig& frame_config);
|
const Vp8FrameConfig& frame_config);
|
||||||
// Flag indicating if this frame has expired, ie it belongs to a previous
|
// Flag indicating if this frame has expired, ie it belongs to a previous
|
||||||
// iteration of the temporal pattern.
|
// iteration of the temporal pattern.
|
||||||
bool expired = false;
|
bool expired = false;
|
||||||
@ -80,7 +81,7 @@ class DefaultTemporalLayers : public Vp8TemporalLayers {
|
|||||||
// updates.
|
// updates.
|
||||||
uint8_t updated_buffer_mask = 0;
|
uint8_t updated_buffer_mask = 0;
|
||||||
// The frame config return by UpdateLayerConfig() for this frame.
|
// The frame config return by UpdateLayerConfig() for this frame.
|
||||||
FrameConfig frame_config;
|
Vp8FrameConfig frame_config;
|
||||||
};
|
};
|
||||||
// Map from rtp timestamp to pending frame status. Reset on pattern loop.
|
// Map from rtp timestamp to pending frame status. Reset on pattern loop.
|
||||||
std::map<uint32_t, PendingFrame> pending_frames_;
|
std::map<uint32_t, PendingFrame> pending_frames_;
|
||||||
@ -88,7 +89,8 @@ class DefaultTemporalLayers : public Vp8TemporalLayers {
|
|||||||
// One counter per Vp8BufferReference, indicating number of frames since last
|
// One counter per Vp8BufferReference, indicating number of frames since last
|
||||||
// refresh. For non-base-layer frames (ie golden, altref buffers), this is
|
// refresh. For non-base-layer frames (ie golden, altref buffers), this is
|
||||||
// reset when the pattern loops.
|
// reset when the pattern loops.
|
||||||
std::map<Vp8BufferReference, size_t> frames_since_buffer_refresh_;
|
std::map<Vp8FrameConfig::Vp8BufferReference, size_t>
|
||||||
|
frames_since_buffer_refresh_;
|
||||||
|
|
||||||
// Optional utility used to verify reference validity.
|
// Optional utility used to verify reference validity.
|
||||||
std::unique_ptr<TemporalLayersChecker> checker_;
|
std::unique_ptr<TemporalLayersChecker> checker_;
|
||||||
@ -99,9 +101,8 @@ class DefaultTemporalLayersChecker : public TemporalLayersChecker {
|
|||||||
explicit DefaultTemporalLayersChecker(int number_of_temporal_layers);
|
explicit DefaultTemporalLayersChecker(int number_of_temporal_layers);
|
||||||
~DefaultTemporalLayersChecker() override;
|
~DefaultTemporalLayersChecker() override;
|
||||||
|
|
||||||
bool CheckTemporalConfig(
|
bool CheckTemporalConfig(bool frame_is_keyframe,
|
||||||
bool frame_is_keyframe,
|
const Vp8FrameConfig& frame_config) override;
|
||||||
const Vp8TemporalLayers::FrameConfig& frame_config) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct BufferState {
|
struct BufferState {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "api/video/video_bitrate_allocation.h"
|
#include "api/video/video_bitrate_allocation.h"
|
||||||
#include "api/video_codecs/video_codec.h"
|
#include "api/video_codecs/video_codec.h"
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "common_types.h" // NOLINT(build/include)
|
#include "common_types.h" // NOLINT(build/include)
|
||||||
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
|
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
|
||||||
#include "modules/video_coding/include/video_codec_interface.h"
|
#include "modules/video_coding/include/video_codec_interface.h"
|
||||||
@ -82,7 +83,8 @@ constexpr int kDefaultBytesPerFrame =
|
|||||||
constexpr int kDefaultQp = 2;
|
constexpr int kDefaultQp = 2;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
using BufferFlags = Vp8TemporalLayers::BufferFlags;
|
using BufferFlags = Vp8FrameConfig::BufferFlags;
|
||||||
|
using Vp8BufferReference = Vp8FrameConfig::Vp8BufferReference;
|
||||||
|
|
||||||
class TemporalLayersTest : public ::testing::Test {
|
class TemporalLayersTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
@ -136,7 +138,7 @@ TEST_F(TemporalLayersTest, 2Layers) {
|
|||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
CodecSpecificInfo info;
|
CodecSpecificInfo info;
|
||||||
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
||||||
&vp8_info);
|
&vp8_info);
|
||||||
@ -189,7 +191,7 @@ TEST_F(TemporalLayersTest, 3Layers) {
|
|||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
CodecSpecificInfo info;
|
CodecSpecificInfo info;
|
||||||
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
||||||
&vp8_info);
|
&vp8_info);
|
||||||
@ -231,7 +233,7 @@ TEST_F(TemporalLayersTest, Alternative3Layers) {
|
|||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
CodecSpecificInfo info;
|
CodecSpecificInfo info;
|
||||||
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
||||||
&vp8_info);
|
&vp8_info);
|
||||||
@ -261,7 +263,7 @@ TEST_F(TemporalLayersTest, SearchOrder) {
|
|||||||
|
|
||||||
// Start with a key-frame. tl_config flags can be ignored.
|
// Start with a key-frame. tl_config flags can be ignored.
|
||||||
uint32_t timestamp = 0;
|
uint32_t timestamp = 0;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
|
|
||||||
@ -304,7 +306,7 @@ TEST_F(TemporalLayersTest, SearchOrderWithDrop) {
|
|||||||
|
|
||||||
// Start with a key-frame. tl_config flags can be ignored.
|
// Start with a key-frame. tl_config flags can be ignored.
|
||||||
uint32_t timestamp = 0;
|
uint32_t timestamp = 0;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
|
|
||||||
@ -366,7 +368,7 @@ TEST_F(TemporalLayersTest, 4Layers) {
|
|||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
CodecSpecificInfo info;
|
CodecSpecificInfo info;
|
||||||
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
EXPECT_EQ(expected_flags[i], LibvpxVp8Encoder::EncodeFlags(tl_config)) << i;
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
||||||
&vp8_info);
|
&vp8_info);
|
||||||
@ -395,7 +397,7 @@ TEST_F(TemporalLayersTest, DoesNotReferenceDroppedFrames) {
|
|||||||
|
|
||||||
// Start with a keyframe.
|
// Start with a keyframe.
|
||||||
uint32_t timestamp = 0;
|
uint32_t timestamp = 0;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
|
|
||||||
@ -481,7 +483,7 @@ TEST_F(TemporalLayersTest, DoesNotReferenceUnlessGuaranteedToExist) {
|
|||||||
|
|
||||||
// Start with a keyframe.
|
// Start with a keyframe.
|
||||||
uint32_t timestamp = 0;
|
uint32_t timestamp = 0;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
|
|
||||||
@ -550,7 +552,7 @@ TEST_F(TemporalLayersTest, DoesNotReferenceUnlessGuaranteedToExistLongDelay) {
|
|||||||
|
|
||||||
// Start with a keyframe.
|
// Start with a keyframe.
|
||||||
uint32_t timestamp = 0;
|
uint32_t timestamp = 0;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
|
|
||||||
@ -629,8 +631,7 @@ TEST_F(TemporalLayersTest, KeyFrame) {
|
|||||||
for (int j = 1; j <= i; ++j) {
|
for (int j = 1; j <= i; ++j) {
|
||||||
// Since last frame was always a keyframe and thus index 0 in the pattern,
|
// Since last frame was always a keyframe and thus index 0 in the pattern,
|
||||||
// this loop starts at index 1.
|
// this loop starts at index 1.
|
||||||
Vp8TemporalLayers::FrameConfig tl_config =
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.UpdateLayerConfig(timestamp);
|
|
||||||
EXPECT_EQ(expected_flags[j], LibvpxVp8Encoder::EncodeFlags(tl_config))
|
EXPECT_EQ(expected_flags[j], LibvpxVp8Encoder::EncodeFlags(tl_config))
|
||||||
<< j;
|
<< j;
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, false, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, false, kDefaultQp,
|
||||||
@ -644,7 +645,7 @@ TEST_F(TemporalLayersTest, KeyFrame) {
|
|||||||
|
|
||||||
CodecSpecificInfo info;
|
CodecSpecificInfo info;
|
||||||
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
CodecSpecificInfoVP8& vp8_info = info.codecSpecific.VP8;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||||
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
tl.OnEncodeDone(timestamp, kDefaultBytesPerFrame, true, kDefaultQp,
|
||||||
&vp8_info);
|
&vp8_info);
|
||||||
EXPECT_TRUE(vp8_info.layerSync) << "Key frame should be marked layer sync.";
|
EXPECT_TRUE(vp8_info.layerSync) << "Key frame should be marked layer sync.";
|
||||||
@ -675,9 +676,8 @@ class TemporalLayersReferenceTest : public TemporalLayersTest,
|
|||||||
bool sync;
|
bool sync;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool UpdateSyncRefState(const Vp8TemporalLayers::BufferFlags& flags,
|
bool UpdateSyncRefState(const BufferFlags& flags, BufferState* buffer_state) {
|
||||||
BufferState* buffer_state) {
|
if (flags & BufferFlags::kReference) {
|
||||||
if (flags & Vp8TemporalLayers::kReference) {
|
|
||||||
if (buffer_state->temporal_idx == -1)
|
if (buffer_state->temporal_idx == -1)
|
||||||
return true; // References key-frame.
|
return true; // References key-frame.
|
||||||
if (buffer_state->temporal_idx == 0) {
|
if (buffer_state->temporal_idx == 0) {
|
||||||
@ -691,10 +691,10 @@ class TemporalLayersReferenceTest : public TemporalLayersTest,
|
|||||||
return true; // No reference, does not affect sync frame status.
|
return true; // No reference, does not affect sync frame status.
|
||||||
}
|
}
|
||||||
|
|
||||||
void ValidateReference(const Vp8TemporalLayers::BufferFlags& flags,
|
void ValidateReference(const BufferFlags& flags,
|
||||||
const BufferState& buffer_state,
|
const BufferState& buffer_state,
|
||||||
int temporal_layer) {
|
int temporal_layer) {
|
||||||
if (flags & Vp8TemporalLayers::kReference) {
|
if (flags & BufferFlags::kReference) {
|
||||||
if (temporal_layer > 0 && buffer_state.timestamp > 0) {
|
if (temporal_layer > 0 && buffer_state.timestamp > 0) {
|
||||||
// Check that high layer reference does not go past last sync frame.
|
// Check that high layer reference does not go past last sync frame.
|
||||||
EXPECT_GE(buffer_state.timestamp, last_sync_timestamp_);
|
EXPECT_GE(buffer_state.timestamp, last_sync_timestamp_);
|
||||||
@ -731,9 +731,9 @@ TEST_P(TemporalLayersReferenceTest, ValidFrameConfigs) {
|
|||||||
// (any). If a given buffer is never updated, it is legal to reference it
|
// (any). If a given buffer is never updated, it is legal to reference it
|
||||||
// even for sync frames. In order to be general, don't assume TL0 always
|
// even for sync frames. In order to be general, don't assume TL0 always
|
||||||
// updates |last|.
|
// updates |last|.
|
||||||
std::vector<Vp8TemporalLayers::FrameConfig> tl_configs(kMaxPatternLength);
|
std::vector<Vp8FrameConfig> tl_configs(kMaxPatternLength);
|
||||||
for (int i = 0; i < kMaxPatternLength; ++i) {
|
for (int i = 0; i < kMaxPatternLength; ++i) {
|
||||||
Vp8TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp_);
|
Vp8FrameConfig tl_config = tl.UpdateLayerConfig(timestamp_);
|
||||||
tl.OnEncodeDone(timestamp_, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
tl.OnEncodeDone(timestamp_, kDefaultBytesPerFrame, i == 0, kDefaultQp,
|
||||||
IgnoredCodecSpecificInfoVp8());
|
IgnoredCodecSpecificInfoVp8());
|
||||||
++timestamp_;
|
++timestamp_;
|
||||||
@ -774,11 +774,11 @@ TEST_P(TemporalLayersReferenceTest, ValidFrameConfigs) {
|
|||||||
|
|
||||||
// Update the current layer state.
|
// Update the current layer state.
|
||||||
BufferState state = {temporal_idx, timestamp_, is_sync_frame};
|
BufferState state = {temporal_idx, timestamp_, is_sync_frame};
|
||||||
if (tl_config.last_buffer_flags & Vp8TemporalLayers::kUpdate)
|
if (tl_config.last_buffer_flags & BufferFlags::kUpdate)
|
||||||
last_state = state;
|
last_state = state;
|
||||||
if (tl_config.golden_buffer_flags & Vp8TemporalLayers::kUpdate)
|
if (tl_config.golden_buffer_flags & BufferFlags::kUpdate)
|
||||||
golden_state = state;
|
golden_state = state;
|
||||||
if (tl_config.arf_buffer_flags & Vp8TemporalLayers::kUpdate)
|
if (tl_config.arf_buffer_flags & BufferFlags::kUpdate)
|
||||||
altref_state = state;
|
altref_state = state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "api/video_codecs/vp8_temporal_layers.h"
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -27,9 +28,8 @@ class TemporalLayersChecker {
|
|||||||
explicit TemporalLayersChecker(int num_temporal_layers);
|
explicit TemporalLayersChecker(int num_temporal_layers);
|
||||||
virtual ~TemporalLayersChecker() {}
|
virtual ~TemporalLayersChecker() {}
|
||||||
|
|
||||||
virtual bool CheckTemporalConfig(
|
virtual bool CheckTemporalConfig(bool frame_is_keyframe,
|
||||||
bool frame_is_keyframe,
|
const Vp8FrameConfig& frame_config);
|
||||||
const Vp8TemporalLayers::FrameConfig& frame_config);
|
|
||||||
|
|
||||||
static std::unique_ptr<TemporalLayersChecker> CreateTemporalLayersChecker(
|
static std::unique_ptr<TemporalLayersChecker> CreateTemporalLayersChecker(
|
||||||
Vp8TemporalLayersType type,
|
Vp8TemporalLayersType type,
|
||||||
@ -46,7 +46,7 @@ class TemporalLayersChecker {
|
|||||||
bool* need_sync,
|
bool* need_sync,
|
||||||
bool frame_is_keyframe,
|
bool frame_is_keyframe,
|
||||||
uint8_t temporal_layer,
|
uint8_t temporal_layer,
|
||||||
webrtc::Vp8TemporalLayers::BufferFlags flags,
|
Vp8FrameConfig::BufferFlags flags,
|
||||||
uint32_t sequence_number,
|
uint32_t sequence_number,
|
||||||
uint32_t* lowest_sequence_referenced);
|
uint32_t* lowest_sequence_referenced);
|
||||||
BufferState last_;
|
BufferState last_;
|
||||||
|
@ -132,22 +132,27 @@ std::unique_ptr<VideoEncoder> VP8Encoder::Create() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags(
|
vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags(
|
||||||
const Vp8TemporalLayers::FrameConfig& references) {
|
const Vp8FrameConfig& references) {
|
||||||
RTC_DCHECK(!references.drop_frame);
|
RTC_DCHECK(!references.drop_frame);
|
||||||
|
|
||||||
vpx_enc_frame_flags_t flags = 0;
|
vpx_enc_frame_flags_t flags = 0;
|
||||||
|
|
||||||
if ((references.last_buffer_flags & Vp8TemporalLayers::kReference) == 0)
|
if ((references.last_buffer_flags &
|
||||||
|
Vp8FrameConfig::BufferFlags::kReference) == 0)
|
||||||
flags |= VP8_EFLAG_NO_REF_LAST;
|
flags |= VP8_EFLAG_NO_REF_LAST;
|
||||||
if ((references.last_buffer_flags & Vp8TemporalLayers::kUpdate) == 0)
|
if ((references.last_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
|
||||||
|
0)
|
||||||
flags |= VP8_EFLAG_NO_UPD_LAST;
|
flags |= VP8_EFLAG_NO_UPD_LAST;
|
||||||
if ((references.golden_buffer_flags & Vp8TemporalLayers::kReference) == 0)
|
if ((references.golden_buffer_flags &
|
||||||
|
Vp8FrameConfig::BufferFlags::kReference) == 0)
|
||||||
flags |= VP8_EFLAG_NO_REF_GF;
|
flags |= VP8_EFLAG_NO_REF_GF;
|
||||||
if ((references.golden_buffer_flags & Vp8TemporalLayers::kUpdate) == 0)
|
if ((references.golden_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
|
||||||
|
0)
|
||||||
flags |= VP8_EFLAG_NO_UPD_GF;
|
flags |= VP8_EFLAG_NO_UPD_GF;
|
||||||
if ((references.arf_buffer_flags & Vp8TemporalLayers::kReference) == 0)
|
if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kReference) ==
|
||||||
|
0)
|
||||||
flags |= VP8_EFLAG_NO_REF_ARF;
|
flags |= VP8_EFLAG_NO_REF_ARF;
|
||||||
if ((references.arf_buffer_flags & Vp8TemporalLayers::kUpdate) == 0)
|
if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) == 0)
|
||||||
flags |= VP8_EFLAG_NO_UPD_ARF;
|
flags |= VP8_EFLAG_NO_UPD_ARF;
|
||||||
if (references.freeze_entropy)
|
if (references.freeze_entropy)
|
||||||
flags |= VP8_EFLAG_NO_UPD_ENTROPY;
|
flags |= VP8_EFLAG_NO_UPD_ENTROPY;
|
||||||
@ -757,7 +762,7 @@ int LibvpxVp8Encoder::Encode(const VideoFrame& frame,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
|
vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
|
||||||
Vp8TemporalLayers::FrameConfig tl_configs[kMaxSimulcastStreams];
|
Vp8FrameConfig tl_configs[kMaxSimulcastStreams];
|
||||||
for (size_t i = 0; i < encoders_.size(); ++i) {
|
for (size_t i = 0; i < encoders_.size(); ++i) {
|
||||||
tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp());
|
tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp());
|
||||||
if (tl_configs[i].drop_frame) {
|
if (tl_configs[i].drop_frame) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "api/video/encoded_image.h"
|
#include "api/video/encoded_image.h"
|
||||||
#include "api/video/video_frame.h"
|
#include "api/video/video_frame.h"
|
||||||
#include "api/video_codecs/video_encoder.h"
|
#include "api/video_codecs/video_encoder.h"
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "api/video_codecs/vp8_temporal_layers.h"
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
||||||
#include "common_types.h" // NOLINT(build/include)
|
#include "common_types.h" // NOLINT(build/include)
|
||||||
#include "modules/video_coding/codecs/vp8/include/vp8.h"
|
#include "modules/video_coding/codecs/vp8/include/vp8.h"
|
||||||
@ -52,8 +53,7 @@ class LibvpxVp8Encoder : public VideoEncoder {
|
|||||||
|
|
||||||
EncoderInfo GetEncoderInfo() const override;
|
EncoderInfo GetEncoderInfo() const override;
|
||||||
|
|
||||||
static vpx_enc_frame_flags_t EncodeFlags(
|
static vpx_enc_frame_flags_t EncodeFlags(const Vp8FrameConfig& references);
|
||||||
const Vp8TemporalLayers::FrameConfig& references);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupTemporalLayers(const VideoCodec& codec);
|
void SetupTemporalLayers(const VideoCodec& codec);
|
||||||
|
@ -22,11 +22,20 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
static const int kOneSecond90Khz = 90000;
|
using Buffer = Vp8FrameConfig::Buffer;
|
||||||
static const int kMinTimeBetweenSyncs = kOneSecond90Khz * 2;
|
using BufferFlags = Vp8FrameConfig::BufferFlags;
|
||||||
static const int kMaxTimeBetweenSyncs = kOneSecond90Khz * 4;
|
|
||||||
static const int kQpDeltaThresholdForSync = 8;
|
constexpr BufferFlags kNone = Vp8FrameConfig::BufferFlags::kNone;
|
||||||
static const int kMinBitrateKbpsForQpBoost = 500;
|
constexpr BufferFlags kReference = Vp8FrameConfig::BufferFlags::kReference;
|
||||||
|
constexpr BufferFlags kUpdate = Vp8FrameConfig::BufferFlags::kUpdate;
|
||||||
|
constexpr BufferFlags kReferenceAndUpdate =
|
||||||
|
Vp8FrameConfig::BufferFlags::kReferenceAndUpdate;
|
||||||
|
|
||||||
|
constexpr int kOneSecond90Khz = 90000;
|
||||||
|
constexpr int kMinTimeBetweenSyncs = kOneSecond90Khz * 2;
|
||||||
|
constexpr int kMaxTimeBetweenSyncs = kOneSecond90Khz * 4;
|
||||||
|
constexpr int kQpDeltaThresholdForSync = 8;
|
||||||
|
constexpr int kMinBitrateKbpsForQpBoost = 500;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5;
|
const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5;
|
||||||
@ -68,8 +77,7 @@ bool ScreenshareLayers::SupportsEncoderFrameDropping() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
Vp8FrameConfig ScreenshareLayers::UpdateLayerConfig(uint32_t timestamp) {
|
||||||
uint32_t timestamp) {
|
|
||||||
auto it = pending_frame_configs_.find(timestamp);
|
auto it = pending_frame_configs_.find(timestamp);
|
||||||
if (it != pending_frame_configs_.end()) {
|
if (it != pending_frame_configs_.end()) {
|
||||||
// Drop and re-encode, reuse the previous config.
|
// Drop and re-encode, reuse the previous config.
|
||||||
@ -79,8 +87,8 @@ Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
|||||||
if (number_of_temporal_layers_ <= 1) {
|
if (number_of_temporal_layers_ <= 1) {
|
||||||
// No flags needed for 1 layer screenshare.
|
// No flags needed for 1 layer screenshare.
|
||||||
// TODO(pbos): Consider updating only last, and not all buffers.
|
// TODO(pbos): Consider updating only last, and not all buffers.
|
||||||
Vp8TemporalLayers::FrameConfig tl_config(
|
Vp8FrameConfig tl_config(kReferenceAndUpdate, kReferenceAndUpdate,
|
||||||
kReferenceAndUpdate, kReferenceAndUpdate, kReferenceAndUpdate);
|
kReferenceAndUpdate);
|
||||||
pending_frame_configs_[timestamp] = tl_config;
|
pending_frame_configs_[timestamp] = tl_config;
|
||||||
return tl_config;
|
return tl_config;
|
||||||
}
|
}
|
||||||
@ -100,7 +108,7 @@ Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
|||||||
// averaging window, or if frame interval is below 90% of desired value,
|
// averaging window, or if frame interval is below 90% of desired value,
|
||||||
// drop frame.
|
// drop frame.
|
||||||
if (encode_framerate_.Rate(now_ms).value_or(0) > *target_framerate_)
|
if (encode_framerate_.Rate(now_ms).value_or(0) > *target_framerate_)
|
||||||
return Vp8TemporalLayers::FrameConfig(kNone, kNone, kNone);
|
return Vp8FrameConfig(kNone, kNone, kNone);
|
||||||
|
|
||||||
// Primarily check if frame interval is too short using frame timestamps,
|
// Primarily check if frame interval is too short using frame timestamps,
|
||||||
// as if they are correct they won't be affected by queuing in webrtc.
|
// as if they are correct they won't be affected by queuing in webrtc.
|
||||||
@ -108,7 +116,7 @@ Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
|||||||
kOneSecond90Khz / *target_framerate_;
|
kOneSecond90Khz / *target_framerate_;
|
||||||
if (last_timestamp_ != -1 && ts_diff > 0) {
|
if (last_timestamp_ != -1 && ts_diff > 0) {
|
||||||
if (ts_diff < 85 * expected_frame_interval_90khz / 100) {
|
if (ts_diff < 85 * expected_frame_interval_90khz / 100) {
|
||||||
return Vp8TemporalLayers::FrameConfig(kNone, kNone, kNone);
|
return Vp8FrameConfig(kNone, kNone, kNone);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Timestamps looks off, use realtime clock here instead.
|
// Timestamps looks off, use realtime clock here instead.
|
||||||
@ -116,7 +124,7 @@ Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
|||||||
if (last_frame_time_ms_ != -1 &&
|
if (last_frame_time_ms_ != -1 &&
|
||||||
now_ms - last_frame_time_ms_ <
|
now_ms - last_frame_time_ms_ <
|
||||||
(85 * expected_frame_interval_ms) / 100) {
|
(85 * expected_frame_interval_ms) / 100) {
|
||||||
return Vp8TemporalLayers::FrameConfig(kNone, kNone, kNone);
|
return Vp8FrameConfig(kNone, kNone, kNone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,30 +190,28 @@ Vp8TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
|
|||||||
RTC_NOTREACHED();
|
RTC_NOTREACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig tl_config;
|
Vp8FrameConfig tl_config;
|
||||||
// TODO(pbos): Consider referencing but not updating the 'alt' buffer for all
|
// TODO(pbos): Consider referencing but not updating the 'alt' buffer for all
|
||||||
// layers.
|
// layers.
|
||||||
switch (layer_state) {
|
switch (layer_state) {
|
||||||
case TemporalLayerState::kDrop:
|
case TemporalLayerState::kDrop:
|
||||||
tl_config = Vp8TemporalLayers::FrameConfig(kNone, kNone, kNone);
|
tl_config = Vp8FrameConfig(kNone, kNone, kNone);
|
||||||
break;
|
break;
|
||||||
case TemporalLayerState::kTl0:
|
case TemporalLayerState::kTl0:
|
||||||
// TL0 only references and updates 'last'.
|
// TL0 only references and updates 'last'.
|
||||||
tl_config =
|
tl_config = Vp8FrameConfig(kReferenceAndUpdate, kNone, kNone);
|
||||||
Vp8TemporalLayers::FrameConfig(kReferenceAndUpdate, kNone, kNone);
|
|
||||||
tl_config.packetizer_temporal_idx = 0;
|
tl_config.packetizer_temporal_idx = 0;
|
||||||
break;
|
break;
|
||||||
case TemporalLayerState::kTl1:
|
case TemporalLayerState::kTl1:
|
||||||
// TL1 references both 'last' and 'golden' but only updates 'golden'.
|
// TL1 references both 'last' and 'golden' but only updates 'golden'.
|
||||||
tl_config = Vp8TemporalLayers::FrameConfig(kReference,
|
tl_config = Vp8FrameConfig(kReference, kReferenceAndUpdate, kNone);
|
||||||
kReferenceAndUpdate, kNone);
|
|
||||||
tl_config.packetizer_temporal_idx = 1;
|
tl_config.packetizer_temporal_idx = 1;
|
||||||
break;
|
break;
|
||||||
case TemporalLayerState::kTl1Sync:
|
case TemporalLayerState::kTl1Sync:
|
||||||
// Predict from only TL0 to allow participants to switch to the high
|
// Predict from only TL0 to allow participants to switch to the high
|
||||||
// bitrate stream. Updates 'golden' so that TL1 can continue to refer to
|
// bitrate stream. Updates 'golden' so that TL1 can continue to refer to
|
||||||
// and update 'golden' from this point on.
|
// and update 'golden' from this point on.
|
||||||
tl_config = Vp8TemporalLayers::FrameConfig(kReference, kUpdate, kNone);
|
tl_config = Vp8FrameConfig(kReference, kUpdate, kNone);
|
||||||
tl_config.packetizer_temporal_idx = 1;
|
tl_config.packetizer_temporal_idx = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -266,7 +272,7 @@ void ScreenshareLayers::OnEncodeDone(uint32_t rtp_timestamp,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::optional<FrameConfig> frame_config;
|
absl::optional<Vp8FrameConfig> frame_config;
|
||||||
auto it = pending_frame_configs_.find(rtp_timestamp);
|
auto it = pending_frame_configs_.find(rtp_timestamp);
|
||||||
if (it != pending_frame_configs_.end()) {
|
if (it != pending_frame_configs_.end()) {
|
||||||
frame_config = it->second;
|
frame_config = it->second;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "api/video_codecs/vp8_temporal_layers.h"
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
||||||
#include "modules/video_coding/codecs/vp8/include/temporal_layers_checker.h"
|
#include "modules/video_coding/codecs/vp8/include/temporal_layers_checker.h"
|
||||||
#include "modules/video_coding/utility/frame_dropper.h"
|
#include "modules/video_coding/utility/frame_dropper.h"
|
||||||
@ -38,8 +39,7 @@ class ScreenshareLayers : public Vp8TemporalLayers {
|
|||||||
|
|
||||||
// Returns the recommended VP8 encode flags needed. May refresh the decoder
|
// Returns the recommended VP8 encode flags needed. May refresh the decoder
|
||||||
// and/or update the reference buffers.
|
// and/or update the reference buffers.
|
||||||
Vp8TemporalLayers::FrameConfig UpdateLayerConfig(
|
Vp8FrameConfig UpdateLayerConfig(uint32_t rtp_timestamp) override;
|
||||||
uint32_t rtp_timestamp) override;
|
|
||||||
|
|
||||||
// New target bitrate, per temporal layer.
|
// New target bitrate, per temporal layer.
|
||||||
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
|
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
|
||||||
@ -74,7 +74,7 @@ class ScreenshareLayers : public Vp8TemporalLayers {
|
|||||||
int max_qp_;
|
int max_qp_;
|
||||||
uint32_t max_debt_bytes_;
|
uint32_t max_debt_bytes_;
|
||||||
|
|
||||||
std::map<uint32_t, Vp8TemporalLayers::FrameConfig> pending_frame_configs_;
|
std::map<uint32_t, Vp8FrameConfig> pending_frame_configs_;
|
||||||
|
|
||||||
// Configured max framerate.
|
// Configured max framerate.
|
||||||
absl::optional<uint32_t> target_framerate_;
|
absl::optional<uint32_t> target_framerate_;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "modules/video_coding/codecs/interface/common_constants.h"
|
#include "modules/video_coding/codecs/interface/common_constants.h"
|
||||||
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
|
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
|
||||||
#include "modules/video_coding/codecs/vp8/screenshare_layers.h"
|
#include "modules/video_coding/codecs/vp8/screenshare_layers.h"
|
||||||
@ -93,7 +94,7 @@ class ScreenshareLayerTest : public ::testing::Test {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vp8TemporalLayers::FrameConfig UpdateLayerConfig(uint32_t timestamp) {
|
Vp8FrameConfig UpdateLayerConfig(uint32_t timestamp) {
|
||||||
int64_t timestamp_ms = timestamp / 90;
|
int64_t timestamp_ms = timestamp / 90;
|
||||||
clock_.AdvanceTimeMilliseconds(timestamp_ms - clock_.TimeInMilliseconds());
|
clock_.AdvanceTimeMilliseconds(timestamp_ms - clock_.TimeInMilliseconds());
|
||||||
return layers_->UpdateLayerConfig(timestamp);
|
return layers_->UpdateLayerConfig(timestamp);
|
||||||
@ -173,7 +174,7 @@ class ScreenshareLayerTest : public ::testing::Test {
|
|||||||
std::unique_ptr<ScreenshareLayers> layers_;
|
std::unique_ptr<ScreenshareLayers> layers_;
|
||||||
|
|
||||||
uint32_t timestamp_;
|
uint32_t timestamp_;
|
||||||
Vp8TemporalLayers::FrameConfig tl_config_;
|
Vp8FrameConfig tl_config_;
|
||||||
Vp8EncoderConfig cfg_;
|
Vp8EncoderConfig cfg_;
|
||||||
bool config_updated_;
|
bool config_updated_;
|
||||||
CodecSpecificInfoVP8 vp8_info_;
|
CodecSpecificInfoVP8 vp8_info_;
|
||||||
|
@ -40,10 +40,10 @@ bool TemporalLayersChecker::CheckAndUpdateBufferState(
|
|||||||
bool* need_sync,
|
bool* need_sync,
|
||||||
bool frame_is_keyframe,
|
bool frame_is_keyframe,
|
||||||
uint8_t temporal_layer,
|
uint8_t temporal_layer,
|
||||||
webrtc::Vp8TemporalLayers::BufferFlags flags,
|
Vp8FrameConfig::BufferFlags flags,
|
||||||
uint32_t sequence_number,
|
uint32_t sequence_number,
|
||||||
uint32_t* lowest_sequence_referenced) {
|
uint32_t* lowest_sequence_referenced) {
|
||||||
if (flags & Vp8TemporalLayers::BufferFlags::kReference) {
|
if (flags & Vp8FrameConfig::BufferFlags::kReference) {
|
||||||
if (state->temporal_layer > 0 && !state->is_keyframe) {
|
if (state->temporal_layer > 0 && !state->is_keyframe) {
|
||||||
*need_sync = false;
|
*need_sync = false;
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ bool TemporalLayersChecker::CheckAndUpdateBufferState(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((flags & Vp8TemporalLayers::BufferFlags::kUpdate)) {
|
if ((flags & Vp8FrameConfig::BufferFlags::kUpdate)) {
|
||||||
state->temporal_layer = temporal_layer;
|
state->temporal_layer = temporal_layer;
|
||||||
state->sequence_number = sequence_number;
|
state->sequence_number = sequence_number;
|
||||||
state->is_keyframe = frame_is_keyframe;
|
state->is_keyframe = frame_is_keyframe;
|
||||||
@ -69,7 +69,7 @@ bool TemporalLayersChecker::CheckAndUpdateBufferState(
|
|||||||
|
|
||||||
bool TemporalLayersChecker::CheckTemporalConfig(
|
bool TemporalLayersChecker::CheckTemporalConfig(
|
||||||
bool frame_is_keyframe,
|
bool frame_is_keyframe,
|
||||||
const Vp8TemporalLayers::FrameConfig& frame_config) {
|
const Vp8FrameConfig& frame_config) {
|
||||||
if (frame_config.drop_frame ||
|
if (frame_config.drop_frame ||
|
||||||
frame_config.packetizer_temporal_idx == kNoTemporalIdx) {
|
frame_config.packetizer_temporal_idx == kNoTemporalIdx) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/video_codecs/vp8_frame_config.h"
|
||||||
#include "api/video_codecs/vp8_temporal_layers.h"
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "test/field_trial.h"
|
#include "test/field_trial.h"
|
||||||
@ -36,15 +37,14 @@ constexpr uint32_t kSimulcastScreenshareMaxBitrateKbps = 1250;
|
|||||||
|
|
||||||
class MockTemporalLayers : public Vp8TemporalLayers {
|
class MockTemporalLayers : public Vp8TemporalLayers {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD1(UpdateLayerConfig, Vp8TemporalLayers::FrameConfig(uint32_t));
|
MOCK_METHOD1(UpdateLayerConfig, Vp8FrameConfig(uint32_t));
|
||||||
MOCK_METHOD2(OnRatesUpdated, void(const std::vector<uint32_t>&, int));
|
MOCK_METHOD2(OnRatesUpdated, void(const std::vector<uint32_t>&, int));
|
||||||
MOCK_METHOD1(UpdateConfiguration, bool(Vp8EncoderConfig*));
|
MOCK_METHOD1(UpdateConfiguration, bool(Vp8EncoderConfig*));
|
||||||
MOCK_METHOD5(OnEncodeDone,
|
MOCK_METHOD5(OnEncodeDone,
|
||||||
void(uint32_t, size_t, bool, int, CodecSpecificInfoVP8*));
|
void(uint32_t, size_t, bool, int, CodecSpecificInfoVP8*));
|
||||||
MOCK_METHOD3(FrameEncoded, void(uint32_t, size_t, int));
|
MOCK_METHOD3(FrameEncoded, void(uint32_t, size_t, int));
|
||||||
MOCK_CONST_METHOD0(Tl0PicIdx, uint8_t());
|
MOCK_CONST_METHOD0(Tl0PicIdx, uint8_t());
|
||||||
MOCK_CONST_METHOD1(GetTemporalLayerId,
|
MOCK_CONST_METHOD1(GetTemporalLayerId, int(const Vp8FrameConfig&));
|
||||||
int(const Vp8TemporalLayers::FrameConfig&));
|
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user