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

Unified Diff: webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc

Issue 2747123005: Simplify default temporal layers. (Closed)
Patch Set: update comments so that it doesn't look like updates are optional Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc
diff --git a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc
index 4f04743fc5d0359d62ff02117d9fe620e26fc309..2af8f41cd71d5798a14c83e4674f3974cea00f39 100644
--- a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc
@@ -9,7 +9,6 @@
#include "webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h"
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -26,26 +25,175 @@
namespace webrtc {
+TemporalReferences::TemporalReferences(TemporalBufferUsage last,
+ TemporalBufferUsage golden,
+ TemporalBufferUsage arf)
+ : TemporalReferences(last, golden, arf, false, false) {}
+
+TemporalReferences::TemporalReferences(TemporalBufferUsage last,
+ TemporalBufferUsage golden,
+ TemporalBufferUsage arf,
+ int extra_flags)
+ : TemporalReferences(last,
+ golden,
+ arf,
+ (extra_flags & kLayerSync) != 0,
+ (extra_flags & kFreezeEntropy) != 0) {}
+
+TemporalReferences::TemporalReferences(TemporalBufferUsage last,
+ TemporalBufferUsage golden,
+ TemporalBufferUsage arf,
+ bool layer_sync,
+ bool freeze_entropy)
+ : reference_last((last & kReference) != 0),
+ update_last((last & kUpdate) != 0),
+ reference_golden((golden & kReference) != 0),
+ update_golden((golden & kUpdate) != 0),
+ reference_arf((arf & kReference) != 0),
+ update_arf((arf & kUpdate) != 0),
+ layer_sync(layer_sync),
+ freeze_entropy(freeze_entropy) {}
+
+namespace {
+
+std::vector<unsigned int> GetTemporalIds(size_t num_layers) {
+ switch (num_layers) {
+ case 1:
+ // Temporal layer structure (single layer):
+ // 0 0 0 0 ...
+ return {0};
+ case 2:
+ // Temporal layer structure:
+ // 1 1 ...
+ // 0 0 ...
+ return {0, 1};
+ case 3:
+ // Temporal layer structure:
+ // 2 2 2 2 ...
+ // 1 1 ...
+ // 0 0 ...
+ return {0, 2, 1, 2};
+ case 4:
+ // Temporal layer structure:
+ // 3 3 3 3 3 3 3 3 ...
+ // 2 2 2 2 ...
+ // 1 1 ...
+ // 0 0 ...
+ return {0, 3, 2, 3, 1, 3, 2, 3};
+ default:
+ RTC_NOTREACHED();
+ break;
+ }
+ RTC_NOTREACHED();
+ return {0};
+}
+
+std::vector<TemporalReferences> GetTemporalPattern(
+ size_t num_layers) {
+ // For indexing in the patterns described below (which temporal layers they
+ // belong to), see the diagram above.
+ // Layer sync is done similarly for all patterns (except single stream) and
+ // happens every 8 frames:
+ // TL1 layer syncs by periodically by only referencing TL0 ('last'), but still
+ // updating 'golden', so it can be used as a reference by future TL1 frames.
+ // TL2 layer syncs just before TL1 by only depending on TL0 (and not depending
+ // on TL1's buffer before TL1 has layer synced).
+ // TODO(pbos): Consider cyclically updating 'arf' (and 'golden' for 1TL) for
+ // the base layer in 1-3TL instead of 'last' periodically on long intervals,
+ // so that if scene changes occur (user walks between rooms or rotates webcam)
+ // the 'arf' (or 'golden' respectively) is not stuck on a no-longer relevant
+ // keyframe.
+ switch (num_layers) {
+ case 1:
+ // All frames reference all buffers and the 'last' buffer is updated.
+ return {TemporalReferences(kReferenceAndUpdate, kReference, kReference)};
+ case 2:
+ // All layers can reference but not update the 'alt' buffer, this means
+ // that the 'alt' buffer reference is effectively the last keyframe.
+ // TL0 also references and updates the 'last' buffer.
+ // TL1 also references 'last' and references and updates 'golden'.
+ return {TemporalReferences(kReferenceAndUpdate, kUpdate, kReference),
+ TemporalReferences(kReference, kUpdate, kReference, kLayerSync),
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference),
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference),
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference),
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference),
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference),
+ TemporalReferences(kReference, kReference, kReference,
+ kFreezeEntropy)};
+ case 3:
+ // All layers can reference but not update the 'alt' buffer, this means
+ // that the 'alt' buffer reference is effectively the last keyframe.
+ // TL0 also references and updates the 'last' buffer.
+ // TL1 also references 'last' and references and updates 'golden'.
+ // TL2 references both 'last' and 'golden' but updates no buffer.
+ return {TemporalReferences(kReferenceAndUpdate, kUpdate, kReference),
+ TemporalReferences(kReference, kNone, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kUpdate, kReference, kLayerSync),
+ TemporalReferences(kReference, kReference, kReference,
+ kFreezeEntropy),
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference),
+ TemporalReferences(kReference, kReference, kReference,
+ kFreezeEntropy),
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference),
+ TemporalReferences(kReference, kReference, kReference,
+ kFreezeEntropy)};
+ case 4:
+ // TL0 references and updates only the 'last' buffer.
+ // TL1 references 'last' and updates and references 'golden'.
+ // TL2 references 'last' and 'golden', and references and updates 'arf'.
+ // TL3 references all buffers but update none of them.
+ return {TemporalReferences(kReferenceAndUpdate, kNone, kNone),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kNone, kUpdate, kLayerSync),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kUpdate, kNone, kLayerSync),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReferenceAndUpdate, kNone, kNone),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kReferenceAndUpdate, kNone),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy),
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate),
+ TemporalReferences(kReference, kReference, kReference,
+ kLayerSync | kFreezeEntropy)};
+ default:
+ RTC_NOTREACHED();
+ break;
+ }
+ RTC_NOTREACHED();
+ return {TemporalReferences(kNone, kNone, kNone)};
+}
+
+} // namespace
+
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers,
uint8_t initial_tl0_pic_idx)
- : number_of_temporal_layers_(number_of_temporal_layers),
- temporal_ids_length_(0),
- temporal_pattern_length_(0),
+ : num_layers_(std::max(1, number_of_temporal_layers)),
+ temporal_ids_(GetTemporalIds(num_layers_)),
+ temporal_pattern_(GetTemporalPattern(num_layers_)),
tl0_pic_idx_(initial_tl0_pic_idx),
pattern_idx_(255),
timestamp_(0),
last_base_layer_sync_(false) {
RTC_CHECK_GE(kMaxTemporalStreams, number_of_temporal_layers);
RTC_CHECK_GE(number_of_temporal_layers, 0);
- memset(temporal_ids_, 0, sizeof(temporal_ids_));
- memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
+ RTC_CHECK_LE(number_of_temporal_layers, 4);
}
int DefaultTemporalLayers::CurrentLayerId() const {
- assert(temporal_ids_length_ > 0);
- int index = pattern_idx_ % temporal_ids_length_;
- assert(index >= 0);
- return temporal_ids_[index];
+ return temporal_ids_[pattern_idx_ % temporal_ids_.size()];
}
std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated(
@@ -53,17 +201,16 @@ std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated(
int max_bitrate_kbps,
int framerate) {
std::vector<uint32_t> bitrates;
- const int num_layers = std::max(1, number_of_temporal_layers_);
- for (int i = 0; i < num_layers; ++i) {
+ for (size_t i = 0; i < num_layers_; ++i) {
float layer_bitrate =
- bitrate_kbps * kVp8LayerRateAlloction[num_layers - 1][i];
+ bitrate_kbps * kVp8LayerRateAlloction[num_layers_ - 1][i];
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
- for (int i = 0; i < num_layers; ++i) {
+ for (size_t i = 0; i < num_layers_; ++i) {
uint32_t layer_bitrate = bitrates[i];
RTC_DCHECK_LE(sum, bitrates[i]);
bitrates[i] -= sum;
@@ -83,197 +230,46 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
if (!new_bitrates_kbps_)
return false;
- switch (number_of_temporal_layers_) {
- case 0:
- FALLTHROUGH();
- case 1:
- temporal_ids_length_ = 1;
- temporal_ids_[0] = 0;
- cfg->ts_number_layers = number_of_temporal_layers_;
- cfg->ts_periodicity = temporal_ids_length_;
- cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
- cfg->ts_rate_decimator[0] = 1;
- memcpy(cfg->ts_layer_id, temporal_ids_,
- sizeof(unsigned int) * temporal_ids_length_);
- temporal_pattern_length_ = 1;
- temporal_pattern_[0] = kTemporalUpdateLastRefAll;
- break;
- case 2:
- temporal_ids_length_ = 2;
- temporal_ids_[0] = 0;
- temporal_ids_[1] = 1;
- cfg->ts_number_layers = number_of_temporal_layers_;
- cfg->ts_periodicity = temporal_ids_length_;
- // Split stream 60% 40%.
- // Bitrate API for VP8 is the agregated bitrate for all lower layers.
- cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
- cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
- cfg->ts_rate_decimator[0] = 2;
- cfg->ts_rate_decimator[1] = 1;
- memcpy(cfg->ts_layer_id, temporal_ids_,
- sizeof(unsigned int) * temporal_ids_length_);
- temporal_pattern_length_ = 8;
- temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef;
- temporal_pattern_[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
- temporal_pattern_[2] = kTemporalUpdateLastRefAltRef;
- temporal_pattern_[3] = kTemporalUpdateGoldenRefAltRef;
- temporal_pattern_[4] = kTemporalUpdateLastRefAltRef;
- temporal_pattern_[5] = kTemporalUpdateGoldenRefAltRef;
- temporal_pattern_[6] = kTemporalUpdateLastRefAltRef;
- temporal_pattern_[7] = kTemporalUpdateNone;
- break;
- case 3:
- temporal_ids_length_ = 4;
- temporal_ids_[0] = 0;
- temporal_ids_[1] = 2;
- temporal_ids_[2] = 1;
- temporal_ids_[3] = 2;
- cfg->ts_number_layers = number_of_temporal_layers_;
- cfg->ts_periodicity = temporal_ids_length_;
- // Split stream 40% 20% 40%.
- // Bitrate API for VP8 is the agregated bitrate for all lower layers.
- cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
- cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
- cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
- cfg->ts_rate_decimator[0] = 4;
- cfg->ts_rate_decimator[1] = 2;
- cfg->ts_rate_decimator[2] = 1;
- memcpy(cfg->ts_layer_id, temporal_ids_,
- sizeof(unsigned int) * temporal_ids_length_);
- temporal_pattern_length_ = 8;
- temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef;
- temporal_pattern_[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
- temporal_pattern_[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
- temporal_pattern_[3] = kTemporalUpdateNone;
- temporal_pattern_[4] = kTemporalUpdateLastRefAltRef;
- temporal_pattern_[5] = kTemporalUpdateNone;
- temporal_pattern_[6] = kTemporalUpdateGoldenRefAltRef;
- temporal_pattern_[7] = kTemporalUpdateNone;
- break;
- case 4:
- temporal_ids_length_ = 8;
- temporal_ids_[0] = 0;
- temporal_ids_[1] = 3;
- temporal_ids_[2] = 2;
- temporal_ids_[3] = 3;
- temporal_ids_[4] = 1;
- temporal_ids_[5] = 3;
- temporal_ids_[6] = 2;
- temporal_ids_[7] = 3;
- // Split stream 25% 15% 20% 40%.
- // Bitrate API for VP8 is the agregated bitrate for all lower layers.
- cfg->ts_number_layers = 4;
- cfg->ts_periodicity = temporal_ids_length_;
- cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
- cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
- cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
- cfg->ts_target_bitrate[3] = (*new_bitrates_kbps_)[3];
- cfg->ts_rate_decimator[0] = 8;
- cfg->ts_rate_decimator[1] = 4;
- cfg->ts_rate_decimator[2] = 2;
- cfg->ts_rate_decimator[3] = 1;
- memcpy(cfg->ts_layer_id, temporal_ids_,
- sizeof(unsigned int) * temporal_ids_length_);
- temporal_pattern_length_ = 16;
- temporal_pattern_[0] = kTemporalUpdateLast;
- temporal_pattern_[1] = kTemporalUpdateNone;
- temporal_pattern_[2] = kTemporalUpdateAltrefWithoutDependency;
- temporal_pattern_[3] = kTemporalUpdateNone;
- temporal_pattern_[4] = kTemporalUpdateGoldenWithoutDependency;
- temporal_pattern_[5] = kTemporalUpdateNone;
- temporal_pattern_[6] = kTemporalUpdateAltref;
- temporal_pattern_[7] = kTemporalUpdateNone;
- temporal_pattern_[8] = kTemporalUpdateLast;
- temporal_pattern_[9] = kTemporalUpdateNone;
- temporal_pattern_[10] = kTemporalUpdateAltref;
- temporal_pattern_[11] = kTemporalUpdateNone;
- temporal_pattern_[12] = kTemporalUpdateGolden;
- temporal_pattern_[13] = kTemporalUpdateNone;
- temporal_pattern_[14] = kTemporalUpdateAltref;
- temporal_pattern_[15] = kTemporalUpdateNone;
- break;
- default:
- RTC_NOTREACHED();
- return false;
+ for (size_t i = 0; i < num_layers_; ++i) {
+ cfg->ts_target_bitrate[i] = (*new_bitrates_kbps_)[i];
+ // ..., 4, 2, 1
+ cfg->ts_rate_decimator[i] = 1 << (num_layers_ - i - 1);
}
+ cfg->ts_number_layers = num_layers_;
+ cfg->ts_periodicity = temporal_ids_.size();
+ memcpy(cfg->ts_layer_id, &temporal_ids_[0],
+ sizeof(unsigned int) * temporal_ids_.size());
+
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
return true;
}
int DefaultTemporalLayers::EncodeFlags(uint32_t timestamp) {
- assert(number_of_temporal_layers_ > 0);
- assert(kMaxTemporalPattern >= temporal_pattern_length_);
- assert(0 < temporal_pattern_length_);
+ RTC_DCHECK_GT(num_layers_, 0);
+ RTC_DCHECK_LT(0, temporal_pattern_.size());
int flags = 0;
- int patternIdx = ++pattern_idx_ % temporal_pattern_length_;
- assert(kMaxTemporalPattern >= patternIdx);
- switch (temporal_pattern_[patternIdx]) {
- case kTemporalUpdateLast:
- flags |= VP8_EFLAG_NO_UPD_GF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_REF_GF;
- flags |= VP8_EFLAG_NO_REF_ARF;
- break;
- case kTemporalUpdateGoldenWithoutDependency:
- flags |= VP8_EFLAG_NO_REF_GF;
- // Deliberately no break here.
- FALLTHROUGH();
- case kTemporalUpdateGolden:
- flags |= VP8_EFLAG_NO_REF_ARF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- break;
- case kTemporalUpdateAltrefWithoutDependency:
- flags |= VP8_EFLAG_NO_REF_ARF;
- flags |= VP8_EFLAG_NO_REF_GF;
- // Deliberately no break here.
- FALLTHROUGH();
- case kTemporalUpdateAltref:
- flags |= VP8_EFLAG_NO_UPD_GF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- break;
- case kTemporalUpdateNoneNoRefAltref:
- flags |= VP8_EFLAG_NO_REF_ARF;
- // Deliberately no break here.
- FALLTHROUGH();
- case kTemporalUpdateNone:
- flags |= VP8_EFLAG_NO_UPD_GF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- flags |= VP8_EFLAG_NO_UPD_ENTROPY;
- break;
- case kTemporalUpdateNoneNoRefGoldenRefAltRef:
- flags |= VP8_EFLAG_NO_REF_GF;
- flags |= VP8_EFLAG_NO_UPD_GF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- flags |= VP8_EFLAG_NO_UPD_ENTROPY;
- break;
- case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
- flags |= VP8_EFLAG_NO_REF_GF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- break;
- case kTemporalUpdateLastRefAltRef:
- flags |= VP8_EFLAG_NO_UPD_GF;
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_REF_GF;
- break;
- case kTemporalUpdateGoldenRefAltRef:
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_LAST;
- break;
- case kTemporalUpdateLastAndGoldenRefAltRef:
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_REF_GF;
- break;
- case kTemporalUpdateLastRefAll:
- flags |= VP8_EFLAG_NO_UPD_ARF;
- flags |= VP8_EFLAG_NO_UPD_GF;
- break;
- }
+ // TODO(pbos): Move pattern-update out of EncodeFlags. It's not obvious that
+ // EncodeFlags() is non-const.
+ const TemporalReferences& references =
+ temporal_pattern_[++pattern_idx_ % temporal_pattern_.size()];
+
+ if (!references.reference_last)
+ flags |= VP8_EFLAG_NO_REF_LAST;
+ if (!references.update_last)
+ flags |= VP8_EFLAG_NO_UPD_LAST;
+ if (!references.reference_golden)
+ flags |= VP8_EFLAG_NO_REF_GF;
+ if (!references.update_golden)
+ flags |= VP8_EFLAG_NO_UPD_GF;
+ if (!references.reference_arf)
+ flags |= VP8_EFLAG_NO_REF_ARF;
+ if (!references.update_arf)
+ flags |= VP8_EFLAG_NO_UPD_ARF;
+ if (references.freeze_entropy)
+ flags |= VP8_EFLAG_NO_UPD_ENTROPY;
+
return flags;
}
@@ -281,10 +277,9 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) {
- assert(number_of_temporal_layers_ > 0);
- assert(0 < temporal_ids_length_);
+ RTC_DCHECK_GT(num_layers_, 0);
- if (number_of_temporal_layers_ == 1) {
+ if (num_layers_ == 1) {
vp8_info->temporalIdx = kNoTemporalIdx;
vp8_info->layerSync = false;
vp8_info->tl0PicIdx = kNoTl0PicIdx;
@@ -295,19 +290,9 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
} else {
vp8_info->temporalIdx = CurrentLayerId();
TemporalReferences temporal_reference =
- temporal_pattern_[pattern_idx_ % temporal_pattern_length_];
-
- if (temporal_reference == kTemporalUpdateAltrefWithoutDependency ||
- temporal_reference == kTemporalUpdateGoldenWithoutDependency ||
- temporal_reference ==
- kTemporalUpdateGoldenWithoutDependencyRefAltRef ||
- temporal_reference == kTemporalUpdateNoneNoRefGoldenRefAltRef ||
- (temporal_reference == kTemporalUpdateNone &&
- number_of_temporal_layers_ == 4)) {
- vp8_info->layerSync = true;
- } else {
- vp8_info->layerSync = false;
- }
+ temporal_pattern_[pattern_idx_ % temporal_pattern_.size()];
+
+ vp8_info->layerSync = temporal_reference.layer_sync;
}
if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) {
// Regardless of pattern the frame after a base layer sync will always
« no previous file with comments | « webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698