Implement temporal layers checkers for vp8
All frames are checked against hard-coded dependency graph using new helper class. It's invoked in RTC_DCHECK(). Only DefaultTemporalLayers are fully implemented in this CL, checker for ScreenshareLayers is not doing anything for now. Bug: none Change-Id: I3ec017176d8c25f7572c8f161e52f2ebfac8220f Reviewed-on: https://webrtc-review.googlesource.com/3740 Reviewed-by: Per Kjellander <perkj@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20066}
This commit is contained in:
committed by
Commit Bot
parent
581df618fe
commit
bf35298996
@ -1,11 +1,11 @@
|
||||
/* Copyright (c) 2013 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.
|
||||
*/
|
||||
*
|
||||
* 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 "modules/video_coding/codecs/vp8/default_temporal_layers.h"
|
||||
|
||||
@ -13,16 +13,19 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "modules/include/module_common_types.h"
|
||||
#include "modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -424,8 +427,166 @@ TemporalLayers* TemporalLayersFactory::Create(
|
||||
return tl;
|
||||
}
|
||||
|
||||
std::unique_ptr<TemporalLayersChecker> TemporalLayersFactory::CreateChecker(
|
||||
int /*simulcast_id*/,
|
||||
int temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const {
|
||||
TemporalLayersChecker* tlc =
|
||||
new DefaultTemporalLayersChecker(temporal_layers, initial_tl0_pic_idx);
|
||||
return std::unique_ptr<TemporalLayersChecker>(tlc);
|
||||
}
|
||||
|
||||
void TemporalLayersFactory::SetListener(TemporalLayersListener* listener) {
|
||||
listener_ = listener;
|
||||
}
|
||||
|
||||
// Returns list of temporal dependencies for each frame in the temporal pattern.
|
||||
// Values are lists of indecies in the pattern.
|
||||
std::vector<std::set<uint8_t>> GetTemporalDependencies(
|
||||
int num_temporal_layers) {
|
||||
switch (num_temporal_layers) {
|
||||
case 1:
|
||||
return {{0}};
|
||||
case 2:
|
||||
return {{6}, {0}, {0}, {1, 2}, {2}, {3, 4}, {4}, {5, 6}};
|
||||
case 3:
|
||||
if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) {
|
||||
return {{0}, {0}, {0}, {0, 1, 2}};
|
||||
} else {
|
||||
return {{4}, {0}, {0}, {0, 2}, {0}, {2, 4}, {2, 4}, {4, 6}};
|
||||
}
|
||||
case 4:
|
||||
return {{8}, {0}, {0}, {0, 2},
|
||||
{0}, {0, 2, 4}, {0, 2, 4}, {0, 4, 6},
|
||||
{0}, {4, 6, 8}, {4, 6, 8}, {4, 8, 10},
|
||||
{4, 8}, {8, 10, 12}, {8, 10, 12}, {8, 12, 14}};
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
DefaultTemporalLayersChecker::DefaultTemporalLayersChecker(
|
||||
int num_temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx)
|
||||
: num_layers_(std::max(1, num_temporal_layers)),
|
||||
temporal_ids_(GetTemporalIds(num_layers_)),
|
||||
temporal_dependencies_(GetTemporalDependencies(num_layers_)),
|
||||
pattern_idx_(255) {
|
||||
int i = 0;
|
||||
while (temporal_ids_.size() < temporal_dependencies_.size()) {
|
||||
temporal_ids_.push_back(temporal_ids_[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
bool DefaultTemporalLayersChecker::CheckTemporalConfig(
|
||||
bool frame_is_keyframe,
|
||||
const TemporalLayers::FrameConfig& frame_config) {
|
||||
++pattern_idx_;
|
||||
if (pattern_idx_ == temporal_ids_.size()) {
|
||||
// All non key-frame buffers should be updated each pattern cycle.
|
||||
if (!last_.is_keyframe && !last_.is_updated_this_cycle) {
|
||||
LOG(LS_ERROR) << "Last buffer was not updated during pattern cycle.";
|
||||
return false;
|
||||
}
|
||||
if (!arf_.is_keyframe && !arf_.is_updated_this_cycle) {
|
||||
LOG(LS_ERROR) << "Arf buffer was not updated during pattern cycle.";
|
||||
return false;
|
||||
}
|
||||
if (!golden_.is_keyframe && !golden_.is_updated_this_cycle) {
|
||||
LOG(LS_ERROR) << "Golden buffer was not updated during pattern cycle.";
|
||||
return false;
|
||||
}
|
||||
last_.is_updated_this_cycle = false;
|
||||
arf_.is_updated_this_cycle = false;
|
||||
golden_.is_updated_this_cycle = false;
|
||||
pattern_idx_ = 0;
|
||||
}
|
||||
uint8_t expected_tl_idx = temporal_ids_[pattern_idx_];
|
||||
if (frame_config.packetizer_temporal_idx != expected_tl_idx) {
|
||||
LOG(LS_ERROR) << "Frame has an incorrect temporal index. Expected: "
|
||||
<< static_cast<int>(expected_tl_idx) << " Actual: "
|
||||
<< static_cast<int>(frame_config.packetizer_temporal_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool need_sync = temporal_ids_[pattern_idx_] > 0 &&
|
||||
temporal_ids_[pattern_idx_] != kNoTemporalIdx;
|
||||
std::vector<int> dependencies;
|
||||
|
||||
if (frame_config.last_buffer_flags &
|
||||
TemporalLayers::BufferFlags::kReference) {
|
||||
uint8_t referenced_layer = temporal_ids_[last_.pattern_idx];
|
||||
if (referenced_layer > 0) {
|
||||
need_sync = false;
|
||||
}
|
||||
if (!last_.is_keyframe) {
|
||||
dependencies.push_back(last_.pattern_idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_config.arf_buffer_flags & TemporalLayers::BufferFlags::kReference) {
|
||||
uint8_t referenced_layer = temporal_ids_[arf_.pattern_idx];
|
||||
if (referenced_layer > 0) {
|
||||
need_sync = false;
|
||||
}
|
||||
if (!arf_.is_keyframe) {
|
||||
dependencies.push_back(arf_.pattern_idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_config.golden_buffer_flags &
|
||||
TemporalLayers::BufferFlags::kReference) {
|
||||
uint8_t referenced_layer = temporal_ids_[golden_.pattern_idx];
|
||||
if (referenced_layer > 0) {
|
||||
need_sync = false;
|
||||
}
|
||||
if (!golden_.is_keyframe) {
|
||||
dependencies.push_back(golden_.pattern_idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_sync != frame_config.layer_sync) {
|
||||
LOG(LS_ERROR) << "Sync bit is set incorrectly on a frame. Expected: "
|
||||
<< need_sync << " Actual: " << frame_config.layer_sync;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frame_is_keyframe) {
|
||||
size_t i;
|
||||
for (i = 0; i < dependencies.size(); ++i) {
|
||||
if (temporal_dependencies_[pattern_idx_].find(dependencies[i]) ==
|
||||
temporal_dependencies_[pattern_idx_].end()) {
|
||||
LOG(LS_ERROR) << "Illegal temporal dependency out of defined pattern "
|
||||
"from position "
|
||||
<< static_cast<int>(pattern_idx_) << " to position "
|
||||
<< static_cast<int>(dependencies[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_config.last_buffer_flags & TemporalLayers::BufferFlags::kUpdate) {
|
||||
last_.is_updated_this_cycle = true;
|
||||
last_.pattern_idx = pattern_idx_;
|
||||
last_.is_keyframe = false;
|
||||
}
|
||||
if (frame_config.arf_buffer_flags & TemporalLayers::BufferFlags::kUpdate) {
|
||||
arf_.is_updated_this_cycle = true;
|
||||
arf_.pattern_idx = pattern_idx_;
|
||||
arf_.is_keyframe = false;
|
||||
}
|
||||
if (frame_config.golden_buffer_flags & TemporalLayers::BufferFlags::kUpdate) {
|
||||
golden_.is_updated_this_cycle = true;
|
||||
golden_.pattern_idx = pattern_idx_;
|
||||
golden_.is_keyframe = false;
|
||||
}
|
||||
if (frame_is_keyframe) {
|
||||
last_.is_keyframe = true;
|
||||
arf_.is_keyframe = true;
|
||||
golden_.is_keyframe = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/* Copyright (c) 2013 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.
|
||||
*/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* This file defines classes for doing temporal layers with VP8.
|
||||
*/
|
||||
* This file defines classes for doing temporal layers with VP8.
|
||||
*/
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||
@ -59,5 +60,31 @@ class DefaultTemporalLayers : public TemporalLayers {
|
||||
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
|
||||
};
|
||||
|
||||
class DefaultTemporalLayersChecker : public TemporalLayersChecker {
|
||||
public:
|
||||
DefaultTemporalLayersChecker(int number_of_temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx);
|
||||
bool CheckTemporalConfig(
|
||||
bool frame_is_keyframe,
|
||||
const TemporalLayers::FrameConfig& frame_config) override;
|
||||
|
||||
private:
|
||||
struct BufferState {
|
||||
BufferState()
|
||||
: is_updated_this_cycle(false), is_keyframe(true), pattern_idx(0) {}
|
||||
|
||||
bool is_updated_this_cycle;
|
||||
bool is_keyframe;
|
||||
uint8_t pattern_idx;
|
||||
};
|
||||
const size_t num_layers_;
|
||||
std::vector<unsigned int> temporal_ids_;
|
||||
const std::vector<std::set<uint8_t>> temporal_dependencies_;
|
||||
BufferState last_;
|
||||
BufferState arf_;
|
||||
BufferState golden_;
|
||||
uint8_t pattern_idx_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
||||
|
||||
@ -9,12 +9,12 @@
|
||||
*/
|
||||
|
||||
#include "modules/video_coding/codecs/vp8/default_temporal_layers.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "modules/video_coding/codecs/vp8/vp8_impl.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gtest.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
@ -53,6 +53,7 @@ enum {
|
||||
|
||||
TEST(TemporalLayersTest, 2Layers) {
|
||||
DefaultTemporalLayers tl(2, 0);
|
||||
DefaultTemporalLayersChecker checker(2, 0);
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
CodecSpecificInfoVP8 vp8_info;
|
||||
tl.OnRatesUpdated(500, 500, 30);
|
||||
@ -87,11 +88,12 @@ TEST(TemporalLayersTest, 2Layers) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i;
|
||||
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
|
||||
tl.PopulateCodecSpecific(i == 0, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(i == 0, tl_config));
|
||||
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id);
|
||||
EXPECT_EQ(expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(i == 0 || expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(expected_layer_sync[i], tl_config.layer_sync);
|
||||
timestamp += 3000;
|
||||
}
|
||||
@ -99,6 +101,7 @@ TEST(TemporalLayersTest, 2Layers) {
|
||||
|
||||
TEST(TemporalLayersTest, 3Layers) {
|
||||
DefaultTemporalLayers tl(3, 0);
|
||||
DefaultTemporalLayersChecker checker(3, 0);
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
CodecSpecificInfoVP8 vp8_info;
|
||||
tl.OnRatesUpdated(500, 500, 30);
|
||||
@ -133,11 +136,12 @@ TEST(TemporalLayersTest, 3Layers) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i;
|
||||
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
|
||||
tl.PopulateCodecSpecific(i == 0, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(i == 0, tl_config));
|
||||
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id);
|
||||
EXPECT_EQ(expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(i == 0 || expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(expected_layer_sync[i], tl_config.layer_sync);
|
||||
timestamp += 3000;
|
||||
}
|
||||
@ -146,6 +150,7 @@ TEST(TemporalLayersTest, 3Layers) {
|
||||
TEST(TemporalLayersTest, Alternative3Layers) {
|
||||
ScopedFieldTrials field_trial("WebRTC-UseShortVP8TL3Pattern/Enabled/");
|
||||
DefaultTemporalLayers tl(3, 0);
|
||||
DefaultTemporalLayersChecker checker(3, 0);
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
CodecSpecificInfoVP8 vp8_info;
|
||||
tl.OnRatesUpdated(500, 500, 30);
|
||||
@ -168,11 +173,12 @@ TEST(TemporalLayersTest, Alternative3Layers) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i;
|
||||
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
|
||||
tl.PopulateCodecSpecific(i == 0, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(i == 0, tl_config));
|
||||
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id);
|
||||
EXPECT_EQ(expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(i == 0 || expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(expected_layer_sync[i], tl_config.layer_sync);
|
||||
timestamp += 3000;
|
||||
}
|
||||
@ -180,6 +186,7 @@ TEST(TemporalLayersTest, Alternative3Layers) {
|
||||
|
||||
TEST(TemporalLayersTest, 4Layers) {
|
||||
DefaultTemporalLayers tl(4, 0);
|
||||
DefaultTemporalLayersChecker checker(4, 0);
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
CodecSpecificInfoVP8 vp8_info;
|
||||
tl.OnRatesUpdated(500, 500, 30);
|
||||
@ -213,11 +220,12 @@ TEST(TemporalLayersTest, 4Layers) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i;
|
||||
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
|
||||
tl.PopulateCodecSpecific(i == 0, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(i == 0, tl_config));
|
||||
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id);
|
||||
EXPECT_EQ(expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(i == 0 || expected_layer_sync[i], vp8_info.layerSync);
|
||||
EXPECT_EQ(expected_layer_sync[i], tl_config.layer_sync);
|
||||
timestamp += 3000;
|
||||
}
|
||||
@ -225,6 +233,7 @@ TEST(TemporalLayersTest, 4Layers) {
|
||||
|
||||
TEST(TemporalLayersTest, KeyFrame) {
|
||||
DefaultTemporalLayers tl(3, 0);
|
||||
DefaultTemporalLayersChecker checker(3, 0);
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
CodecSpecificInfoVP8 vp8_info;
|
||||
tl.OnRatesUpdated(500, 500, 30);
|
||||
@ -249,6 +258,7 @@ TEST(TemporalLayersTest, KeyFrame) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i;
|
||||
tl.PopulateCodecSpecific(true, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(true, tl_config));
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx);
|
||||
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id);
|
||||
EXPECT_EQ(0, vp8_info.temporalIdx)
|
||||
@ -260,6 +270,7 @@ TEST(TemporalLayersTest, KeyFrame) {
|
||||
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp);
|
||||
EXPECT_EQ(expected_flags[7], VP8EncoderImpl::EncodeFlags(tl_config));
|
||||
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
|
||||
EXPECT_TRUE(checker.CheckTemporalConfig(false, tl_config));
|
||||
EXPECT_NE(0, vp8_info.temporalIdx)
|
||||
<< "To test something useful, this frame should not use layer 0.";
|
||||
EXPECT_EQ(expected_temporal_idx[7], vp8_info.temporalIdx)
|
||||
|
||||
@ -1,24 +1,25 @@
|
||||
/* Copyright (c) 2013 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.
|
||||
*/
|
||||
*
|
||||
* 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 "modules/video_coding/codecs/vp8/screenshare_layers.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -54,6 +55,23 @@ webrtc::TemporalLayers* ScreenshareTemporalLayersFactory::Create(
|
||||
return tl;
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::TemporalLayersChecker>
|
||||
ScreenshareTemporalLayersFactory::CreateChecker(
|
||||
int simulcast_id,
|
||||
int temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const {
|
||||
webrtc::TemporalLayersChecker* tlc;
|
||||
if (simulcast_id == 0) {
|
||||
tlc = new webrtc::ScreenshareTemporalLayersChecker(temporal_layers,
|
||||
initial_tl0_pic_idx);
|
||||
} else {
|
||||
TemporalLayersFactory rt_tl_factory;
|
||||
return rt_tl_factory.CreateChecker(simulcast_id, temporal_layers,
|
||||
initial_tl0_pic_idx);
|
||||
}
|
||||
return std::unique_ptr<webrtc::TemporalLayersChecker>(tlc);
|
||||
}
|
||||
|
||||
ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx,
|
||||
Clock* clock)
|
||||
@ -433,8 +451,9 @@ void ScreenshareLayers::UpdateHistograms() {
|
||||
int total_frames = stats_.num_tl0_frames_ + stats_.num_tl1_frames_;
|
||||
RTC_HISTOGRAM_COUNTS_10000(
|
||||
"WebRTC.Video.Screenshare.FramesPerDrop",
|
||||
(stats_.num_dropped_frames_ == 0 ? 0 : total_frames /
|
||||
stats_.num_dropped_frames_));
|
||||
(stats_.num_dropped_frames_ == 0
|
||||
? 0
|
||||
: total_frames / stats_.num_dropped_frames_));
|
||||
RTC_HISTOGRAM_COUNTS_10000(
|
||||
"WebRTC.Video.Screenshare.FramesPerOvershoot",
|
||||
(stats_.num_overshoots_ == 0 ? 0
|
||||
@ -456,4 +475,16 @@ void ScreenshareLayers::UpdateHistograms() {
|
||||
}
|
||||
}
|
||||
|
||||
ScreenshareTemporalLayersChecker::ScreenshareTemporalLayersChecker(
|
||||
int /*num_temporal_layers*/,
|
||||
uint8_t /*initial_tl0_pic_idx*/) {}
|
||||
|
||||
// TODO(ilnik): Implement layers dependency checks here. Keep track of
|
||||
// last/golden/arf buffers and sync bits.
|
||||
bool ScreenshareTemporalLayersChecker::CheckTemporalConfig(
|
||||
bool /*frame_is_keyframe*/,
|
||||
const TemporalLayers::FrameConfig& /*frame_config*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/* Copyright (c) 2013 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.
|
||||
*/
|
||||
*
|
||||
* 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 MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
||||
|
||||
@ -122,6 +122,15 @@ class ScreenshareLayers : public TemporalLayers {
|
||||
int64_t tl1_target_bitrate_sum_ = 0;
|
||||
} stats_;
|
||||
};
|
||||
|
||||
class ScreenshareTemporalLayersChecker : public TemporalLayersChecker {
|
||||
public:
|
||||
ScreenshareTemporalLayersChecker(int number_of_temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx);
|
||||
bool CheckTemporalConfig(
|
||||
bool frame_is_keyframe,
|
||||
const TemporalLayers::FrameConfig& frame_config) override;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
/* Copyright (c) 2011 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.
|
||||
*/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* This file defines the interface for doing temporal layers with VP8.
|
||||
*/
|
||||
* This file defines the interface for doing temporal layers with VP8.
|
||||
*/
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "typedefs.h" // NOLINT(build/include)
|
||||
|
||||
struct vpx_codec_enc_cfg;
|
||||
typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t;
|
||||
|
||||
@ -115,6 +115,8 @@ class TemporalLayers {
|
||||
};
|
||||
|
||||
class TemporalLayersListener;
|
||||
class TemporalLayersChecker;
|
||||
|
||||
class TemporalLayersFactory {
|
||||
public:
|
||||
TemporalLayersFactory() : listener_(nullptr) {}
|
||||
@ -122,6 +124,15 @@ class TemporalLayersFactory {
|
||||
virtual TemporalLayers* Create(int simulcast_id,
|
||||
int temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const;
|
||||
|
||||
// Creates helper class which performs online checks of a correctness of
|
||||
// temporal layers dependencies returned by TemporalLayers class created in
|
||||
// the same factory.
|
||||
virtual std::unique_ptr<TemporalLayersChecker> CreateChecker(
|
||||
int simulcast_id,
|
||||
int temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const;
|
||||
|
||||
void SetListener(TemporalLayersListener* listener);
|
||||
|
||||
protected:
|
||||
@ -136,6 +147,14 @@ class ScreenshareTemporalLayersFactory : public webrtc::TemporalLayersFactory {
|
||||
webrtc::TemporalLayers* Create(int simulcast_id,
|
||||
int num_temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const override;
|
||||
|
||||
// Creates helper class which performs online checks of a correctness of
|
||||
// temporal layers dependencies returned by TemporalLayers class created in
|
||||
// the same factory.
|
||||
std::unique_ptr<webrtc::TemporalLayersChecker> CreateChecker(
|
||||
int simulcast_id,
|
||||
int temporal_layers,
|
||||
uint8_t initial_tl0_pic_idx) const override;
|
||||
};
|
||||
|
||||
class TemporalLayersListener {
|
||||
@ -147,5 +166,18 @@ class TemporalLayersListener {
|
||||
TemporalLayers* layers) = 0;
|
||||
};
|
||||
|
||||
// Used only inside RTC_DCHECK(). It checks correctness of temporal layers
|
||||
// dependencies and sync bits. The only method of this class is called after
|
||||
// each UpdateLayersConfig() of a corresponding TemporalLayers class.
|
||||
class TemporalLayersChecker {
|
||||
public:
|
||||
TemporalLayersChecker() {}
|
||||
virtual ~TemporalLayersChecker() {}
|
||||
|
||||
virtual bool CheckTemporalConfig(
|
||||
bool frame_is_keyframe,
|
||||
const TemporalLayers::FrameConfig& frame_config) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
#include <string>
|
||||
|
||||
// NOTE(ajm): Path provided by gyp.
|
||||
#include "libyuv/scale.h" // NOLINT
|
||||
#include "libyuv/convert.h" // NOLINT
|
||||
#include "libyuv/scale.h" // NOLINT
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
||||
@ -224,6 +224,7 @@ VP8EncoderImpl::VP8EncoderImpl()
|
||||
tl0_pic_idx_.push_back(random.Rand<uint8_t>());
|
||||
}
|
||||
temporal_layers_.reserve(kMaxSimulcastStreams);
|
||||
temporal_layers_checkers_.reserve(kMaxSimulcastStreams);
|
||||
raw_images_.reserve(kMaxSimulcastStreams);
|
||||
encoded_images_.reserve(kMaxSimulcastStreams);
|
||||
send_stream_.reserve(kMaxSimulcastStreams);
|
||||
@ -263,6 +264,7 @@ int VP8EncoderImpl::Release() {
|
||||
tl0_pic_idx_[i] = temporal_layers_[i]->Tl0PicIdx();
|
||||
}
|
||||
temporal_layers_.clear();
|
||||
temporal_layers_checkers_.clear();
|
||||
inited_ = false;
|
||||
return ret_val;
|
||||
}
|
||||
@ -332,8 +334,7 @@ const char* VP8EncoderImpl::ImplementationName() const {
|
||||
return "libvpx";
|
||||
}
|
||||
|
||||
void VP8EncoderImpl::SetStreamState(bool send_stream,
|
||||
int stream_idx) {
|
||||
void VP8EncoderImpl::SetStreamState(bool send_stream, int stream_idx) {
|
||||
if (send_stream && !send_stream_[stream_idx]) {
|
||||
// Need a key frame if we have not sent this stream before.
|
||||
key_frame_request_[stream_idx] = true;
|
||||
@ -349,6 +350,8 @@ void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
|
||||
if (num_streams == 1) {
|
||||
temporal_layers_.emplace_back(
|
||||
tl_factory->Create(0, num_temporal_layers, tl0_pic_idx_[0]));
|
||||
temporal_layers_checkers_.emplace_back(
|
||||
tl_factory->CreateChecker(0, num_temporal_layers, tl0_pic_idx_[0]));
|
||||
} else {
|
||||
for (int i = 0; i < num_streams; ++i) {
|
||||
RTC_CHECK_GT(num_temporal_layers, 0);
|
||||
@ -356,6 +359,8 @@ void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
|
||||
codec.simulcastStream[i].numberOfTemporalLayers);
|
||||
temporal_layers_.emplace_back(
|
||||
tl_factory->Create(i, layers, tl0_pic_idx_[i]));
|
||||
temporal_layers_checkers_.emplace_back(
|
||||
tl_factory->CreateChecker(i, layers, tl0_pic_idx_[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -754,17 +759,6 @@ int VP8EncoderImpl::Encode(const VideoFrame& frame,
|
||||
raw_images_[i].stride[VPX_PLANE_V], raw_images_[i].d_w,
|
||||
raw_images_[i].d_h, libyuv::kFilterBilinear);
|
||||
}
|
||||
vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
|
||||
TemporalLayers::FrameConfig tl_configs[kMaxSimulcastStreams];
|
||||
for (size_t i = 0; i < encoders_.size(); ++i) {
|
||||
tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp());
|
||||
|
||||
if (tl_configs[i].drop_frame) {
|
||||
// Drop this frame.
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
flags[i] = EncodeFlags(tl_configs[i]);
|
||||
}
|
||||
bool send_key_frame = false;
|
||||
for (size_t i = 0; i < key_frame_request_.size() && i < send_stream_.size();
|
||||
++i) {
|
||||
@ -782,6 +776,18 @@ int VP8EncoderImpl::Encode(const VideoFrame& frame,
|
||||
}
|
||||
}
|
||||
}
|
||||
vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
|
||||
TemporalLayers::FrameConfig tl_configs[kMaxSimulcastStreams];
|
||||
for (size_t i = 0; i < encoders_.size(); ++i) {
|
||||
tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp());
|
||||
RTC_DCHECK(temporal_layers_checkers_[i]->CheckTemporalConfig(
|
||||
send_key_frame, tl_configs[i]));
|
||||
if (tl_configs[i].drop_frame) {
|
||||
// Drop this frame.
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
flags[i] = EncodeFlags(tl_configs[i]);
|
||||
}
|
||||
if (send_key_frame) {
|
||||
// Adapt the size of the key frame when in screenshare with 1 temporal
|
||||
// layer.
|
||||
@ -833,13 +839,13 @@ int VP8EncoderImpl::Encode(const VideoFrame& frame,
|
||||
// the frame must be reencoded with the same parameters again because
|
||||
// target bitrate is exceeded and encoder state has been reset.
|
||||
while (num_tries == 0 ||
|
||||
(num_tries == 1 &&
|
||||
(num_tries == 1 &&
|
||||
error == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT)) {
|
||||
++num_tries;
|
||||
// Note we must pass 0 for |flags| field in encode call below since they are
|
||||
// set above in |vpx_codec_control| function for each encoder/spatial layer.
|
||||
error = vpx_codec_encode(&encoders_[0], &raw_images_[0], timestamp_,
|
||||
duration, 0, VPX_DL_REALTIME);
|
||||
duration, 0, VPX_DL_REALTIME);
|
||||
// Reset specific intra frame thresholds, following the key frame.
|
||||
if (send_key_frame) {
|
||||
vpx_codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
||||
@ -1233,8 +1239,8 @@ int VP8DecoderImpl::ReturnFrame(const vpx_image_t* img,
|
||||
img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
|
||||
buffer->MutableDataY(), buffer->StrideY(),
|
||||
buffer->MutableDataU(), buffer->StrideU(),
|
||||
buffer->MutableDataV(), buffer->StrideV(),
|
||||
img->d_w, img->d_h);
|
||||
buffer->MutableDataV(), buffer->StrideV(), img->d_w,
|
||||
img->d_h);
|
||||
|
||||
VideoFrame decoded_image(buffer, timestamp, 0, kVideoRotation_0);
|
||||
decoded_image.set_ntp_time_ms(ntp_time_ms);
|
||||
|
||||
@ -18,10 +18,10 @@
|
||||
|
||||
// NOTE: This include order must remain to avoid compile errors, even though
|
||||
// it breaks the style guide.
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#include "api/video/video_frame.h"
|
||||
#include "common_video/include/i420_buffer_pool.h"
|
||||
@ -105,6 +105,7 @@ class VP8EncoderImpl : public VP8Encoder {
|
||||
int number_of_cores_;
|
||||
uint32_t rc_max_intra_target_;
|
||||
std::vector<std::unique_ptr<TemporalLayers>> temporal_layers_;
|
||||
std::vector<std::unique_ptr<TemporalLayersChecker>> temporal_layers_checkers_;
|
||||
std::vector<uint16_t> picture_id_;
|
||||
std::vector<uint8_t> tl0_pic_idx_;
|
||||
std::vector<bool> key_frame_request_;
|
||||
|
||||
Reference in New Issue
Block a user