Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
4
TMessagesProj/jni/voip/webrtc/common_video/OWNERS
Normal file
4
TMessagesProj/jni/voip/webrtc/common_video/OWNERS
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
magjed@webrtc.org
|
||||
marpan@webrtc.org
|
||||
sprang@webrtc.org
|
||||
stefan@webrtc.org
|
||||
159
TMessagesProj/jni/voip/webrtc/common_video/bitrate_adjuster.cc
Normal file
159
TMessagesProj/jni/voip/webrtc/common_video/bitrate_adjuster.cc
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright 2016 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 "common_video/include/bitrate_adjuster.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Update bitrate at most once every second.
|
||||
const uint32_t BitrateAdjuster::kBitrateUpdateIntervalMs = 1000;
|
||||
|
||||
// Update bitrate at most once every 30 frames.
|
||||
const uint32_t BitrateAdjuster::kBitrateUpdateFrameInterval = 30;
|
||||
|
||||
// 10 percent of original.
|
||||
const float BitrateAdjuster::kBitrateTolerancePct = .1f;
|
||||
|
||||
const float BitrateAdjuster::kBytesPerMsToBitsPerSecond = 8 * 1000;
|
||||
|
||||
BitrateAdjuster::BitrateAdjuster(float min_adjusted_bitrate_pct,
|
||||
float max_adjusted_bitrate_pct)
|
||||
: min_adjusted_bitrate_pct_(min_adjusted_bitrate_pct),
|
||||
max_adjusted_bitrate_pct_(max_adjusted_bitrate_pct),
|
||||
bitrate_tracker_(1.5 * kBitrateUpdateIntervalMs,
|
||||
kBytesPerMsToBitsPerSecond) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void BitrateAdjuster::SetTargetBitrateBps(uint32_t bitrate_bps) {
|
||||
MutexLock lock(&mutex_);
|
||||
// If the change in target bitrate is large, update the adjusted bitrate
|
||||
// immediately since it's likely we have gained or lost a sizeable amount of
|
||||
// bandwidth and we'll want to respond quickly.
|
||||
// If the change in target bitrate fits within the existing tolerance of
|
||||
// encoder output, wait for the next adjustment time to preserve
|
||||
// existing penalties and not forcibly reset the adjusted bitrate to target.
|
||||
// However, if we received many small deltas within an update time
|
||||
// window and one of them exceeds the tolerance when compared to the last
|
||||
// target we updated against, treat it as a large change in target bitrate.
|
||||
if (!IsWithinTolerance(bitrate_bps, target_bitrate_bps_) ||
|
||||
!IsWithinTolerance(bitrate_bps, last_adjusted_target_bitrate_bps_)) {
|
||||
adjusted_bitrate_bps_ = bitrate_bps;
|
||||
last_adjusted_target_bitrate_bps_ = bitrate_bps;
|
||||
}
|
||||
target_bitrate_bps_ = bitrate_bps;
|
||||
}
|
||||
|
||||
uint32_t BitrateAdjuster::GetTargetBitrateBps() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return target_bitrate_bps_;
|
||||
}
|
||||
|
||||
uint32_t BitrateAdjuster::GetAdjustedBitrateBps() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return adjusted_bitrate_bps_;
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> BitrateAdjuster::GetEstimatedBitrateBps() {
|
||||
MutexLock lock(&mutex_);
|
||||
return bitrate_tracker_.Rate(rtc::TimeMillis());
|
||||
}
|
||||
|
||||
void BitrateAdjuster::Update(size_t frame_size) {
|
||||
MutexLock lock(&mutex_);
|
||||
uint32_t current_time_ms = rtc::TimeMillis();
|
||||
bitrate_tracker_.Update(frame_size, current_time_ms);
|
||||
UpdateBitrate(current_time_ms);
|
||||
}
|
||||
|
||||
bool BitrateAdjuster::IsWithinTolerance(uint32_t bitrate_bps,
|
||||
uint32_t target_bitrate_bps) {
|
||||
if (target_bitrate_bps == 0) {
|
||||
return false;
|
||||
}
|
||||
float delta = std::abs(static_cast<float>(bitrate_bps) -
|
||||
static_cast<float>(target_bitrate_bps));
|
||||
float delta_pct = delta / target_bitrate_bps;
|
||||
return delta_pct < kBitrateTolerancePct;
|
||||
}
|
||||
|
||||
uint32_t BitrateAdjuster::GetMinAdjustedBitrateBps() const {
|
||||
return min_adjusted_bitrate_pct_ * target_bitrate_bps_;
|
||||
}
|
||||
|
||||
uint32_t BitrateAdjuster::GetMaxAdjustedBitrateBps() const {
|
||||
return max_adjusted_bitrate_pct_ * target_bitrate_bps_;
|
||||
}
|
||||
|
||||
// Only safe to call this after Update calls have stopped
|
||||
void BitrateAdjuster::Reset() {
|
||||
MutexLock lock(&mutex_);
|
||||
target_bitrate_bps_ = 0;
|
||||
adjusted_bitrate_bps_ = 0;
|
||||
last_adjusted_target_bitrate_bps_ = 0;
|
||||
last_bitrate_update_time_ms_ = 0;
|
||||
frames_since_last_update_ = 0;
|
||||
bitrate_tracker_.Reset();
|
||||
}
|
||||
|
||||
void BitrateAdjuster::UpdateBitrate(uint32_t current_time_ms) {
|
||||
uint32_t time_since_last_update_ms =
|
||||
current_time_ms - last_bitrate_update_time_ms_;
|
||||
// Don't attempt to update bitrate unless enough time and frames have passed.
|
||||
++frames_since_last_update_;
|
||||
if (time_since_last_update_ms < kBitrateUpdateIntervalMs ||
|
||||
frames_since_last_update_ < kBitrateUpdateFrameInterval) {
|
||||
return;
|
||||
}
|
||||
float target_bitrate_bps = target_bitrate_bps_;
|
||||
float estimated_bitrate_bps =
|
||||
bitrate_tracker_.Rate(current_time_ms).value_or(target_bitrate_bps);
|
||||
float error = target_bitrate_bps - estimated_bitrate_bps;
|
||||
|
||||
// Adjust if we've overshot by any amount or if we've undershot too much.
|
||||
if (estimated_bitrate_bps > target_bitrate_bps ||
|
||||
error > kBitrateTolerancePct * target_bitrate_bps) {
|
||||
// Adjust the bitrate by a fraction of the error.
|
||||
float adjustment = .5 * error;
|
||||
float adjusted_bitrate_bps = target_bitrate_bps + adjustment;
|
||||
|
||||
// Clamp the adjustment.
|
||||
float min_bitrate_bps = GetMinAdjustedBitrateBps();
|
||||
float max_bitrate_bps = GetMaxAdjustedBitrateBps();
|
||||
adjusted_bitrate_bps = std::max(adjusted_bitrate_bps, min_bitrate_bps);
|
||||
adjusted_bitrate_bps = std::min(adjusted_bitrate_bps, max_bitrate_bps);
|
||||
|
||||
// Set the adjustment if it's not already set.
|
||||
float last_adjusted_bitrate_bps = adjusted_bitrate_bps_;
|
||||
if (adjusted_bitrate_bps != last_adjusted_bitrate_bps) {
|
||||
RTC_LOG(LS_VERBOSE) << "Adjusting encoder bitrate:"
|
||||
"\n target_bitrate:"
|
||||
<< static_cast<uint32_t>(target_bitrate_bps)
|
||||
<< "\n estimated_bitrate:"
|
||||
<< static_cast<uint32_t>(estimated_bitrate_bps)
|
||||
<< "\n last_adjusted_bitrate:"
|
||||
<< static_cast<uint32_t>(last_adjusted_bitrate_bps)
|
||||
<< "\n adjusted_bitrate:"
|
||||
<< static_cast<uint32_t>(adjusted_bitrate_bps);
|
||||
adjusted_bitrate_bps_ = adjusted_bitrate_bps;
|
||||
}
|
||||
}
|
||||
last_bitrate_update_time_ms_ = current_time_ms;
|
||||
frames_since_last_update_ = 0;
|
||||
last_adjusted_target_bitrate_bps_ = target_bitrate_bps_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
34
TMessagesProj/jni/voip/webrtc/common_video/frame_counts.h
Normal file
34
TMessagesProj/jni/voip/webrtc/common_video/frame_counts.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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 COMMON_VIDEO_FRAME_COUNTS_H_
|
||||
#define COMMON_VIDEO_FRAME_COUNTS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct FrameCounts {
|
||||
FrameCounts() : key_frames(0), delta_frames(0) {}
|
||||
int key_frames;
|
||||
int delta_frames;
|
||||
};
|
||||
|
||||
// Callback, used to notify an observer whenever frame counts have been updated.
|
||||
class FrameCountObserver {
|
||||
public:
|
||||
virtual ~FrameCountObserver() {}
|
||||
virtual void FrameCountUpdated(const FrameCounts& frame_counts,
|
||||
uint32_t ssrc) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_FRAME_COUNTS_H_
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 "common_video/frame_rate_estimator.h"
|
||||
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FrameRateEstimator::FrameRateEstimator(TimeDelta averaging_window)
|
||||
: averaging_window_(averaging_window) {}
|
||||
|
||||
void FrameRateEstimator::OnFrame(Timestamp time) {
|
||||
CullOld(time);
|
||||
frame_times_.push_back(time);
|
||||
}
|
||||
|
||||
absl::optional<double> FrameRateEstimator::GetAverageFps() const {
|
||||
if (frame_times_.size() < 2) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
TimeDelta time_span = frame_times_.back() - frame_times_.front();
|
||||
if (time_span < TimeDelta::Micros(1)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
TimeDelta avg_frame_interval = time_span / (frame_times_.size() - 1);
|
||||
|
||||
return static_cast<double>(rtc::kNumMicrosecsPerSec) /
|
||||
avg_frame_interval.us();
|
||||
}
|
||||
|
||||
absl::optional<double> FrameRateEstimator::GetAverageFps(Timestamp now) {
|
||||
CullOld(now);
|
||||
return GetAverageFps();
|
||||
}
|
||||
|
||||
void FrameRateEstimator::Reset() {
|
||||
frame_times_.clear();
|
||||
}
|
||||
|
||||
void FrameRateEstimator::CullOld(Timestamp now) {
|
||||
while (!frame_times_.empty() &&
|
||||
frame_times_.front() + averaging_window_ < now) {
|
||||
frame_times_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 COMMON_VIDEO_FRAME_RATE_ESTIMATOR_H_
|
||||
#define COMMON_VIDEO_FRAME_RATE_ESTIMATOR_H_
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Class used to estimate a frame-rate using inter-frame intervals.
|
||||
// Some notes on usage:
|
||||
// This class is intended to accurately estimate the frame rate during a
|
||||
// continuous stream. Unlike a traditional rate estimator that looks at number
|
||||
// of data points within a time window, if the input stops this implementation
|
||||
// will not smoothly fall down towards 0. This is done so that the estimated
|
||||
// fps is not affected by edge conditions like if we sample just before or just
|
||||
// after the next frame.
|
||||
// To avoid problems if a stream is stopped and restarted (where estimated fps
|
||||
// could look too low), users of this class should explicitly call Reset() on
|
||||
// restart.
|
||||
// Also note that this class is not thread safe, it's up to the user to guard
|
||||
// against concurrent access.
|
||||
class FrameRateEstimator {
|
||||
public:
|
||||
explicit FrameRateEstimator(TimeDelta averaging_window);
|
||||
|
||||
// Insert a frame, potentially culling old frames that falls outside the
|
||||
// averaging window.
|
||||
void OnFrame(Timestamp time);
|
||||
|
||||
// Get the current average FPS, based on the frames currently in the window.
|
||||
absl::optional<double> GetAverageFps() const;
|
||||
|
||||
// Move the window so it ends at `now`, and return the new fps estimate.
|
||||
absl::optional<double> GetAverageFps(Timestamp now);
|
||||
|
||||
// Completely clear the averaging window.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void CullOld(Timestamp now);
|
||||
const TimeDelta averaging_window_;
|
||||
std::deque<Timestamp> frame_times_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_FRAME_RATE_ESTIMATOR_H_
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2021 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 "common_video/framerate_controller.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
constexpr double kMinFramerate = 0.5;
|
||||
} // namespace
|
||||
|
||||
FramerateController::FramerateController()
|
||||
: FramerateController(std::numeric_limits<double>::max()) {}
|
||||
|
||||
FramerateController::FramerateController(double max_framerate)
|
||||
: max_framerate_(max_framerate) {}
|
||||
|
||||
FramerateController::~FramerateController() {}
|
||||
|
||||
void FramerateController::SetMaxFramerate(double max_framerate) {
|
||||
max_framerate_ = max_framerate;
|
||||
}
|
||||
|
||||
double FramerateController::GetMaxFramerate() const {
|
||||
return max_framerate_;
|
||||
}
|
||||
|
||||
bool FramerateController::ShouldDropFrame(int64_t in_timestamp_ns) {
|
||||
if (max_framerate_ < kMinFramerate)
|
||||
return true;
|
||||
|
||||
// If `max_framerate_` is not set (i.e. maxdouble), `frame_interval_ns` is
|
||||
// rounded to 0.
|
||||
int64_t frame_interval_ns = rtc::kNumNanosecsPerSec / max_framerate_;
|
||||
if (frame_interval_ns <= 0) {
|
||||
// Frame rate throttling not enabled.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (next_frame_timestamp_ns_) {
|
||||
// Time until next frame should be outputted.
|
||||
const int64_t time_until_next_frame_ns =
|
||||
(*next_frame_timestamp_ns_ - in_timestamp_ns);
|
||||
// Continue if timestamp is within expected range.
|
||||
if (std::abs(time_until_next_frame_ns) < 2 * frame_interval_ns) {
|
||||
// Drop if a frame shouldn't be outputted yet.
|
||||
if (time_until_next_frame_ns > 0)
|
||||
return true;
|
||||
// Time to output new frame.
|
||||
*next_frame_timestamp_ns_ += frame_interval_ns;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// First timestamp received or timestamp is way outside expected range, so
|
||||
// reset. Set first timestamp target to just half the interval to prefer
|
||||
// keeping frames in case of jitter.
|
||||
next_frame_timestamp_ns_ = in_timestamp_ns + frame_interval_ns / 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
void FramerateController::Reset() {
|
||||
max_framerate_ = std::numeric_limits<double>::max();
|
||||
next_frame_timestamp_ns_ = absl::nullopt;
|
||||
}
|
||||
|
||||
void FramerateController::KeepFrame(int64_t in_timestamp_ns) {
|
||||
if (ShouldDropFrame(in_timestamp_ns)) {
|
||||
if (max_framerate_ < kMinFramerate)
|
||||
return;
|
||||
|
||||
int64_t frame_interval_ns = rtc::kNumNanosecsPerSec / max_framerate_;
|
||||
if (next_frame_timestamp_ns_)
|
||||
*next_frame_timestamp_ns_ += frame_interval_ns;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2021 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 COMMON_VIDEO_FRAMERATE_CONTROLLER_H_
|
||||
#define COMMON_VIDEO_FRAMERATE_CONTROLLER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Determines which frames that should be dropped based on input framerate and
|
||||
// requested framerate.
|
||||
class FramerateController {
|
||||
public:
|
||||
FramerateController();
|
||||
explicit FramerateController(double max_framerate);
|
||||
~FramerateController();
|
||||
|
||||
// Sets max framerate (default is maxdouble).
|
||||
void SetMaxFramerate(double max_framerate);
|
||||
double GetMaxFramerate() const;
|
||||
|
||||
// Returns true if the frame should be dropped, false otherwise.
|
||||
bool ShouldDropFrame(int64_t in_timestamp_ns);
|
||||
|
||||
void Reset();
|
||||
|
||||
void KeepFrame(int64_t in_timestamp_ns);
|
||||
|
||||
private:
|
||||
double max_framerate_;
|
||||
absl::optional<int64_t> next_frame_timestamp_ns_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_FRAMERATE_CONTROLLER_H_
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
philipel@webrtc.org
|
||||
danilchap@webrtc.org
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 "common_video/generic_frame_descriptor/generic_frame_info.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
GenericFrameInfo::GenericFrameInfo() = default;
|
||||
GenericFrameInfo::GenericFrameInfo(const GenericFrameInfo&) = default;
|
||||
GenericFrameInfo::~GenericFrameInfo() = default;
|
||||
|
||||
GenericFrameInfo::Builder::Builder() = default;
|
||||
GenericFrameInfo::Builder::~Builder() = default;
|
||||
|
||||
GenericFrameInfo GenericFrameInfo::Builder::Build() const {
|
||||
return info_;
|
||||
}
|
||||
|
||||
GenericFrameInfo::Builder& GenericFrameInfo::Builder::T(int temporal_id) {
|
||||
info_.temporal_id = temporal_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericFrameInfo::Builder& GenericFrameInfo::Builder::S(int spatial_id) {
|
||||
info_.spatial_id = spatial_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericFrameInfo::Builder& GenericFrameInfo::Builder::Dtis(
|
||||
absl::string_view indication_symbols) {
|
||||
info_.decode_target_indications =
|
||||
webrtc_impl::StringToDecodeTargetIndications(indication_symbols);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 COMMON_VIDEO_GENERIC_FRAME_DESCRIPTOR_GENERIC_FRAME_INFO_H_
|
||||
#define COMMON_VIDEO_GENERIC_FRAME_DESCRIPTOR_GENERIC_FRAME_INFO_H_
|
||||
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/transport/rtp/dependency_descriptor.h"
|
||||
#include "api/video/video_codec_constants.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Describes how a certain encoder buffer was used when encoding a frame.
|
||||
struct CodecBufferUsage {
|
||||
constexpr CodecBufferUsage(int id, bool referenced, bool updated)
|
||||
: id(id), referenced(referenced), updated(updated) {}
|
||||
|
||||
int id = 0;
|
||||
bool referenced = false;
|
||||
bool updated = false;
|
||||
};
|
||||
|
||||
struct GenericFrameInfo : public FrameDependencyTemplate {
|
||||
class Builder;
|
||||
|
||||
GenericFrameInfo();
|
||||
GenericFrameInfo(const GenericFrameInfo&);
|
||||
~GenericFrameInfo();
|
||||
|
||||
absl::InlinedVector<CodecBufferUsage, kMaxEncoderBuffers> encoder_buffers;
|
||||
std::vector<bool> part_of_chain;
|
||||
std::bitset<32> active_decode_targets = ~uint32_t{0};
|
||||
};
|
||||
|
||||
class GenericFrameInfo::Builder {
|
||||
public:
|
||||
Builder();
|
||||
~Builder();
|
||||
|
||||
GenericFrameInfo Build() const;
|
||||
Builder& T(int temporal_id);
|
||||
Builder& S(int spatial_id);
|
||||
Builder& Dtis(absl::string_view indication_symbols);
|
||||
|
||||
private:
|
||||
GenericFrameInfo info_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_GENERIC_FRAME_DESCRIPTOR_GENERIC_FRAME_INFO_H_
|
||||
1
TMessagesProj/jni/voip/webrtc/common_video/h264/OWNERS
Normal file
1
TMessagesProj/jni/voip/webrtc/common_video/h264/OWNERS
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssilkin@webrtc.org
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 "common_video/h264/h264_bitstream_parser.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h264/h264_common.h"
|
||||
#include "rtc_base/bitstream_reader.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
constexpr int kMaxAbsQpDeltaValue = 51;
|
||||
constexpr int kMinQpValue = 0;
|
||||
constexpr int kMaxQpValue = 51;
|
||||
|
||||
} // namespace
|
||||
|
||||
H264BitstreamParser::H264BitstreamParser() = default;
|
||||
H264BitstreamParser::~H264BitstreamParser() = default;
|
||||
|
||||
H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu(
|
||||
const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type) {
|
||||
if (!sps_ || !pps_)
|
||||
return kInvalidStream;
|
||||
|
||||
last_slice_qp_delta_ = absl::nullopt;
|
||||
const std::vector<uint8_t> slice_rbsp =
|
||||
H264::ParseRbsp(source, source_length);
|
||||
if (slice_rbsp.size() < H264::kNaluTypeSize)
|
||||
return kInvalidStream;
|
||||
|
||||
BitstreamReader slice_reader(slice_rbsp);
|
||||
slice_reader.ConsumeBits(H264::kNaluTypeSize * 8);
|
||||
|
||||
// Check to see if this is an IDR slice, which has an extra field to parse
|
||||
// out.
|
||||
bool is_idr = (source[0] & 0x0F) == H264::NaluType::kIdr;
|
||||
uint8_t nal_ref_idc = (source[0] & 0x60) >> 5;
|
||||
|
||||
// first_mb_in_slice: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
// slice_type: ue(v)
|
||||
uint32_t slice_type = slice_reader.ReadExponentialGolomb();
|
||||
// slice_type's 5..9 range is used to indicate that all slices of a picture
|
||||
// have the same value of slice_type % 5, we don't care about that, so we map
|
||||
// to the corresponding 0..4 range.
|
||||
slice_type %= 5;
|
||||
// pic_parameter_set_id: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
if (sps_->separate_colour_plane_flag == 1) {
|
||||
// colour_plane_id
|
||||
slice_reader.ConsumeBits(2);
|
||||
}
|
||||
// frame_num: u(v)
|
||||
// Represented by log2_max_frame_num bits.
|
||||
slice_reader.ConsumeBits(sps_->log2_max_frame_num);
|
||||
bool field_pic_flag = false;
|
||||
if (sps_->frame_mbs_only_flag == 0) {
|
||||
// field_pic_flag: u(1)
|
||||
field_pic_flag = slice_reader.Read<bool>();
|
||||
if (field_pic_flag) {
|
||||
// bottom_field_flag: u(1)
|
||||
slice_reader.ConsumeBits(1);
|
||||
}
|
||||
}
|
||||
if (is_idr) {
|
||||
// idr_pic_id: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
// pic_order_cnt_lsb: u(v)
|
||||
// Represented by sps_.log2_max_pic_order_cnt_lsb bits.
|
||||
if (sps_->pic_order_cnt_type == 0) {
|
||||
slice_reader.ConsumeBits(sps_->log2_max_pic_order_cnt_lsb);
|
||||
if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) {
|
||||
// delta_pic_order_cnt_bottom: se(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
}
|
||||
if (sps_->pic_order_cnt_type == 1 &&
|
||||
!sps_->delta_pic_order_always_zero_flag) {
|
||||
// delta_pic_order_cnt[0]: se(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) {
|
||||
// delta_pic_order_cnt[1]: se(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
}
|
||||
if (pps_->redundant_pic_cnt_present_flag) {
|
||||
// redundant_pic_cnt: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
if (slice_type == H264::SliceType::kB) {
|
||||
// direct_spatial_mv_pred_flag: u(1)
|
||||
slice_reader.ConsumeBits(1);
|
||||
}
|
||||
switch (slice_type) {
|
||||
case H264::SliceType::kP:
|
||||
case H264::SliceType::kB:
|
||||
case H264::SliceType::kSp:
|
||||
// num_ref_idx_active_override_flag: u(1)
|
||||
if (slice_reader.Read<bool>()) {
|
||||
// num_ref_idx_l0_active_minus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
if (slice_type == H264::SliceType::kB) {
|
||||
// num_ref_idx_l1_active_minus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!slice_reader.Ok()) {
|
||||
return kInvalidStream;
|
||||
}
|
||||
// assume nal_unit_type != 20 && nal_unit_type != 21:
|
||||
if (nalu_type == 20 || nalu_type == 21) {
|
||||
RTC_LOG(LS_ERROR) << "Unsupported nal unit type.";
|
||||
return kUnsupportedStream;
|
||||
}
|
||||
// if (nal_unit_type == 20 || nal_unit_type == 21)
|
||||
// ref_pic_list_mvc_modification()
|
||||
// else
|
||||
{
|
||||
// ref_pic_list_modification():
|
||||
// `slice_type` checks here don't use named constants as they aren't named
|
||||
// in the spec for this segment. Keeping them consistent makes it easier to
|
||||
// verify that they are both the same.
|
||||
if (slice_type % 5 != 2 && slice_type % 5 != 4) {
|
||||
// ref_pic_list_modification_flag_l0: u(1)
|
||||
if (slice_reader.Read<bool>()) {
|
||||
uint32_t modification_of_pic_nums_idc;
|
||||
do {
|
||||
// modification_of_pic_nums_idc: ue(v)
|
||||
modification_of_pic_nums_idc = slice_reader.ReadExponentialGolomb();
|
||||
if (modification_of_pic_nums_idc == 0 ||
|
||||
modification_of_pic_nums_idc == 1) {
|
||||
// abs_diff_pic_num_minus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
} else if (modification_of_pic_nums_idc == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
} while (modification_of_pic_nums_idc != 3 && slice_reader.Ok());
|
||||
}
|
||||
}
|
||||
if (slice_type % 5 == 1) {
|
||||
// ref_pic_list_modification_flag_l1: u(1)
|
||||
if (slice_reader.Read<bool>()) {
|
||||
uint32_t modification_of_pic_nums_idc;
|
||||
do {
|
||||
// modification_of_pic_nums_idc: ue(v)
|
||||
modification_of_pic_nums_idc = slice_reader.ReadExponentialGolomb();
|
||||
if (modification_of_pic_nums_idc == 0 ||
|
||||
modification_of_pic_nums_idc == 1) {
|
||||
// abs_diff_pic_num_minus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
} else if (modification_of_pic_nums_idc == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
} while (modification_of_pic_nums_idc != 3 && slice_reader.Ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!slice_reader.Ok()) {
|
||||
return kInvalidStream;
|
||||
}
|
||||
// TODO(pbos): Do we need support for pred_weight_table()?
|
||||
if ((pps_->weighted_pred_flag && (slice_type == H264::SliceType::kP ||
|
||||
slice_type == H264::SliceType::kSp)) ||
|
||||
(pps_->weighted_bipred_idc == 1 && slice_type == H264::SliceType::kB)) {
|
||||
RTC_LOG(LS_ERROR) << "Streams with pred_weight_table unsupported.";
|
||||
return kUnsupportedStream;
|
||||
}
|
||||
// if ((weighted_pred_flag && (slice_type == P || slice_type == SP)) ||
|
||||
// (weighted_bipred_idc == 1 && slice_type == B)) {
|
||||
// pred_weight_table()
|
||||
// }
|
||||
if (nal_ref_idc != 0) {
|
||||
// dec_ref_pic_marking():
|
||||
if (is_idr) {
|
||||
// no_output_of_prior_pics_flag: u(1)
|
||||
// long_term_reference_flag: u(1)
|
||||
slice_reader.ConsumeBits(2);
|
||||
} else {
|
||||
// adaptive_ref_pic_marking_mode_flag: u(1)
|
||||
if (slice_reader.Read<bool>()) {
|
||||
uint32_t memory_management_control_operation;
|
||||
do {
|
||||
// memory_management_control_operation: ue(v)
|
||||
memory_management_control_operation =
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
if (memory_management_control_operation == 1 ||
|
||||
memory_management_control_operation == 3) {
|
||||
// difference_of_pic_nums_minus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
if (memory_management_control_operation == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
if (memory_management_control_operation == 3 ||
|
||||
memory_management_control_operation == 6) {
|
||||
// long_term_frame_idx: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
if (memory_management_control_operation == 4) {
|
||||
// max_long_term_frame_idx_plus1: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
} while (memory_management_control_operation != 0 && slice_reader.Ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pps_->entropy_coding_mode_flag && slice_type != H264::SliceType::kI &&
|
||||
slice_type != H264::SliceType::kSi) {
|
||||
// cabac_init_idc: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
}
|
||||
|
||||
int last_slice_qp_delta = slice_reader.ReadSignedExponentialGolomb();
|
||||
if (!slice_reader.Ok()) {
|
||||
return kInvalidStream;
|
||||
}
|
||||
if (abs(last_slice_qp_delta) > kMaxAbsQpDeltaValue) {
|
||||
// Something has gone wrong, and the parsed value is invalid.
|
||||
RTC_LOG(LS_WARNING) << "Parsed QP value out of range.";
|
||||
return kInvalidStream;
|
||||
}
|
||||
|
||||
last_slice_qp_delta_ = last_slice_qp_delta;
|
||||
return kOk;
|
||||
}
|
||||
|
||||
void H264BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) {
|
||||
H264::NaluType nalu_type = H264::ParseNaluType(slice[0]);
|
||||
switch (nalu_type) {
|
||||
case H264::NaluType::kSps: {
|
||||
sps_ = SpsParser::ParseSps(slice + H264::kNaluTypeSize,
|
||||
length - H264::kNaluTypeSize);
|
||||
if (!sps_)
|
||||
RTC_DLOG(LS_WARNING) << "Unable to parse SPS from H264 bitstream.";
|
||||
break;
|
||||
}
|
||||
case H264::NaluType::kPps: {
|
||||
pps_ = PpsParser::ParsePps(slice + H264::kNaluTypeSize,
|
||||
length - H264::kNaluTypeSize);
|
||||
if (!pps_)
|
||||
RTC_DLOG(LS_WARNING) << "Unable to parse PPS from H264 bitstream.";
|
||||
break;
|
||||
}
|
||||
case H264::NaluType::kAud:
|
||||
case H264::NaluType::kSei:
|
||||
case H264::NaluType::kPrefix:
|
||||
break; // Ignore these nalus, as we don't care about their contents.
|
||||
default:
|
||||
Result res = ParseNonParameterSetNalu(slice, length, nalu_type);
|
||||
if (res != kOk)
|
||||
RTC_DLOG(LS_INFO) << "Failed to parse bitstream. Error: " << res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void H264BitstreamParser::ParseBitstream(
|
||||
rtc::ArrayView<const uint8_t> bitstream) {
|
||||
std::vector<H264::NaluIndex> nalu_indices =
|
||||
H264::FindNaluIndices(bitstream.data(), bitstream.size());
|
||||
for (const H264::NaluIndex& index : nalu_indices)
|
||||
ParseSlice(bitstream.data() + index.payload_start_offset,
|
||||
index.payload_size);
|
||||
}
|
||||
|
||||
absl::optional<int> H264BitstreamParser::GetLastSliceQp() const {
|
||||
if (!last_slice_qp_delta_ || !pps_)
|
||||
return absl::nullopt;
|
||||
const int qp = 26 + pps_->pic_init_qp_minus26 + *last_slice_qp_delta_;
|
||||
if (qp < kMinQpValue || qp > kMaxQpValue) {
|
||||
RTC_LOG(LS_ERROR) << "Parsed invalid QP from bitstream.";
|
||||
return absl::nullopt;
|
||||
}
|
||||
return qp;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 COMMON_VIDEO_H264_H264_BITSTREAM_PARSER_H_
|
||||
#define COMMON_VIDEO_H264_H264_BITSTREAM_PARSER_H_
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video_codecs/bitstream_parser.h"
|
||||
#include "common_video/h264/pps_parser.h"
|
||||
#include "common_video/h264/sps_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Stateful H264 bitstream parser (due to SPS/PPS). Used to parse out QP values
|
||||
// from the bitstream.
|
||||
// TODO(pbos): Unify with RTP SPS parsing and only use one H264 parser.
|
||||
// TODO(pbos): If/when this gets used on the receiver side CHECKs must be
|
||||
// removed and gracefully abort as we have no control over receive-side
|
||||
// bitstreams.
|
||||
class H264BitstreamParser : public BitstreamParser {
|
||||
public:
|
||||
H264BitstreamParser();
|
||||
~H264BitstreamParser() override;
|
||||
|
||||
void ParseBitstream(rtc::ArrayView<const uint8_t> bitstream) override;
|
||||
absl::optional<int> GetLastSliceQp() const override;
|
||||
|
||||
protected:
|
||||
enum Result {
|
||||
kOk,
|
||||
kInvalidStream,
|
||||
kUnsupportedStream,
|
||||
};
|
||||
void ParseSlice(const uint8_t* slice, size_t length);
|
||||
Result ParseNonParameterSetNalu(const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type);
|
||||
|
||||
// SPS/PPS state, updated when parsing new SPS/PPS, used to parse slices.
|
||||
absl::optional<SpsParser::SpsState> sps_;
|
||||
absl::optional<PpsParser::PpsState> pps_;
|
||||
|
||||
// Last parsed slice QP.
|
||||
absl::optional<int32_t> last_slice_qp_delta_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H264_H264_BITSTREAM_PARSER_H_
|
||||
116
TMessagesProj/jni/voip/webrtc/common_video/h264/h264_common.cc
Normal file
116
TMessagesProj/jni/voip/webrtc/common_video/h264/h264_common.cc
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 "common_video/h264/h264_common.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace webrtc {
|
||||
namespace H264 {
|
||||
|
||||
const uint8_t kNaluTypeMask = 0x1F;
|
||||
|
||||
std::vector<NaluIndex> FindNaluIndices(const uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
// This is sorta like Boyer-Moore, but with only the first optimization step:
|
||||
// given a 3-byte sequence we're looking at, if the 3rd byte isn't 1 or 0,
|
||||
// skip ahead to the next 3-byte sequence. 0s and 1s are relatively rare, so
|
||||
// this will skip the majority of reads/checks.
|
||||
std::vector<NaluIndex> sequences;
|
||||
if (buffer_size < kNaluShortStartSequenceSize)
|
||||
return sequences;
|
||||
|
||||
static_assert(kNaluShortStartSequenceSize >= 2,
|
||||
"kNaluShortStartSequenceSize must be larger or equals to 2");
|
||||
const size_t end = buffer_size - kNaluShortStartSequenceSize;
|
||||
for (size_t i = 0; i < end;) {
|
||||
if (buffer[i + 2] > 1) {
|
||||
i += 3;
|
||||
} else if (buffer[i + 2] == 1) {
|
||||
if (buffer[i + 1] == 0 && buffer[i] == 0) {
|
||||
// We found a start sequence, now check if it was a 3 of 4 byte one.
|
||||
NaluIndex index = {i, i + 3, 0};
|
||||
if (index.start_offset > 0 && buffer[index.start_offset - 1] == 0)
|
||||
--index.start_offset;
|
||||
|
||||
// Update length of previous entry.
|
||||
auto it = sequences.rbegin();
|
||||
if (it != sequences.rend())
|
||||
it->payload_size = index.start_offset - it->payload_start_offset;
|
||||
|
||||
sequences.push_back(index);
|
||||
}
|
||||
|
||||
i += 3;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Update length of last entry, if any.
|
||||
auto it = sequences.rbegin();
|
||||
if (it != sequences.rend())
|
||||
it->payload_size = buffer_size - it->payload_start_offset;
|
||||
|
||||
return sequences;
|
||||
}
|
||||
|
||||
NaluType ParseNaluType(uint8_t data) {
|
||||
return static_cast<NaluType>(data & kNaluTypeMask);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ParseRbsp(const uint8_t* data, size_t length) {
|
||||
std::vector<uint8_t> out;
|
||||
out.reserve(length);
|
||||
|
||||
for (size_t i = 0; i < length;) {
|
||||
// Be careful about over/underflow here. byte_length_ - 3 can underflow, and
|
||||
// i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_
|
||||
// above, and that expression will produce the number of bytes left in
|
||||
// the stream including the byte at i.
|
||||
if (length - i >= 3 && !data[i] && !data[i + 1] && data[i + 2] == 3) {
|
||||
// Two rbsp bytes.
|
||||
out.push_back(data[i++]);
|
||||
out.push_back(data[i++]);
|
||||
// Skip the emulation byte.
|
||||
i++;
|
||||
} else {
|
||||
// Single rbsp byte.
|
||||
out.push_back(data[i++]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void WriteRbsp(const uint8_t* bytes, size_t length, rtc::Buffer* destination) {
|
||||
static const uint8_t kZerosInStartSequence = 2;
|
||||
static const uint8_t kEmulationByte = 0x03u;
|
||||
size_t num_consecutive_zeros = 0;
|
||||
destination->EnsureCapacity(destination->size() + length);
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
uint8_t byte = bytes[i];
|
||||
if (byte <= kEmulationByte &&
|
||||
num_consecutive_zeros >= kZerosInStartSequence) {
|
||||
// Need to escape.
|
||||
destination->AppendData(kEmulationByte);
|
||||
num_consecutive_zeros = 0;
|
||||
}
|
||||
destination->AppendData(byte);
|
||||
if (byte == 0) {
|
||||
++num_consecutive_zeros;
|
||||
} else {
|
||||
num_consecutive_zeros = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace H264
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 COMMON_VIDEO_H264_H264_COMMON_H_
|
||||
#define COMMON_VIDEO_H264_H264_COMMON_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace H264 {
|
||||
// The size of a full NALU start sequence {0 0 0 1}, used for the first NALU
|
||||
// of an access unit, and for SPS and PPS blocks.
|
||||
const size_t kNaluLongStartSequenceSize = 4;
|
||||
|
||||
// The size of a shortened NALU start sequence {0 0 1}, that may be used if
|
||||
// not the first NALU of an access unit or an SPS or PPS block.
|
||||
const size_t kNaluShortStartSequenceSize = 3;
|
||||
|
||||
// The size of the NALU type byte (1).
|
||||
const size_t kNaluTypeSize = 1;
|
||||
|
||||
enum NaluType : uint8_t {
|
||||
kSlice = 1,
|
||||
kIdr = 5,
|
||||
kSei = 6,
|
||||
kSps = 7,
|
||||
kPps = 8,
|
||||
kAud = 9,
|
||||
kEndOfSequence = 10,
|
||||
kEndOfStream = 11,
|
||||
kFiller = 12,
|
||||
kPrefix = 14,
|
||||
kStapA = 24,
|
||||
kFuA = 28
|
||||
};
|
||||
|
||||
enum SliceType : uint8_t { kP = 0, kB = 1, kI = 2, kSp = 3, kSi = 4 };
|
||||
|
||||
struct NaluIndex {
|
||||
// Start index of NALU, including start sequence.
|
||||
size_t start_offset;
|
||||
// Start index of NALU payload, typically type header.
|
||||
size_t payload_start_offset;
|
||||
// Length of NALU payload, in bytes, counting from payload_start_offset.
|
||||
size_t payload_size;
|
||||
};
|
||||
|
||||
// Returns a vector of the NALU indices in the given buffer.
|
||||
RTC_EXPORT std::vector<NaluIndex> FindNaluIndices(const uint8_t* buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
// Get the NAL type from the header byte immediately following start sequence.
|
||||
RTC_EXPORT NaluType ParseNaluType(uint8_t data);
|
||||
|
||||
// Methods for parsing and writing RBSP. See section 7.4.1 of the H264 spec.
|
||||
//
|
||||
// The following sequences are illegal, and need to be escaped when encoding:
|
||||
// 00 00 00 -> 00 00 03 00
|
||||
// 00 00 01 -> 00 00 03 01
|
||||
// 00 00 02 -> 00 00 03 02
|
||||
// And things in the source that look like the emulation byte pattern (00 00 03)
|
||||
// need to have an extra emulation byte added, so it's removed when decoding:
|
||||
// 00 00 03 -> 00 00 03 03
|
||||
//
|
||||
// Decoding is simply a matter of finding any 00 00 03 sequence and removing
|
||||
// the 03 emulation byte.
|
||||
|
||||
// Parse the given data and remove any emulation byte escaping.
|
||||
std::vector<uint8_t> ParseRbsp(const uint8_t* data, size_t length);
|
||||
|
||||
// Write the given data to the destination buffer, inserting and emulation
|
||||
// bytes in order to escape any data the could be interpreted as a start
|
||||
// sequence.
|
||||
void WriteRbsp(const uint8_t* bytes, size_t length, rtc::Buffer* destination);
|
||||
} // namespace H264
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H264_H264_COMMON_H_
|
||||
160
TMessagesProj/jni/voip/webrtc/common_video/h264/pps_parser.cc
Normal file
160
TMessagesProj/jni/voip/webrtc/common_video/h264/pps_parser.cc
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 "common_video/h264/pps_parser.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "common_video/h264/h264_common.h"
|
||||
#include "rtc_base/bitstream_reader.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
constexpr int kMaxPicInitQpDeltaValue = 25;
|
||||
constexpr int kMinPicInitQpDeltaValue = -26;
|
||||
} // namespace
|
||||
|
||||
// General note: this is based off the 02/2014 version of the H.264 standard.
|
||||
// You can find it on this page:
|
||||
// http://www.itu.int/rec/T-REC-H.264
|
||||
|
||||
absl::optional<PpsParser::PpsState> PpsParser::ParsePps(const uint8_t* data,
|
||||
size_t length) {
|
||||
// First, parse out rbsp, which is basically the source buffer minus emulation
|
||||
// bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in
|
||||
// section 7.3.1 of the H.264 standard.
|
||||
return ParseInternal(H264::ParseRbsp(data, length));
|
||||
}
|
||||
|
||||
bool PpsParser::ParsePpsIds(const uint8_t* data,
|
||||
size_t length,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id) {
|
||||
RTC_DCHECK(pps_id);
|
||||
RTC_DCHECK(sps_id);
|
||||
// First, parse out rbsp, which is basically the source buffer minus emulation
|
||||
// bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in
|
||||
// section 7.3.1 of the H.264 standard.
|
||||
std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
|
||||
BitstreamReader reader(unpacked_buffer);
|
||||
*pps_id = reader.ReadExponentialGolomb();
|
||||
*sps_id = reader.ReadExponentialGolomb();
|
||||
return reader.Ok();
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> PpsParser::ParsePpsIdFromSlice(const uint8_t* data,
|
||||
size_t length) {
|
||||
std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
|
||||
BitstreamReader slice_reader(unpacked_buffer);
|
||||
|
||||
// first_mb_in_slice: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
// slice_type: ue(v)
|
||||
slice_reader.ReadExponentialGolomb();
|
||||
// pic_parameter_set_id: ue(v)
|
||||
uint32_t slice_pps_id = slice_reader.ReadExponentialGolomb();
|
||||
if (!slice_reader.Ok()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
return slice_pps_id;
|
||||
}
|
||||
|
||||
absl::optional<PpsParser::PpsState> PpsParser::ParseInternal(
|
||||
rtc::ArrayView<const uint8_t> buffer) {
|
||||
BitstreamReader reader(buffer);
|
||||
PpsState pps;
|
||||
pps.id = reader.ReadExponentialGolomb();
|
||||
pps.sps_id = reader.ReadExponentialGolomb();
|
||||
|
||||
// entropy_coding_mode_flag: u(1)
|
||||
pps.entropy_coding_mode_flag = reader.Read<bool>();
|
||||
// bottom_field_pic_order_in_frame_present_flag: u(1)
|
||||
pps.bottom_field_pic_order_in_frame_present_flag = reader.Read<bool>();
|
||||
|
||||
// num_slice_groups_minus1: ue(v)
|
||||
uint32_t num_slice_groups_minus1 = reader.ReadExponentialGolomb();
|
||||
if (num_slice_groups_minus1 > 0) {
|
||||
// slice_group_map_type: ue(v)
|
||||
uint32_t slice_group_map_type = reader.ReadExponentialGolomb();
|
||||
if (slice_group_map_type == 0) {
|
||||
for (uint32_t i_group = 0;
|
||||
i_group <= num_slice_groups_minus1 && reader.Ok(); ++i_group) {
|
||||
// run_length_minus1[iGroup]: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
}
|
||||
} else if (slice_group_map_type == 1) {
|
||||
// TODO(sprang): Implement support for dispersed slice group map type.
|
||||
// See 8.2.2.2 Specification for dispersed slice group map type.
|
||||
} else if (slice_group_map_type == 2) {
|
||||
for (uint32_t i_group = 0;
|
||||
i_group <= num_slice_groups_minus1 && reader.Ok(); ++i_group) {
|
||||
// top_left[iGroup]: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// bottom_right[iGroup]: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
}
|
||||
} else if (slice_group_map_type == 3 || slice_group_map_type == 4 ||
|
||||
slice_group_map_type == 5) {
|
||||
// slice_group_change_direction_flag: u(1)
|
||||
reader.ConsumeBits(1);
|
||||
// slice_group_change_rate_minus1: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
} else if (slice_group_map_type == 6) {
|
||||
// pic_size_in_map_units_minus1: ue(v)
|
||||
uint32_t pic_size_in_map_units = reader.ReadExponentialGolomb() + 1;
|
||||
int slice_group_id_bits = 1 + absl::bit_width(num_slice_groups_minus1);
|
||||
|
||||
// slice_group_id: array of size pic_size_in_map_units, each element
|
||||
// is represented by ceil(log2(num_slice_groups_minus1 + 1)) bits.
|
||||
int64_t bits_to_consume =
|
||||
int64_t{slice_group_id_bits} * pic_size_in_map_units;
|
||||
if (!reader.Ok() || bits_to_consume > std::numeric_limits<int>::max()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
reader.ConsumeBits(bits_to_consume);
|
||||
}
|
||||
}
|
||||
// num_ref_idx_l0_default_active_minus1: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// num_ref_idx_l1_default_active_minus1: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// weighted_pred_flag: u(1)
|
||||
pps.weighted_pred_flag = reader.Read<bool>();
|
||||
// weighted_bipred_idc: u(2)
|
||||
pps.weighted_bipred_idc = reader.ReadBits(2);
|
||||
|
||||
// pic_init_qp_minus26: se(v)
|
||||
pps.pic_init_qp_minus26 = reader.ReadSignedExponentialGolomb();
|
||||
// Sanity-check parsed value
|
||||
if (!reader.Ok() || pps.pic_init_qp_minus26 > kMaxPicInitQpDeltaValue ||
|
||||
pps.pic_init_qp_minus26 < kMinPicInitQpDeltaValue) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
// pic_init_qs_minus26: se(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// chroma_qp_index_offset: se(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// deblocking_filter_control_present_flag: u(1)
|
||||
// constrained_intra_pred_flag: u(1)
|
||||
reader.ConsumeBits(2);
|
||||
// redundant_pic_cnt_present_flag: u(1)
|
||||
pps.redundant_pic_cnt_present_flag = reader.ReadBit();
|
||||
if (!reader.Ok()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
return pps;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
60
TMessagesProj/jni/voip/webrtc/common_video/h264/pps_parser.h
Normal file
60
TMessagesProj/jni/voip/webrtc/common_video/h264/pps_parser.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 COMMON_VIDEO_H264_PPS_PARSER_H_
|
||||
#define COMMON_VIDEO_H264_PPS_PARSER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class for parsing out picture parameter set (PPS) data from a H264 NALU.
|
||||
class PpsParser {
|
||||
public:
|
||||
// The parsed state of the PPS. Only some select values are stored.
|
||||
// Add more as they are actually needed.
|
||||
struct PpsState {
|
||||
PpsState() = default;
|
||||
|
||||
bool bottom_field_pic_order_in_frame_present_flag = false;
|
||||
bool weighted_pred_flag = false;
|
||||
bool entropy_coding_mode_flag = false;
|
||||
uint32_t weighted_bipred_idc = false;
|
||||
uint32_t redundant_pic_cnt_present_flag = 0;
|
||||
int pic_init_qp_minus26 = 0;
|
||||
uint32_t id = 0;
|
||||
uint32_t sps_id = 0;
|
||||
};
|
||||
|
||||
// Unpack RBSP and parse PPS state from the supplied buffer.
|
||||
static absl::optional<PpsState> ParsePps(const uint8_t* data, size_t length);
|
||||
|
||||
static bool ParsePpsIds(const uint8_t* data,
|
||||
size_t length,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id);
|
||||
|
||||
static absl::optional<uint32_t> ParsePpsIdFromSlice(const uint8_t* data,
|
||||
size_t length);
|
||||
|
||||
protected:
|
||||
// Parse the PPS state, for a buffer where RBSP decoding has already been
|
||||
// performed.
|
||||
static absl::optional<PpsState> ParseInternal(
|
||||
rtc::ArrayView<const uint8_t> buffer);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H264_PPS_PARSER_H_
|
||||
227
TMessagesProj/jni/voip/webrtc/common_video/h264/sps_parser.cc
Normal file
227
TMessagesProj/jni/voip/webrtc/common_video/h264/sps_parser.cc
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 "common_video/h264/sps_parser.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h264/h264_common.h"
|
||||
#include "rtc_base/bitstream_reader.h"
|
||||
|
||||
namespace {
|
||||
constexpr int kScalingDeltaMin = -128;
|
||||
constexpr int kScaldingDeltaMax = 127;
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
SpsParser::SpsState::SpsState() = default;
|
||||
SpsParser::SpsState::SpsState(const SpsState&) = default;
|
||||
SpsParser::SpsState::~SpsState() = default;
|
||||
|
||||
// General note: this is based off the 02/2014 version of the H.264 standard.
|
||||
// You can find it on this page:
|
||||
// http://www.itu.int/rec/T-REC-H.264
|
||||
|
||||
// Unpack RBSP and parse SPS state from the supplied buffer.
|
||||
absl::optional<SpsParser::SpsState> SpsParser::ParseSps(const uint8_t* data,
|
||||
size_t length) {
|
||||
std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
|
||||
BitstreamReader reader(unpacked_buffer);
|
||||
return ParseSpsUpToVui(reader);
|
||||
}
|
||||
|
||||
absl::optional<SpsParser::SpsState> SpsParser::ParseSpsUpToVui(
|
||||
BitstreamReader& reader) {
|
||||
// Now, we need to use a bitstream reader to parse through the actual AVC SPS
|
||||
// format. See Section 7.3.2.1.1 ("Sequence parameter set data syntax") of the
|
||||
// H.264 standard for a complete description.
|
||||
// Since we only care about resolution, we ignore the majority of fields, but
|
||||
// we still have to actively parse through a lot of the data, since many of
|
||||
// the fields have variable size.
|
||||
// We're particularly interested in:
|
||||
// chroma_format_idc -> affects crop units
|
||||
// pic_{width,height}_* -> resolution of the frame in macroblocks (16x16).
|
||||
// frame_crop_*_offset -> crop information
|
||||
|
||||
SpsState sps;
|
||||
|
||||
// chroma_format_idc will be ChromaArrayType if separate_colour_plane_flag is
|
||||
// 0. It defaults to 1, when not specified.
|
||||
uint32_t chroma_format_idc = 1;
|
||||
|
||||
// profile_idc: u(8). We need it to determine if we need to read/skip chroma
|
||||
// formats.
|
||||
uint8_t profile_idc = reader.Read<uint8_t>();
|
||||
// constraint_set0_flag through constraint_set5_flag + reserved_zero_2bits
|
||||
// 1 bit each for the flags + 2 bits + 8 bits for level_idc = 16 bits.
|
||||
reader.ConsumeBits(16);
|
||||
// seq_parameter_set_id: ue(v)
|
||||
sps.id = reader.ReadExponentialGolomb();
|
||||
sps.separate_colour_plane_flag = 0;
|
||||
// See if profile_idc has chroma format information.
|
||||
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 ||
|
||||
profile_idc == 244 || profile_idc == 44 || profile_idc == 83 ||
|
||||
profile_idc == 86 || profile_idc == 118 || profile_idc == 128 ||
|
||||
profile_idc == 138 || profile_idc == 139 || profile_idc == 134) {
|
||||
// chroma_format_idc: ue(v)
|
||||
chroma_format_idc = reader.ReadExponentialGolomb();
|
||||
if (chroma_format_idc == 3) {
|
||||
// separate_colour_plane_flag: u(1)
|
||||
sps.separate_colour_plane_flag = reader.ReadBit();
|
||||
}
|
||||
// bit_depth_luma_minus8: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// bit_depth_chroma_minus8: ue(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// qpprime_y_zero_transform_bypass_flag: u(1)
|
||||
reader.ConsumeBits(1);
|
||||
// seq_scaling_matrix_present_flag: u(1)
|
||||
if (reader.Read<bool>()) {
|
||||
// Process the scaling lists just enough to be able to properly
|
||||
// skip over them, so we can still read the resolution on streams
|
||||
// where this is included.
|
||||
int scaling_list_count = (chroma_format_idc == 3 ? 12 : 8);
|
||||
for (int i = 0; i < scaling_list_count; ++i) {
|
||||
// seq_scaling_list_present_flag[i] : u(1)
|
||||
if (reader.Read<bool>()) {
|
||||
int last_scale = 8;
|
||||
int next_scale = 8;
|
||||
int size_of_scaling_list = i < 6 ? 16 : 64;
|
||||
for (int j = 0; j < size_of_scaling_list; j++) {
|
||||
if (next_scale != 0) {
|
||||
// delta_scale: se(v)
|
||||
int delta_scale = reader.ReadSignedExponentialGolomb();
|
||||
if (!reader.Ok() || delta_scale < kScalingDeltaMin ||
|
||||
delta_scale > kScaldingDeltaMax) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
next_scale = (last_scale + delta_scale + 256) % 256;
|
||||
}
|
||||
if (next_scale != 0)
|
||||
last_scale = next_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// log2_max_frame_num and log2_max_pic_order_cnt_lsb are used with
|
||||
// BitstreamReader::ReadBits, which can read at most 64 bits at a time. We
|
||||
// also have to avoid overflow when adding 4 to the on-wire golomb value,
|
||||
// e.g., for evil input data, ReadExponentialGolomb might return 0xfffc.
|
||||
const uint32_t kMaxLog2Minus4 = 12;
|
||||
|
||||
// log2_max_frame_num_minus4: ue(v)
|
||||
uint32_t log2_max_frame_num_minus4 = reader.ReadExponentialGolomb();
|
||||
if (!reader.Ok() || log2_max_frame_num_minus4 > kMaxLog2Minus4) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
sps.log2_max_frame_num = log2_max_frame_num_minus4 + 4;
|
||||
|
||||
// pic_order_cnt_type: ue(v)
|
||||
sps.pic_order_cnt_type = reader.ReadExponentialGolomb();
|
||||
if (sps.pic_order_cnt_type == 0) {
|
||||
// log2_max_pic_order_cnt_lsb_minus4: ue(v)
|
||||
uint32_t log2_max_pic_order_cnt_lsb_minus4 = reader.ReadExponentialGolomb();
|
||||
if (!reader.Ok() || log2_max_pic_order_cnt_lsb_minus4 > kMaxLog2Minus4) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
sps.log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4;
|
||||
} else if (sps.pic_order_cnt_type == 1) {
|
||||
// delta_pic_order_always_zero_flag: u(1)
|
||||
sps.delta_pic_order_always_zero_flag = reader.ReadBit();
|
||||
// offset_for_non_ref_pic: se(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// offset_for_top_to_bottom_field: se(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
// num_ref_frames_in_pic_order_cnt_cycle: ue(v)
|
||||
uint32_t num_ref_frames_in_pic_order_cnt_cycle =
|
||||
reader.ReadExponentialGolomb();
|
||||
for (size_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
|
||||
// offset_for_ref_frame[i]: se(v)
|
||||
reader.ReadExponentialGolomb();
|
||||
if (!reader.Ok()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
// max_num_ref_frames: ue(v)
|
||||
sps.max_num_ref_frames = reader.ReadExponentialGolomb();
|
||||
// gaps_in_frame_num_value_allowed_flag: u(1)
|
||||
reader.ConsumeBits(1);
|
||||
//
|
||||
// IMPORTANT ONES! Now we're getting to resolution. First we read the pic
|
||||
// width/height in macroblocks (16x16), which gives us the base resolution,
|
||||
// and then we continue on until we hit the frame crop offsets, which are used
|
||||
// to signify resolutions that aren't multiples of 16.
|
||||
//
|
||||
// pic_width_in_mbs_minus1: ue(v)
|
||||
sps.width = 16 * (reader.ReadExponentialGolomb() + 1);
|
||||
// pic_height_in_map_units_minus1: ue(v)
|
||||
uint32_t pic_height_in_map_units_minus1 = reader.ReadExponentialGolomb();
|
||||
// frame_mbs_only_flag: u(1)
|
||||
sps.frame_mbs_only_flag = reader.ReadBit();
|
||||
if (!sps.frame_mbs_only_flag) {
|
||||
// mb_adaptive_frame_field_flag: u(1)
|
||||
reader.ConsumeBits(1);
|
||||
}
|
||||
sps.height =
|
||||
16 * (2 - sps.frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1);
|
||||
// direct_8x8_inference_flag: u(1)
|
||||
reader.ConsumeBits(1);
|
||||
//
|
||||
// MORE IMPORTANT ONES! Now we're at the frame crop information.
|
||||
//
|
||||
uint32_t frame_crop_left_offset = 0;
|
||||
uint32_t frame_crop_right_offset = 0;
|
||||
uint32_t frame_crop_top_offset = 0;
|
||||
uint32_t frame_crop_bottom_offset = 0;
|
||||
// frame_cropping_flag: u(1)
|
||||
if (reader.Read<bool>()) {
|
||||
// frame_crop_{left, right, top, bottom}_offset: ue(v)
|
||||
frame_crop_left_offset = reader.ReadExponentialGolomb();
|
||||
frame_crop_right_offset = reader.ReadExponentialGolomb();
|
||||
frame_crop_top_offset = reader.ReadExponentialGolomb();
|
||||
frame_crop_bottom_offset = reader.ReadExponentialGolomb();
|
||||
}
|
||||
// vui_parameters_present_flag: u(1)
|
||||
sps.vui_params_present = reader.ReadBit();
|
||||
|
||||
// Far enough! We don't use the rest of the SPS.
|
||||
if (!reader.Ok()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Figure out the crop units in pixels. That's based on the chroma format's
|
||||
// sampling, which is indicated by chroma_format_idc.
|
||||
if (sps.separate_colour_plane_flag || chroma_format_idc == 0) {
|
||||
frame_crop_bottom_offset *= (2 - sps.frame_mbs_only_flag);
|
||||
frame_crop_top_offset *= (2 - sps.frame_mbs_only_flag);
|
||||
} else if (!sps.separate_colour_plane_flag && chroma_format_idc > 0) {
|
||||
// Width multipliers for formats 1 (4:2:0) and 2 (4:2:2).
|
||||
if (chroma_format_idc == 1 || chroma_format_idc == 2) {
|
||||
frame_crop_left_offset *= 2;
|
||||
frame_crop_right_offset *= 2;
|
||||
}
|
||||
// Height multipliers for format 1 (4:2:0).
|
||||
if (chroma_format_idc == 1) {
|
||||
frame_crop_top_offset *= 2;
|
||||
frame_crop_bottom_offset *= 2;
|
||||
}
|
||||
}
|
||||
// Subtract the crop for each dimension.
|
||||
sps.width -= (frame_crop_left_offset + frame_crop_right_offset);
|
||||
sps.height -= (frame_crop_top_offset + frame_crop_bottom_offset);
|
||||
|
||||
return sps;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
53
TMessagesProj/jni/voip/webrtc/common_video/h264/sps_parser.h
Normal file
53
TMessagesProj/jni/voip/webrtc/common_video/h264/sps_parser.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 COMMON_VIDEO_H264_SPS_PARSER_H_
|
||||
#define COMMON_VIDEO_H264_SPS_PARSER_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "rtc_base/bitstream_reader.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class for parsing out sequence parameter set (SPS) data from an H264 NALU.
|
||||
class RTC_EXPORT SpsParser {
|
||||
public:
|
||||
// The parsed state of the SPS. Only some select values are stored.
|
||||
// Add more as they are actually needed.
|
||||
struct RTC_EXPORT SpsState {
|
||||
SpsState();
|
||||
SpsState(const SpsState&);
|
||||
~SpsState();
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t delta_pic_order_always_zero_flag = 0;
|
||||
uint32_t separate_colour_plane_flag = 0;
|
||||
uint32_t frame_mbs_only_flag = 0;
|
||||
uint32_t log2_max_frame_num = 4; // Smallest valid value.
|
||||
uint32_t log2_max_pic_order_cnt_lsb = 4; // Smallest valid value.
|
||||
uint32_t pic_order_cnt_type = 0;
|
||||
uint32_t max_num_ref_frames = 0;
|
||||
uint32_t vui_params_present = 0;
|
||||
uint32_t id = 0;
|
||||
};
|
||||
|
||||
// Unpack RBSP and parse SPS state from the supplied buffer.
|
||||
static absl::optional<SpsState> ParseSps(const uint8_t* data, size_t length);
|
||||
|
||||
protected:
|
||||
// Parse the SPS state, up till the VUI part, for a buffer where RBSP
|
||||
// decoding has already been performed.
|
||||
static absl::optional<SpsState> ParseSpsUpToVui(BitstreamReader& reader);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // COMMON_VIDEO_H264_SPS_PARSER_H_
|
||||
|
|
@ -0,0 +1,611 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 "common_video/h264/sps_vui_rewriter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "api/video/color_space.h"
|
||||
#include "common_video/h264/h264_common.h"
|
||||
#include "common_video/h264/sps_parser.h"
|
||||
#include "rtc_base/bit_buffer.h"
|
||||
#include "rtc_base/bitstream_reader.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
// The maximum expected growth from adding a VUI to the SPS. It's actually
|
||||
// closer to 24 or so, but better safe than sorry.
|
||||
const size_t kMaxVuiSpsIncrease = 64;
|
||||
|
||||
const char* kSpsValidHistogramName = "WebRTC.Video.H264.SpsValid";
|
||||
enum SpsValidEvent {
|
||||
kReceivedSpsVuiOk = 1,
|
||||
kReceivedSpsRewritten = 2,
|
||||
kReceivedSpsParseFailure = 3,
|
||||
kSentSpsPocOk = 4,
|
||||
kSentSpsVuiOk = 5,
|
||||
kSentSpsRewritten = 6,
|
||||
kSentSpsParseFailure = 7,
|
||||
kSpsRewrittenMax = 8
|
||||
};
|
||||
|
||||
#define RETURN_FALSE_ON_FAIL(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
RTC_LOG_F(LS_ERROR) << " (line:" << __LINE__ << ") FAILED: " #x; \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint8_t CopyUInt8(BitstreamReader& source, rtc::BitBufferWriter& destination) {
|
||||
uint8_t tmp = source.Read<uint8_t>();
|
||||
if (!destination.WriteUInt8(tmp)) {
|
||||
source.Invalidate();
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint32_t CopyExpGolomb(BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination) {
|
||||
uint32_t tmp = source.ReadExponentialGolomb();
|
||||
if (!destination.WriteExponentialGolomb(tmp)) {
|
||||
source.Invalidate();
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint32_t CopyBits(int bits,
|
||||
BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination) {
|
||||
RTC_DCHECK_GT(bits, 0);
|
||||
RTC_DCHECK_LE(bits, 32);
|
||||
uint64_t tmp = source.ReadBits(bits);
|
||||
if (!destination.WriteBits(tmp, bits)) {
|
||||
source.Invalidate();
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool CopyAndRewriteVui(const SpsParser::SpsState& sps,
|
||||
BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination,
|
||||
const webrtc::ColorSpace* color_space,
|
||||
SpsVuiRewriter::ParseResult& out_vui_rewritten);
|
||||
|
||||
void CopyHrdParameters(BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination);
|
||||
bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
|
||||
uint32_t max_num_ref_frames);
|
||||
bool IsDefaultColorSpace(const ColorSpace& color_space);
|
||||
bool AddVideoSignalTypeInfo(rtc::BitBufferWriter& destination,
|
||||
const ColorSpace& color_space);
|
||||
bool CopyOrRewriteVideoSignalTypeInfo(
|
||||
BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination,
|
||||
const ColorSpace* color_space,
|
||||
SpsVuiRewriter::ParseResult& out_vui_rewritten);
|
||||
bool CopyRemainingBits(BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination);
|
||||
} // namespace
|
||||
|
||||
void SpsVuiRewriter::UpdateStats(ParseResult result, Direction direction) {
|
||||
switch (result) {
|
||||
case SpsVuiRewriter::ParseResult::kVuiRewritten:
|
||||
RTC_HISTOGRAM_ENUMERATION(
|
||||
kSpsValidHistogramName,
|
||||
direction == SpsVuiRewriter::Direction::kIncoming
|
||||
? SpsValidEvent::kReceivedSpsRewritten
|
||||
: SpsValidEvent::kSentSpsRewritten,
|
||||
SpsValidEvent::kSpsRewrittenMax);
|
||||
break;
|
||||
case SpsVuiRewriter::ParseResult::kVuiOk:
|
||||
RTC_HISTOGRAM_ENUMERATION(
|
||||
kSpsValidHistogramName,
|
||||
direction == SpsVuiRewriter::Direction::kIncoming
|
||||
? SpsValidEvent::kReceivedSpsVuiOk
|
||||
: SpsValidEvent::kSentSpsVuiOk,
|
||||
SpsValidEvent::kSpsRewrittenMax);
|
||||
break;
|
||||
case SpsVuiRewriter::ParseResult::kFailure:
|
||||
RTC_HISTOGRAM_ENUMERATION(
|
||||
kSpsValidHistogramName,
|
||||
direction == SpsVuiRewriter::Direction::kIncoming
|
||||
? SpsValidEvent::kReceivedSpsParseFailure
|
||||
: SpsValidEvent::kSentSpsParseFailure,
|
||||
SpsValidEvent::kSpsRewrittenMax);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
absl::optional<SpsParser::SpsState>* sps,
|
||||
const webrtc::ColorSpace* color_space,
|
||||
rtc::Buffer* destination) {
|
||||
// Create temporary RBSP decoded buffer of the payload (exlcuding the
|
||||
// leading nalu type header byte (the SpsParser uses only the payload).
|
||||
std::vector<uint8_t> rbsp_buffer = H264::ParseRbsp(buffer, length);
|
||||
BitstreamReader source_buffer(rbsp_buffer);
|
||||
absl::optional<SpsParser::SpsState> sps_state =
|
||||
SpsParser::ParseSpsUpToVui(source_buffer);
|
||||
if (!sps_state)
|
||||
return ParseResult::kFailure;
|
||||
|
||||
*sps = sps_state;
|
||||
|
||||
// We're going to completely muck up alignment, so we need a BitBufferWriter
|
||||
// to write with.
|
||||
rtc::Buffer out_buffer(length + kMaxVuiSpsIncrease);
|
||||
rtc::BitBufferWriter sps_writer(out_buffer.data(), out_buffer.size());
|
||||
|
||||
// Check how far the SpsParser has read, and copy that data in bulk.
|
||||
RTC_DCHECK(source_buffer.Ok());
|
||||
size_t total_bit_offset =
|
||||
rbsp_buffer.size() * 8 - source_buffer.RemainingBitCount();
|
||||
size_t byte_offset = total_bit_offset / 8;
|
||||
size_t bit_offset = total_bit_offset % 8;
|
||||
memcpy(out_buffer.data(), rbsp_buffer.data(),
|
||||
byte_offset + (bit_offset > 0 ? 1 : 0)); // OK to copy the last bits.
|
||||
|
||||
// SpsParser will have read the vui_params_present flag, which we want to
|
||||
// modify, so back off a bit;
|
||||
if (bit_offset == 0) {
|
||||
--byte_offset;
|
||||
bit_offset = 7;
|
||||
} else {
|
||||
--bit_offset;
|
||||
}
|
||||
sps_writer.Seek(byte_offset, bit_offset);
|
||||
|
||||
ParseResult vui_updated;
|
||||
if (!CopyAndRewriteVui(*sps_state, source_buffer, sps_writer, color_space,
|
||||
vui_updated)) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
|
||||
return ParseResult::kFailure;
|
||||
}
|
||||
|
||||
if (vui_updated == ParseResult::kVuiOk) {
|
||||
// No update necessary after all, just return.
|
||||
return vui_updated;
|
||||
}
|
||||
|
||||
if (!CopyRemainingBits(source_buffer, sps_writer)) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
|
||||
return ParseResult::kFailure;
|
||||
}
|
||||
|
||||
// Pad up to next byte with zero bits.
|
||||
sps_writer.GetCurrentOffset(&byte_offset, &bit_offset);
|
||||
if (bit_offset > 0) {
|
||||
sps_writer.WriteBits(0, 8 - bit_offset);
|
||||
++byte_offset;
|
||||
bit_offset = 0;
|
||||
}
|
||||
|
||||
RTC_DCHECK(byte_offset <= length + kMaxVuiSpsIncrease);
|
||||
RTC_CHECK(destination != nullptr);
|
||||
|
||||
out_buffer.SetSize(byte_offset);
|
||||
|
||||
// Write updates SPS to destination with added RBSP
|
||||
H264::WriteRbsp(out_buffer.data(), out_buffer.size(), destination);
|
||||
|
||||
return ParseResult::kVuiRewritten;
|
||||
}
|
||||
|
||||
SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
absl::optional<SpsParser::SpsState>* sps,
|
||||
const webrtc::ColorSpace* color_space,
|
||||
rtc::Buffer* destination,
|
||||
Direction direction) {
|
||||
ParseResult result =
|
||||
ParseAndRewriteSps(buffer, length, sps, color_space, destination);
|
||||
UpdateStats(result, direction);
|
||||
return result;
|
||||
}
|
||||
|
||||
rtc::Buffer SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(
|
||||
rtc::ArrayView<const uint8_t> buffer,
|
||||
const webrtc::ColorSpace* color_space) {
|
||||
std::vector<H264::NaluIndex> nalus =
|
||||
H264::FindNaluIndices(buffer.data(), buffer.size());
|
||||
|
||||
// Allocate some extra space for potentially adding a missing VUI.
|
||||
rtc::Buffer output_buffer(/*size=*/0, /*capacity=*/buffer.size() +
|
||||
nalus.size() * kMaxVuiSpsIncrease);
|
||||
|
||||
for (const H264::NaluIndex& nalu : nalus) {
|
||||
// Copy NAL unit start code.
|
||||
const uint8_t* start_code_ptr = buffer.data() + nalu.start_offset;
|
||||
const size_t start_code_length =
|
||||
nalu.payload_start_offset - nalu.start_offset;
|
||||
const uint8_t* nalu_ptr = buffer.data() + nalu.payload_start_offset;
|
||||
const size_t nalu_length = nalu.payload_size;
|
||||
|
||||
if (H264::ParseNaluType(nalu_ptr[0]) == H264::NaluType::kSps) {
|
||||
// Check if stream uses picture order count type 0, and if so rewrite it
|
||||
// to enable faster decoding. Streams in that format incur additional
|
||||
// delay because it allows decode order to differ from render order.
|
||||
// The mechanism used is to rewrite (edit or add) the SPS's VUI to contain
|
||||
// restrictions on the maximum number of reordered pictures. This reduces
|
||||
// latency significantly, though it still adds about a frame of latency to
|
||||
// decoding.
|
||||
// Note that we do this rewriting both here (send side, in order to
|
||||
// protect legacy receive clients) in RtpDepacketizerH264::ParseSingleNalu
|
||||
// (receive side, in orderer to protect us from unknown or legacy send
|
||||
// clients).
|
||||
absl::optional<SpsParser::SpsState> sps;
|
||||
rtc::Buffer output_nalu;
|
||||
|
||||
// Add the type header to the output buffer first, so that the rewriter
|
||||
// can append modified payload on top of that.
|
||||
output_nalu.AppendData(nalu_ptr[0]);
|
||||
|
||||
ParseResult result = ParseAndRewriteSps(
|
||||
nalu_ptr + H264::kNaluTypeSize, nalu_length - H264::kNaluTypeSize,
|
||||
&sps, color_space, &output_nalu, Direction::kOutgoing);
|
||||
if (result == ParseResult::kVuiRewritten) {
|
||||
output_buffer.AppendData(start_code_ptr, start_code_length);
|
||||
output_buffer.AppendData(output_nalu.data(), output_nalu.size());
|
||||
continue;
|
||||
}
|
||||
} else if (H264::ParseNaluType(nalu_ptr[0]) == H264::NaluType::kAud) {
|
||||
// Skip the access unit delimiter copy.
|
||||
continue;
|
||||
}
|
||||
|
||||
// vui wasn't rewritten and it is not aud, copy the nal unit as is.
|
||||
output_buffer.AppendData(start_code_ptr, start_code_length);
|
||||
output_buffer.AppendData(nalu_ptr, nalu_length);
|
||||
}
|
||||
return output_buffer;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool CopyAndRewriteVui(const SpsParser::SpsState& sps,
|
||||
BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination,
|
||||
const webrtc::ColorSpace* color_space,
|
||||
SpsVuiRewriter::ParseResult& out_vui_rewritten) {
|
||||
out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk;
|
||||
|
||||
//
|
||||
// vui_parameters_present_flag: u(1)
|
||||
//
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1));
|
||||
|
||||
// ********* IMPORTANT! **********
|
||||
// Now we're at the VUI, so we want to (1) add it if it isn't present, and
|
||||
// (2) rewrite frame reordering values so no reordering is allowed.
|
||||
if (!sps.vui_params_present) {
|
||||
// Write a simple VUI with the parameters we want and 0 for all other flags.
|
||||
|
||||
// aspect_ratio_info_present_flag, overscan_info_present_flag. Both u(1).
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(0, 2));
|
||||
|
||||
uint32_t video_signal_type_present_flag =
|
||||
(color_space && !IsDefaultColorSpace(*color_space)) ? 1 : 0;
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteBits(video_signal_type_present_flag, 1));
|
||||
if (video_signal_type_present_flag) {
|
||||
RETURN_FALSE_ON_FAIL(AddVideoSignalTypeInfo(destination, *color_space));
|
||||
}
|
||||
// chroma_loc_info_present_flag, timing_info_present_flag,
|
||||
// nal_hrd_parameters_present_flag, vcl_hrd_parameters_present_flag,
|
||||
// pic_struct_present_flag, All u(1)
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(0, 5));
|
||||
// bitstream_restriction_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
AddBitstreamRestriction(&destination, sps.max_num_ref_frames));
|
||||
|
||||
out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
|
||||
} else {
|
||||
// Parse out the full VUI.
|
||||
// aspect_ratio_info_present_flag: u(1)
|
||||
uint32_t aspect_ratio_info_present_flag = CopyBits(1, source, destination);
|
||||
if (aspect_ratio_info_present_flag) {
|
||||
// aspect_ratio_idc: u(8)
|
||||
uint8_t aspect_ratio_idc = CopyUInt8(source, destination);
|
||||
if (aspect_ratio_idc == 255u) { // Extended_SAR
|
||||
// sar_width/sar_height: u(16) each.
|
||||
CopyBits(32, source, destination);
|
||||
}
|
||||
}
|
||||
// overscan_info_present_flag: u(1)
|
||||
uint32_t overscan_info_present_flag = CopyBits(1, source, destination);
|
||||
if (overscan_info_present_flag) {
|
||||
// overscan_appropriate_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
}
|
||||
|
||||
CopyOrRewriteVideoSignalTypeInfo(source, destination, color_space,
|
||||
out_vui_rewritten);
|
||||
|
||||
// chroma_loc_info_present_flag: u(1)
|
||||
uint32_t chroma_loc_info_present_flag = CopyBits(1, source, destination);
|
||||
if (chroma_loc_info_present_flag == 1) {
|
||||
// chroma_sample_loc_type_(top|bottom)_field: ue(v) each.
|
||||
CopyExpGolomb(source, destination);
|
||||
CopyExpGolomb(source, destination);
|
||||
}
|
||||
// timing_info_present_flag: u(1)
|
||||
uint32_t timing_info_present_flag = CopyBits(1, source, destination);
|
||||
if (timing_info_present_flag == 1) {
|
||||
// num_units_in_tick, time_scale: u(32) each
|
||||
CopyBits(32, source, destination);
|
||||
CopyBits(32, source, destination);
|
||||
// fixed_frame_rate_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
}
|
||||
// nal_hrd_parameters_present_flag: u(1)
|
||||
uint32_t nal_hrd_parameters_present_flag = CopyBits(1, source, destination);
|
||||
if (nal_hrd_parameters_present_flag == 1) {
|
||||
CopyHrdParameters(source, destination);
|
||||
}
|
||||
// vcl_hrd_parameters_present_flag: u(1)
|
||||
uint32_t vcl_hrd_parameters_present_flag = CopyBits(1, source, destination);
|
||||
if (vcl_hrd_parameters_present_flag == 1) {
|
||||
CopyHrdParameters(source, destination);
|
||||
}
|
||||
if (nal_hrd_parameters_present_flag == 1 ||
|
||||
vcl_hrd_parameters_present_flag == 1) {
|
||||
// low_delay_hrd_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
}
|
||||
// pic_struct_present_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
|
||||
// bitstream_restriction_flag: u(1)
|
||||
uint32_t bitstream_restriction_flag = source.ReadBit();
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1));
|
||||
if (bitstream_restriction_flag == 0) {
|
||||
// We're adding one from scratch.
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
AddBitstreamRestriction(&destination, sps.max_num_ref_frames));
|
||||
out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
|
||||
} else {
|
||||
// We're replacing.
|
||||
// motion_vectors_over_pic_boundaries_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
// max_bytes_per_pic_denom: ue(v)
|
||||
CopyExpGolomb(source, destination);
|
||||
// max_bits_per_mb_denom: ue(v)
|
||||
CopyExpGolomb(source, destination);
|
||||
// log2_max_mv_length_horizontal: ue(v)
|
||||
CopyExpGolomb(source, destination);
|
||||
// log2_max_mv_length_vertical: ue(v)
|
||||
CopyExpGolomb(source, destination);
|
||||
// ********* IMPORTANT! **********
|
||||
// The next two are the ones we need to set to low numbers:
|
||||
// max_num_reorder_frames: ue(v)
|
||||
// max_dec_frame_buffering: ue(v)
|
||||
// However, if they are already set to no greater than the numbers we
|
||||
// want, then we don't need to be rewriting.
|
||||
uint32_t max_num_reorder_frames = source.ReadExponentialGolomb();
|
||||
uint32_t max_dec_frame_buffering = source.ReadExponentialGolomb();
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteExponentialGolomb(0));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteExponentialGolomb(sps.max_num_ref_frames));
|
||||
if (max_num_reorder_frames != 0 ||
|
||||
max_dec_frame_buffering > sps.max_num_ref_frames) {
|
||||
out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
return source.Ok();
|
||||
}
|
||||
|
||||
// Copies a VUI HRD parameters segment.
|
||||
void CopyHrdParameters(BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination) {
|
||||
// cbp_cnt_minus1: ue(v)
|
||||
uint32_t cbp_cnt_minus1 = CopyExpGolomb(source, destination);
|
||||
// bit_rate_scale and cbp_size_scale: u(4) each
|
||||
CopyBits(8, source, destination);
|
||||
for (size_t i = 0; source.Ok() && i <= cbp_cnt_minus1; ++i) {
|
||||
// bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each
|
||||
CopyExpGolomb(source, destination);
|
||||
CopyExpGolomb(source, destination);
|
||||
// cbr_flag: u(1)
|
||||
CopyBits(1, source, destination);
|
||||
}
|
||||
// initial_cbp_removal_delay_length_minus1: u(5)
|
||||
// cbp_removal_delay_length_minus1: u(5)
|
||||
// dbp_output_delay_length_minus1: u(5)
|
||||
// time_offset_length: u(5)
|
||||
CopyBits(5 * 4, source, destination);
|
||||
}
|
||||
|
||||
// These functions are similar to webrtc::H264SpsParser::Parse, and based on the
|
||||
// same version of the H.264 standard. You can find it here:
|
||||
// http://www.itu.int/rec/T-REC-H.264
|
||||
|
||||
// Adds a bitstream restriction VUI segment.
|
||||
bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
|
||||
uint32_t max_num_ref_frames) {
|
||||
// motion_vectors_over_pic_boundaries_flag: u(1)
|
||||
// Default is 1 when not present.
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
|
||||
// max_bytes_per_pic_denom: ue(v)
|
||||
// Default is 2 when not present.
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2));
|
||||
// max_bits_per_mb_denom: ue(v)
|
||||
// Default is 1 when not present.
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1));
|
||||
// log2_max_mv_length_horizontal: ue(v)
|
||||
// log2_max_mv_length_vertical: ue(v)
|
||||
// Both default to 16 when not present.
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
|
||||
|
||||
// ********* IMPORTANT! **********
|
||||
// max_num_reorder_frames: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
|
||||
// max_dec_frame_buffering: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDefaultColorSpace(const ColorSpace& color_space) {
|
||||
return color_space.range() != ColorSpace::RangeID::kFull &&
|
||||
color_space.primaries() == ColorSpace::PrimaryID::kUnspecified &&
|
||||
color_space.transfer() == ColorSpace::TransferID::kUnspecified &&
|
||||
color_space.matrix() == ColorSpace::MatrixID::kUnspecified;
|
||||
}
|
||||
|
||||
bool AddVideoSignalTypeInfo(rtc::BitBufferWriter& destination,
|
||||
const ColorSpace& color_space) {
|
||||
// video_format: u(3).
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(5, 3)); // 5 = Unspecified
|
||||
// video_full_range_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(
|
||||
color_space.range() == ColorSpace::RangeID::kFull ? 1 : 0, 1));
|
||||
// colour_description_present_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1));
|
||||
// colour_primaries: u(8)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteUInt8(static_cast<uint8_t>(color_space.primaries())));
|
||||
// transfer_characteristics: u(8)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteUInt8(static_cast<uint8_t>(color_space.transfer())));
|
||||
// matrix_coefficients: u(8)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteUInt8(static_cast<uint8_t>(color_space.matrix())));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyOrRewriteVideoSignalTypeInfo(
|
||||
BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination,
|
||||
const ColorSpace* color_space,
|
||||
SpsVuiRewriter::ParseResult& out_vui_rewritten) {
|
||||
// Read.
|
||||
uint32_t video_format = 5; // H264 default: unspecified
|
||||
uint32_t video_full_range_flag = 0; // H264 default: limited
|
||||
uint32_t colour_description_present_flag = 0;
|
||||
uint8_t colour_primaries = 3; // H264 default: unspecified
|
||||
uint8_t transfer_characteristics = 3; // H264 default: unspecified
|
||||
uint8_t matrix_coefficients = 3; // H264 default: unspecified
|
||||
uint32_t video_signal_type_present_flag = source.ReadBit();
|
||||
if (video_signal_type_present_flag) {
|
||||
video_format = source.ReadBits(3);
|
||||
video_full_range_flag = source.ReadBit();
|
||||
colour_description_present_flag = source.ReadBit();
|
||||
if (colour_description_present_flag) {
|
||||
colour_primaries = source.Read<uint8_t>();
|
||||
transfer_characteristics = source.Read<uint8_t>();
|
||||
matrix_coefficients = source.Read<uint8_t>();
|
||||
}
|
||||
}
|
||||
RETURN_FALSE_ON_FAIL(source.Ok());
|
||||
|
||||
// Update.
|
||||
uint32_t video_signal_type_present_flag_override =
|
||||
video_signal_type_present_flag;
|
||||
uint32_t video_format_override = video_format;
|
||||
uint32_t video_full_range_flag_override = video_full_range_flag;
|
||||
uint32_t colour_description_present_flag_override =
|
||||
colour_description_present_flag;
|
||||
uint8_t colour_primaries_override = colour_primaries;
|
||||
uint8_t transfer_characteristics_override = transfer_characteristics;
|
||||
uint8_t matrix_coefficients_override = matrix_coefficients;
|
||||
if (color_space) {
|
||||
if (IsDefaultColorSpace(*color_space)) {
|
||||
video_signal_type_present_flag_override = 0;
|
||||
} else {
|
||||
video_signal_type_present_flag_override = 1;
|
||||
video_format_override = 5; // unspecified
|
||||
|
||||
if (color_space->range() == ColorSpace::RangeID::kFull) {
|
||||
video_full_range_flag_override = 1;
|
||||
} else {
|
||||
// ColorSpace::RangeID::kInvalid and kDerived are treated as limited.
|
||||
video_full_range_flag_override = 0;
|
||||
}
|
||||
|
||||
colour_description_present_flag_override =
|
||||
color_space->primaries() != ColorSpace::PrimaryID::kUnspecified ||
|
||||
color_space->transfer() != ColorSpace::TransferID::kUnspecified ||
|
||||
color_space->matrix() != ColorSpace::MatrixID::kUnspecified;
|
||||
colour_primaries_override =
|
||||
static_cast<uint8_t>(color_space->primaries());
|
||||
transfer_characteristics_override =
|
||||
static_cast<uint8_t>(color_space->transfer());
|
||||
matrix_coefficients_override =
|
||||
static_cast<uint8_t>(color_space->matrix());
|
||||
}
|
||||
}
|
||||
|
||||
// Write.
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteBits(video_signal_type_present_flag_override, 1));
|
||||
if (video_signal_type_present_flag_override) {
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteBits(video_format_override, 3));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteBits(video_full_range_flag_override, 1));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteBits(colour_description_present_flag_override, 1));
|
||||
if (colour_description_present_flag_override) {
|
||||
RETURN_FALSE_ON_FAIL(destination.WriteUInt8(colour_primaries_override));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteUInt8(transfer_characteristics_override));
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
destination.WriteUInt8(matrix_coefficients_override));
|
||||
}
|
||||
}
|
||||
|
||||
if (video_signal_type_present_flag_override !=
|
||||
video_signal_type_present_flag ||
|
||||
video_format_override != video_format ||
|
||||
video_full_range_flag_override != video_full_range_flag ||
|
||||
colour_description_present_flag_override !=
|
||||
colour_description_present_flag ||
|
||||
colour_primaries_override != colour_primaries ||
|
||||
transfer_characteristics_override != transfer_characteristics ||
|
||||
matrix_coefficients_override != matrix_coefficients) {
|
||||
out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyRemainingBits(BitstreamReader& source,
|
||||
rtc::BitBufferWriter& destination) {
|
||||
// Try to get at least the destination aligned.
|
||||
if (source.RemainingBitCount() > 0 && source.RemainingBitCount() % 8 != 0) {
|
||||
size_t misaligned_bits = source.RemainingBitCount() % 8;
|
||||
CopyBits(misaligned_bits, source, destination);
|
||||
}
|
||||
while (source.RemainingBitCount() > 0) {
|
||||
int count = std::min(32, source.RemainingBitCount());
|
||||
CopyBits(count, source, destination);
|
||||
}
|
||||
// TODO(noahric): The last byte could be all zeroes now, which we should just
|
||||
// strip.
|
||||
return source.Ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 COMMON_VIDEO_H264_SPS_VUI_REWRITER_H_
|
||||
#define COMMON_VIDEO_H264_SPS_VUI_REWRITER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video/color_space.h"
|
||||
#include "common_video/h264/sps_parser.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class that can parse an SPS+VUI and if necessary creates a copy with
|
||||
// updated parameters.
|
||||
// The rewriter disables frame buffering. This should force decoders to deliver
|
||||
// decoded frame immediately and, thus, reduce latency.
|
||||
// The rewriter updates video signal type parameters if external parameters are
|
||||
// provided.
|
||||
class SpsVuiRewriter : private SpsParser {
|
||||
public:
|
||||
enum class ParseResult { kFailure, kVuiOk, kVuiRewritten };
|
||||
enum class Direction { kIncoming, kOutgoing };
|
||||
|
||||
// Parses an SPS block and if necessary copies it and rewrites the VUI.
|
||||
// Returns kFailure on failure, kParseOk if parsing succeeded and no update
|
||||
// was necessary and kParsedAndModified if an updated copy of buffer was
|
||||
// written to destination. destination may be populated with some data even if
|
||||
// no rewrite was necessary, but the end offset should remain unchanged.
|
||||
// Unless parsing fails, the sps parameter will be populated with the parsed
|
||||
// SPS state. This function assumes that any previous headers
|
||||
// (NALU start, type, Stap-A, etc) have already been parsed and that RBSP
|
||||
// decoding has been performed.
|
||||
static ParseResult ParseAndRewriteSps(
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
absl::optional<SpsParser::SpsState>* sps,
|
||||
const ColorSpace* color_space,
|
||||
rtc::Buffer* destination,
|
||||
Direction Direction);
|
||||
|
||||
// Parses NAL units from `buffer`, strips AUD blocks and rewrites VUI in SPS
|
||||
// blocks if necessary.
|
||||
static rtc::Buffer ParseOutgoingBitstreamAndRewrite(
|
||||
rtc::ArrayView<const uint8_t> buffer,
|
||||
const ColorSpace* color_space);
|
||||
|
||||
private:
|
||||
static ParseResult ParseAndRewriteSps(
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
absl::optional<SpsParser::SpsState>* sps,
|
||||
const ColorSpace* color_space,
|
||||
rtc::Buffer* destination);
|
||||
|
||||
static void UpdateStats(ParseResult result, Direction direction);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H264_SPS_VUI_REWRITER_H_
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 "common_video/h265/h265_bitstream_parser.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h265/h265_common.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "common_video/h265/legacy_bit_buffer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxAbsQpDeltaValue = 51;
|
||||
const int kMinQpValue = 0;
|
||||
const int kMaxQpValue = 51;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
#define RETURN_ON_FAIL(x, res) \
|
||||
if (!(x)) { \
|
||||
RTC_LOG_F(LS_ERROR) << "FAILED: " #x; \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define RETURN_INV_ON_FAIL(x) RETURN_ON_FAIL(x, kInvalidStream)
|
||||
|
||||
H265BitstreamParser::H265BitstreamParser() {}
|
||||
H265BitstreamParser::~H265BitstreamParser() {}
|
||||
|
||||
H265BitstreamParser::Result H265BitstreamParser::ParseNonParameterSetNalu(
|
||||
const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type) {
|
||||
if (!sps_ || !pps_)
|
||||
return kInvalidStream;
|
||||
|
||||
last_slice_qp_delta_ = absl::nullopt;
|
||||
const std::vector<uint8_t> slice_rbsp =
|
||||
H265::ParseRbsp(source, source_length);
|
||||
if (slice_rbsp.size() < H265::kNaluTypeSize)
|
||||
return kInvalidStream;
|
||||
|
||||
rtc::BitBuffer slice_reader(slice_rbsp.data() + H265::kNaluTypeSize,
|
||||
slice_rbsp.size() - H265::kNaluTypeSize);
|
||||
// Check to see if this is an IDR slice, which has an extra field to parse
|
||||
// out.
|
||||
//bool is_idr = (source[0] & 0x0F) == H265::NaluType::kIdr;
|
||||
//uint8_t nal_ref_idc = (source[0] & 0x60) >> 5;
|
||||
uint32_t golomb_tmp;
|
||||
uint32_t bits_tmp;
|
||||
|
||||
// first_slice_segment_in_pic_flag: u(1)
|
||||
uint32_t first_slice_segment_in_pic_flag = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&first_slice_segment_in_pic_flag, 1));
|
||||
if (H265::NaluType::kBlaWLp <= nalu_type &&
|
||||
nalu_type <= H265::NaluType::kRsvIrapVcl23) {
|
||||
// no_output_of_prior_pics_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
// slice_pic_parameter_set_id: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
uint32_t dependent_slice_segment_flag = 0;
|
||||
if (first_slice_segment_in_pic_flag == 0) {
|
||||
if (pps_->dependent_slice_segments_enabled_flag) {
|
||||
// dependent_slice_segment_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&dependent_slice_segment_flag, 1));
|
||||
}
|
||||
|
||||
// slice_segment_address: u(v)
|
||||
int32_t log2_ctb_size_y = sps_->log2_min_luma_coding_block_size_minus3 + 3 + sps_->log2_diff_max_min_luma_coding_block_size;
|
||||
uint32_t ctb_size_y = 1 << log2_ctb_size_y;
|
||||
uint32_t pic_width_in_ctbs_y = sps_->pic_width_in_luma_samples / ctb_size_y;
|
||||
if(sps_->pic_width_in_luma_samples % ctb_size_y)
|
||||
pic_width_in_ctbs_y++;
|
||||
|
||||
uint32_t pic_height_in_ctbs_y = sps_->pic_height_in_luma_samples / ctb_size_y;
|
||||
if(sps_->pic_height_in_luma_samples % ctb_size_y)
|
||||
pic_height_in_ctbs_y++;
|
||||
|
||||
uint32_t slice_segment_address_bits = H265::Log2(pic_height_in_ctbs_y * pic_width_in_ctbs_y);
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, slice_segment_address_bits));
|
||||
}
|
||||
|
||||
if (dependent_slice_segment_flag == 0) {
|
||||
for (uint32_t i = 0; i < pps_->num_extra_slice_header_bits; i++) {
|
||||
// slice_reserved_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
// slice_type: ue(v)
|
||||
uint32_t slice_type = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&slice_type));
|
||||
if (pps_->output_flag_present_flag) {
|
||||
// pic_output_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
if (sps_->separate_colour_plane_flag) {
|
||||
// colour_plane_id: u(2)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2));
|
||||
}
|
||||
uint32_t num_long_term_sps = 0;
|
||||
uint32_t num_long_term_pics = 0;
|
||||
std::vector<uint32_t> lt_idx_sps;
|
||||
std::vector<uint32_t> used_by_curr_pic_lt_flag;
|
||||
uint32_t short_term_ref_pic_set_sps_flag = 0;
|
||||
uint32_t short_term_ref_pic_set_idx = 0;
|
||||
H265SpsParser::ShortTermRefPicSet short_term_ref_pic_set;
|
||||
uint32_t slice_temporal_mvp_enabled_flag = 0;
|
||||
if (nalu_type != H265::NaluType::kIdrWRadl && nalu_type != H265::NaluType::kIdrNLp) {
|
||||
// slice_pic_order_cnt_lsb: u(v)
|
||||
uint32_t slice_pic_order_cnt_lsb_bits = sps_->log2_max_pic_order_cnt_lsb_minus4 + 4;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, slice_pic_order_cnt_lsb_bits));
|
||||
// short_term_ref_pic_set_sps_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&short_term_ref_pic_set_sps_flag, 1));
|
||||
if (!short_term_ref_pic_set_sps_flag) {
|
||||
absl::optional<H265SpsParser::ShortTermRefPicSet> ref_pic_set
|
||||
= H265SpsParser::ParseShortTermRefPicSet(sps_->num_short_term_ref_pic_sets,
|
||||
sps_->num_short_term_ref_pic_sets, sps_->short_term_ref_pic_set, *sps_, &slice_reader);
|
||||
if (ref_pic_set) {
|
||||
short_term_ref_pic_set = *ref_pic_set;
|
||||
} else {
|
||||
return kInvalidStream;
|
||||
}
|
||||
} else if (sps_->num_short_term_ref_pic_sets > 1) {
|
||||
// short_term_ref_pic_set_idx: u(v)
|
||||
uint32_t short_term_ref_pic_set_idx_bits = H265::Log2(sps_->num_short_term_ref_pic_sets);
|
||||
if ((1 << short_term_ref_pic_set_idx_bits) < sps_->num_short_term_ref_pic_sets) {
|
||||
short_term_ref_pic_set_idx_bits++;
|
||||
}
|
||||
if (short_term_ref_pic_set_idx_bits > 0) {
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&short_term_ref_pic_set_idx, short_term_ref_pic_set_idx_bits));
|
||||
}
|
||||
}
|
||||
if (sps_->long_term_ref_pics_present_flag) {
|
||||
if (sps_->num_long_term_ref_pics_sps > 0) {
|
||||
// num_long_term_sps: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&num_long_term_sps));
|
||||
}
|
||||
// num_long_term_sps: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&num_long_term_pics));
|
||||
lt_idx_sps.resize(num_long_term_sps + num_long_term_pics, 0);
|
||||
used_by_curr_pic_lt_flag.resize(num_long_term_sps + num_long_term_pics, 0);
|
||||
for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) {
|
||||
if (i < num_long_term_sps) {
|
||||
if (sps_->num_long_term_ref_pics_sps > 1) {
|
||||
// lt_idx_sps: u(v)
|
||||
uint32_t lt_idx_sps_bits = H265::Log2(sps_->num_long_term_ref_pics_sps);
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(<_idx_sps[i], lt_idx_sps_bits));
|
||||
}
|
||||
} else {
|
||||
// poc_lsb_lt: u(v)
|
||||
uint32_t poc_lsb_lt_bits = sps_->log2_max_pic_order_cnt_lsb_minus4 + 4;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, poc_lsb_lt_bits));
|
||||
// used_by_curr_pic_lt_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&used_by_curr_pic_lt_flag[i], 1));
|
||||
}
|
||||
// delta_poc_msb_present_flag: u(1)
|
||||
uint32_t delta_poc_msb_present_flag = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&delta_poc_msb_present_flag, 1));
|
||||
if (delta_poc_msb_present_flag) {
|
||||
// delta_poc_msb_cycle_lt: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sps_->sps_temporal_mvp_enabled_flag) {
|
||||
// slice_temporal_mvp_enabled_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&slice_temporal_mvp_enabled_flag, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (sps_->sample_adaptive_offset_enabled_flag) {
|
||||
// slice_sao_luma_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
uint32_t chroma_array_type = sps_->separate_colour_plane_flag == 0 ? sps_->chroma_format_idc : 0;
|
||||
if (chroma_array_type != 0) {
|
||||
// slice_sao_chroma_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (slice_type == H265::SliceType::kP || slice_type == H265::SliceType::kB) {
|
||||
// num_ref_idx_active_override_flag: u(1)
|
||||
uint32_t num_ref_idx_active_override_flag = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&num_ref_idx_active_override_flag, 1));
|
||||
uint32_t num_ref_idx_l0_active_minus1 = pps_->num_ref_idx_l0_default_active_minus1;
|
||||
uint32_t num_ref_idx_l1_active_minus1 = pps_->num_ref_idx_l1_default_active_minus1;
|
||||
if (num_ref_idx_active_override_flag) {
|
||||
// num_ref_idx_l0_active_minus1: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&num_ref_idx_l0_active_minus1));
|
||||
if (slice_type == H265::SliceType::kB) {
|
||||
// num_ref_idx_l1_active_minus1: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&num_ref_idx_l1_active_minus1));
|
||||
}
|
||||
}
|
||||
uint32_t num_pic_total_curr = CalcNumPocTotalCurr(
|
||||
num_long_term_sps, num_long_term_pics, lt_idx_sps,
|
||||
used_by_curr_pic_lt_flag, short_term_ref_pic_set_sps_flag,
|
||||
short_term_ref_pic_set_idx, short_term_ref_pic_set);
|
||||
if (pps_->lists_modification_present_flag && num_pic_total_curr > 1) {
|
||||
// ref_pic_lists_modification()
|
||||
uint32_t list_entry_bits = H265::Log2(num_pic_total_curr);
|
||||
if ((1 << list_entry_bits) < num_pic_total_curr) {
|
||||
list_entry_bits++;
|
||||
}
|
||||
// ref_pic_list_modification_flag_l0: u(1)
|
||||
uint32_t ref_pic_list_modification_flag_l0 = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&ref_pic_list_modification_flag_l0, 1));
|
||||
if (ref_pic_list_modification_flag_l0) {
|
||||
for (uint32_t i = 0; i < num_ref_idx_l0_active_minus1; i++) {
|
||||
// list_entry_l0: u(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, list_entry_bits));
|
||||
}
|
||||
}
|
||||
if (slice_type == H265::SliceType::kB) {
|
||||
// ref_pic_list_modification_flag_l1: u(1)
|
||||
uint32_t ref_pic_list_modification_flag_l1 = 0;
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&ref_pic_list_modification_flag_l1, 1));
|
||||
if (ref_pic_list_modification_flag_l1) {
|
||||
for (uint32_t i = 0; i < num_ref_idx_l1_active_minus1; i++) {
|
||||
// list_entry_l1: u(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, list_entry_bits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slice_type == H265::SliceType::kB) {
|
||||
// mvd_l1_zero_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
if (pps_->cabac_init_present_flag) {
|
||||
// cabac_init_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
if (slice_temporal_mvp_enabled_flag) {
|
||||
uint32_t collocated_from_l0_flag = 0;
|
||||
if (slice_type == H265::SliceType::kB) {
|
||||
// collocated_from_l0_flag: u(1)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadBits(&collocated_from_l0_flag, 1));
|
||||
}
|
||||
if ((collocated_from_l0_flag && num_ref_idx_l0_active_minus1 > 0)
|
||||
|| (!collocated_from_l0_flag && num_ref_idx_l1_active_minus1 > 0)) {
|
||||
// collocated_ref_idx: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
}
|
||||
if ((pps_->weighted_pred_flag && slice_type == H265::SliceType::kP)
|
||||
|| (pps_->weighted_bipred_flag && slice_type == H265::SliceType::kB)) {
|
||||
// pred_weight_table()
|
||||
// TODO(piasy): Do we need support for pred_weight_table()?
|
||||
RTC_LOG(LS_ERROR) << "Streams with pred_weight_table unsupported.";
|
||||
return kUnsupportedStream;
|
||||
}
|
||||
// five_minus_max_num_merge_cand: ue(v)
|
||||
RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
// TODO(piasy): motion_vector_resolution_control_idc?
|
||||
}
|
||||
}
|
||||
|
||||
// slice_qp_delta: se(v)
|
||||
int32_t last_slice_qp_delta;
|
||||
RETURN_INV_ON_FAIL(
|
||||
slice_reader.ReadSignedExponentialGolomb(&last_slice_qp_delta));
|
||||
if (abs(last_slice_qp_delta) > kMaxAbsQpDeltaValue) {
|
||||
// Something has gone wrong, and the parsed value is invalid.
|
||||
RTC_LOG(LS_WARNING) << "Parsed QP value out of range.";
|
||||
return kInvalidStream;
|
||||
}
|
||||
|
||||
last_slice_qp_delta_ = last_slice_qp_delta;
|
||||
|
||||
return kOk;
|
||||
}
|
||||
|
||||
uint32_t H265BitstreamParser::CalcNumPocTotalCurr(
|
||||
uint32_t num_long_term_sps, uint32_t num_long_term_pics,
|
||||
const std::vector<uint32_t> lt_idx_sps,
|
||||
const std::vector<uint32_t> used_by_curr_pic_lt_flag,
|
||||
uint32_t short_term_ref_pic_set_sps_flag,
|
||||
uint32_t short_term_ref_pic_set_idx,
|
||||
const H265SpsParser::ShortTermRefPicSet& short_term_ref_pic_set) {
|
||||
uint32_t num_poc_total_curr = 0;
|
||||
uint32_t curr_sps_idx;
|
||||
|
||||
bool used_by_curr_pic_lt[16];
|
||||
uint32_t num_long_term = num_long_term_sps + num_long_term_pics;
|
||||
|
||||
for (uint32_t i = 0; i < num_long_term; i++) {
|
||||
if (i < num_long_term_sps) {
|
||||
used_by_curr_pic_lt[i] = sps_->used_by_curr_pic_lt_sps_flag[lt_idx_sps[i]];
|
||||
} else {
|
||||
used_by_curr_pic_lt[i] = used_by_curr_pic_lt_flag[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (short_term_ref_pic_set_sps_flag) {
|
||||
curr_sps_idx = short_term_ref_pic_set_idx;
|
||||
} else {
|
||||
curr_sps_idx = sps_->num_short_term_ref_pic_sets;
|
||||
}
|
||||
|
||||
if (sps_->short_term_ref_pic_set.size() <= curr_sps_idx) {
|
||||
if (curr_sps_idx != 0 || short_term_ref_pic_set_sps_flag) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const H265SpsParser::ShortTermRefPicSet* ref_pic_set;
|
||||
if (curr_sps_idx < sps_->short_term_ref_pic_set.size()) {
|
||||
ref_pic_set = &(sps_->short_term_ref_pic_set[curr_sps_idx]);
|
||||
} else {
|
||||
ref_pic_set = &short_term_ref_pic_set;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < ref_pic_set->num_negative_pics; i++) {
|
||||
if (ref_pic_set->used_by_curr_pic_s0_flag[i]) {
|
||||
num_poc_total_curr++;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < ref_pic_set->num_positive_pics; i++) {
|
||||
if (ref_pic_set->used_by_curr_pic_s1_flag[i]) {
|
||||
num_poc_total_curr++;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) {
|
||||
if (used_by_curr_pic_lt[i]) {
|
||||
num_poc_total_curr++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_poc_total_curr;
|
||||
}
|
||||
|
||||
void H265BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) {
|
||||
H265::NaluType nalu_type = H265::ParseNaluType(slice[0]);
|
||||
if (nalu_type == H265::NaluType::kSps) {
|
||||
sps_ = H265SpsParser::ParseSps(slice + H265::kNaluTypeSize,
|
||||
length - H265::kNaluTypeSize);
|
||||
if (!sps_) {
|
||||
RTC_LOG(LS_WARNING) << "Unable to parse SPS from H265 bitstream.";
|
||||
}
|
||||
} else if (nalu_type == H265::NaluType::kPps) {
|
||||
pps_ = H265PpsParser::ParsePps(slice + H265::kNaluTypeSize,
|
||||
length - H265::kNaluTypeSize);
|
||||
if (!pps_) {
|
||||
RTC_LOG(LS_WARNING) << "Unable to parse PPS from H265 bitstream.";
|
||||
}
|
||||
} else if (nalu_type <= H265::NaluType::kRsvIrapVcl23) {
|
||||
Result res = ParseNonParameterSetNalu(slice, length, nalu_type);
|
||||
if (res != kOk) {
|
||||
RTC_LOG(LS_INFO) << "Failed to parse bitstream. Error: " << res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void H265BitstreamParser::ParseBitstream(const uint8_t* bitstream,
|
||||
size_t length) {
|
||||
std::vector<H265::NaluIndex> nalu_indices =
|
||||
H265::FindNaluIndices(bitstream, length);
|
||||
for (const H265::NaluIndex& index : nalu_indices)
|
||||
ParseSlice(&bitstream[index.payload_start_offset], index.payload_size);
|
||||
}
|
||||
|
||||
bool H265BitstreamParser::GetLastSliceQp(int* qp) const {
|
||||
if (!last_slice_qp_delta_ || !pps_) {
|
||||
return false;
|
||||
}
|
||||
const int parsed_qp = 26 + pps_->pic_init_qp_minus26 + *last_slice_qp_delta_;
|
||||
if (parsed_qp < kMinQpValue || parsed_qp > kMaxQpValue) {
|
||||
RTC_LOG(LS_ERROR) << "Parsed invalid QP from bitstream.";
|
||||
return false;
|
||||
}
|
||||
*qp = parsed_qp;
|
||||
return true;
|
||||
}
|
||||
|
||||
void H265BitstreamParser::ParseBitstream(
|
||||
rtc::ArrayView<const uint8_t> bitstream) {
|
||||
ParseBitstream(bitstream.data(), bitstream.size());
|
||||
}
|
||||
|
||||
absl::optional<int> H265BitstreamParser::GetLastSliceQp() const {
|
||||
int qp;
|
||||
bool success = GetLastSliceQp(&qp);
|
||||
return success ? absl::optional<int>(qp) : absl::nullopt;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 COMMON_VIDEO_H265_H265_BITSTREAM_PARSER_H_
|
||||
#define COMMON_VIDEO_H265_H265_BITSTREAM_PARSER_H_
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video_codecs/bitstream_parser.h"
|
||||
#include "common_video/h265/h265_pps_parser.h"
|
||||
#include "common_video/h265/h265_sps_parser.h"
|
||||
#include "common_video/h265/h265_vps_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Stateful H265 bitstream parser (due to SPS/PPS). Used to parse out QP values
|
||||
// from the bitstream.
|
||||
// TODO(pbos): Unify with RTP SPS parsing and only use one H265 parser.
|
||||
// TODO(pbos): If/when this gets used on the receiver side CHECKs must be
|
||||
// removed and gracefully abort as we have no control over receive-side
|
||||
// bitstreams.
|
||||
class H265BitstreamParser : public BitstreamParser {
|
||||
public:
|
||||
H265BitstreamParser();
|
||||
~H265BitstreamParser() override;
|
||||
|
||||
// These are here for backwards-compatability for the time being.
|
||||
void ParseBitstream(const uint8_t* bitstream, size_t length);
|
||||
bool GetLastSliceQp(int* qp) const;
|
||||
|
||||
// New interface.
|
||||
void ParseBitstream(rtc::ArrayView<const uint8_t> bitstream) override;
|
||||
absl::optional<int> GetLastSliceQp() const override;
|
||||
|
||||
protected:
|
||||
enum Result {
|
||||
kOk,
|
||||
kInvalidStream,
|
||||
kUnsupportedStream,
|
||||
};
|
||||
void ParseSlice(const uint8_t* slice, size_t length);
|
||||
Result ParseNonParameterSetNalu(const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type);
|
||||
|
||||
uint32_t CalcNumPocTotalCurr(uint32_t num_long_term_sps,
|
||||
uint32_t num_long_term_pics,
|
||||
const std::vector<uint32_t> lt_idx_sps,
|
||||
const std::vector<uint32_t> used_by_curr_pic_lt_flag,
|
||||
uint32_t short_term_ref_pic_set_sps_flag,
|
||||
uint32_t short_term_ref_pic_set_idx,
|
||||
const H265SpsParser::ShortTermRefPicSet& short_term_ref_pic_set);
|
||||
|
||||
// SPS/PPS state, updated when parsing new SPS/PPS, used to parse slices.
|
||||
absl::optional<H265SpsParser::SpsState> sps_;
|
||||
absl::optional<H265PpsParser::PpsState> pps_;
|
||||
|
||||
// Last parsed slice QP.
|
||||
absl::optional<int32_t> last_slice_qp_delta_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H265_H265_BITSTREAM_PARSER_H_
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 "common_video/h265/h265_common.h"
|
||||
|
||||
#include "common_video/h264/h264_common.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace H265 {
|
||||
|
||||
constexpr uint8_t kNaluTypeMask = 0x7E;
|
||||
|
||||
std::vector<NaluIndex> FindNaluIndices(const uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
std::vector<H264::NaluIndex> indices =
|
||||
H264::FindNaluIndices(buffer, buffer_size);
|
||||
std::vector<NaluIndex> results;
|
||||
for (auto& index : indices) {
|
||||
results.push_back(
|
||||
{index.start_offset, index.payload_start_offset, index.payload_size});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
NaluType ParseNaluType(uint8_t data) {
|
||||
return static_cast<NaluType>((data & kNaluTypeMask) >> 1);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ParseRbsp(const uint8_t* data, size_t length) {
|
||||
return H264::ParseRbsp(data, length);
|
||||
}
|
||||
|
||||
void WriteRbsp(const uint8_t* bytes, size_t length, rtc::Buffer* destination) {
|
||||
H264::WriteRbsp(bytes, length, destination);
|
||||
}
|
||||
|
||||
uint32_t Log2Ceiling(uint32_t value) {
|
||||
// When n == 0, we want the function to return -1.
|
||||
// When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
|
||||
// why the statement below starts with (n ? 32 : -1).
|
||||
return (value ? 32 : -1) - WebRtcVideo_CountLeadingZeros32(value - 1);
|
||||
}
|
||||
|
||||
uint32_t Log2(uint32_t value) {
|
||||
uint32_t result = 0;
|
||||
// If value is not a power of two an additional bit is required
|
||||
// to account for the ceil() of log2() below.
|
||||
if ((value & (value - 1)) != 0) {
|
||||
++result;
|
||||
}
|
||||
while (value > 0) {
|
||||
value >>= 1;
|
||||
++result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace H265
|
||||
} // namespace webrtc
|
||||
115
TMessagesProj/jni/voip/webrtc/common_video/h265/h265_common.h
Normal file
115
TMessagesProj/jni/voip/webrtc/common_video/h265/h265_common.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 COMMON_VIDEO_H265_H265_COMMON_H_
|
||||
#define COMMON_VIDEO_H265_H265_COMMON_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h265/h265_inline.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace H265 {
|
||||
// The size of a full NALU start sequence {0 0 0 1}, used for the first NALU
|
||||
// of an access unit, and for SPS and PPS blocks.
|
||||
constexpr size_t kNaluLongStartSequenceSize = 4;
|
||||
|
||||
// The size of a shortened NALU start sequence {0 0 1}, that may be used if
|
||||
// not the first NALU of an access unit or an SPS or PPS block.
|
||||
constexpr size_t kNaluShortStartSequenceSize = 3;
|
||||
|
||||
// The size of the NALU header byte (2).
|
||||
constexpr size_t kNaluHeaderSize = 2;
|
||||
|
||||
// The size of the NALU type byte (2).
|
||||
const size_t kNaluTypeSize = 2;
|
||||
|
||||
// Type description of 0-40 is defined in Table7-1 of the H.265 spec
|
||||
// Type desciption of 48-49 is defined in section 4.4.2 and 4.4.3 of RFC7798
|
||||
enum NaluType : uint8_t {
|
||||
kTrailN = 0,
|
||||
kTrailR = 1,
|
||||
kTsaN = 2,
|
||||
kTsaR = 3,
|
||||
kStsaN = 4,
|
||||
kStsaR = 5,
|
||||
kRadlN = 6,
|
||||
kRadlR = 7,
|
||||
kBlaWLp = 16,
|
||||
kBlaWRadl = 17,
|
||||
kBlaNLp = 18,
|
||||
kIdrWRadl = 19,
|
||||
kIdrNLp = 20,
|
||||
kCra = 21,
|
||||
kRsvIrapVcl23 = 23,
|
||||
kVps = 32,
|
||||
kSps = 33,
|
||||
kPps = 34,
|
||||
kAud = 35,
|
||||
kPrefixSei = 39,
|
||||
kSuffixSei = 40,
|
||||
// Aggregation packets, refer to section 4.4.2 in RFC 7798.
|
||||
kAp = 48,
|
||||
// Fragmentation units, refer to section 4.4.3 in RFC 7798.
|
||||
kFu = 49,
|
||||
// PACI packets, refer to section 4.4.4 in RFC 7798.
|
||||
kPaci = 50
|
||||
};
|
||||
|
||||
// Slice type definition. See table 7-7 of the H.265 spec
|
||||
enum SliceType : uint8_t { kB = 0, kP = 1, kI = 2 };
|
||||
|
||||
struct NaluIndex {
|
||||
// Start index of NALU, including start sequence.
|
||||
size_t start_offset = 0;
|
||||
// Start index of NALU payload, typically type header.
|
||||
size_t payload_start_offset = 0;
|
||||
// Length of NALU payload, in bytes, counting from payload_start_offset.
|
||||
size_t payload_size = 0;
|
||||
};
|
||||
|
||||
// Returns a vector of the NALU indices in the given buffer.
|
||||
std::vector<NaluIndex> FindNaluIndices(const uint8_t* buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
// Get the NAL type from the header byte immediately following start sequence.
|
||||
NaluType ParseNaluType(uint8_t data);
|
||||
|
||||
// Methods for parsing and writing RBSP. See section 7.4.2 of the H.265 spec.
|
||||
//
|
||||
// The following sequences are illegal, and need to be escaped when encoding:
|
||||
// 00 00 00 -> 00 00 03 00
|
||||
// 00 00 01 -> 00 00 03 01
|
||||
// 00 00 02 -> 00 00 03 02
|
||||
// And things in the source that look like the emulation byte pattern (00 00 03)
|
||||
// need to have an extra emulation byte added, so it's removed when decoding:
|
||||
// 00 00 03 -> 00 00 03 03
|
||||
//
|
||||
// Decoding is simply a matter of finding any 00 00 03 sequence and removing
|
||||
// the 03 emulation byte.
|
||||
|
||||
// Parse the given data and remove any emulation byte escaping.
|
||||
std::vector<uint8_t> ParseRbsp(const uint8_t* data, size_t length);
|
||||
|
||||
// Write the given data to the destination buffer, inserting and emulation
|
||||
// bytes in order to escape any data the could be interpreted as a start
|
||||
// sequence.
|
||||
void WriteRbsp(const uint8_t* bytes, size_t length, rtc::Buffer* destination);
|
||||
|
||||
uint32_t Log2Ceiling(uint32_t value);
|
||||
uint32_t Log2(uint32_t value);
|
||||
|
||||
} // namespace H265
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H265_H265_COMMON_H_
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 "common_video/h265/h265_inline.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Table used by WebRtcVideo_CountLeadingZeros32_NotBuiltin. For each uint32_t n
|
||||
// that's a sequence of 0 bits followed by a sequence of 1 bits, the entry at
|
||||
// index (n * 0x8c0b2891) >> 26 in this table gives the number of zero bits in
|
||||
// n.
|
||||
const int8_t kWebRtcVideo_CountLeadingZeros32_Table[64] = {
|
||||
32, 8, 17, -1, -1, 14, -1, -1, -1, 20, -1, -1, -1, 28, -1, 18,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 26, 25, 24,
|
||||
4, 11, 23, 31, 3, 7, 10, 16, 22, 30, -1, -1, 2, 6, 13, 9,
|
||||
-1, 15, -1, 21, -1, 29, 19, -1, -1, -1, -1, -1, 1, 27, 5, 12,
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// This header file includes the inline functions in H265 parser.
|
||||
|
||||
#ifndef COMMON_VIDEO_H265_H265_INLINE_H_
|
||||
#define COMMON_VIDEO_H265_H265_INLINE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rtc_base/compile_assert_c.h"
|
||||
|
||||
extern const int8_t kWebRtcVideo_CountLeadingZeros32_Table[64];
|
||||
|
||||
static __inline int WebRtcVideo_CountLeadingZeros32_NotBuiltin(uint32_t n) {
|
||||
// Normalize n by rounding up to the nearest number that is a sequence of 0
|
||||
// bits followed by a sequence of 1 bits. This number has the same number of
|
||||
// leading zeros as the original n. There are exactly 33 such values.
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
|
||||
// Multiply the modified n with a constant selected (by exhaustive search)
|
||||
// such that each of the 33 possible values of n give a product whose 6 most
|
||||
// significant bits are unique. Then look up the answer in the table.
|
||||
return kWebRtcVideo_CountLeadingZeros32_Table[(n * 0x8c0b2891) >> 26];
|
||||
}
|
||||
|
||||
// Returns the number of leading zero bits in the argument.
|
||||
static __inline int WebRtcVideo_CountLeadingZeros32(uint32_t n) {
|
||||
#ifdef __GNUC__
|
||||
RTC_COMPILE_ASSERT(sizeof(unsigned int) == sizeof(uint32_t));
|
||||
return n == 0 ? 32 : __builtin_clz(n);
|
||||
#else
|
||||
return WebRtcVideo_CountLeadingZeros32_NotBuiltin(n);
|
||||
#endif
|
||||
}
|
||||
#endif // COMMON_VIDEO_H265_H265_INLINE_H_
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 "common_video/h265/h265_pps_parser.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h265/h265_common.h"
|
||||
#include "common_video/h265/h265_sps_parser.h"
|
||||
#include "common_video/h265/legacy_bit_buffer.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
#define RETURN_EMPTY_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
return absl::nullopt; \
|
||||
}
|
||||
|
||||
namespace {
|
||||
const int kMaxPicInitQpDeltaValue = 25;
|
||||
const int kMinPicInitQpDeltaValue = -26;
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// General note: this is based off the 06/2019 version of the H.265 standard.
|
||||
// You can find it on this page:
|
||||
// http://www.itu.int/rec/T-REC-H.265
|
||||
|
||||
absl::optional<H265PpsParser::PpsState> H265PpsParser::ParsePps(
|
||||
const uint8_t* data,
|
||||
size_t length) {
|
||||
// First, parse out rbsp, which is basically the source buffer minus emulation
|
||||
// bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in
|
||||
// section 7.3.1.1 of the H.265 standard.
|
||||
std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data, length);
|
||||
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
|
||||
return ParseInternal(&bit_buffer);
|
||||
}
|
||||
|
||||
bool H265PpsParser::ParsePpsIds(const uint8_t* data,
|
||||
size_t length,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id) {
|
||||
RTC_DCHECK(pps_id);
|
||||
RTC_DCHECK(sps_id);
|
||||
// First, parse out rbsp, which is basically the source buffer minus emulation
|
||||
// bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in
|
||||
// section 7.3.1.1 of the H.265 standard.
|
||||
std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data, length);
|
||||
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
|
||||
return ParsePpsIdsInternal(&bit_buffer, pps_id, sps_id);
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> H265PpsParser::ParsePpsIdFromSliceSegmentLayerRbsp(
|
||||
const uint8_t* data,
|
||||
size_t length,
|
||||
uint8_t nalu_type) {
|
||||
rtc::BitBuffer slice_reader(data, length);
|
||||
|
||||
// first_slice_segment_in_pic_flag: u(1)
|
||||
uint32_t first_slice_segment_in_pic_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(
|
||||
slice_reader.ReadBits(&first_slice_segment_in_pic_flag, 1));
|
||||
|
||||
if (nalu_type >= H265::NaluType::kBlaWLp &&
|
||||
nalu_type <= H265::NaluType::kRsvIrapVcl23) {
|
||||
// no_output_of_prior_pics_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(slice_reader.ConsumeBits(1));
|
||||
}
|
||||
|
||||
// slice_pic_parameter_set_id: ue(v)
|
||||
uint32_t slice_pic_parameter_set_id = 0;
|
||||
if (!slice_reader.ReadExponentialGolomb(&slice_pic_parameter_set_id))
|
||||
return absl::nullopt;
|
||||
|
||||
return slice_pic_parameter_set_id;
|
||||
}
|
||||
|
||||
absl::optional<H265PpsParser::PpsState> H265PpsParser::ParseInternal(
|
||||
rtc::BitBuffer* bit_buffer) {
|
||||
PpsState pps;
|
||||
|
||||
RETURN_EMPTY_ON_FAIL(ParsePpsIdsInternal(bit_buffer, &pps.id, &pps.sps_id));
|
||||
|
||||
uint32_t bits_tmp;
|
||||
uint32_t golomb_ignored;
|
||||
int32_t signed_golomb_ignored;
|
||||
// dependent_slice_segments_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.dependent_slice_segments_enabled_flag, 1));
|
||||
// output_flag_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.output_flag_present_flag, 1));
|
||||
// num_extra_slice_header_bits: u(3)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.num_extra_slice_header_bits, 3));
|
||||
// sign_data_hiding_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// cabac_init_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.cabac_init_present_flag, 1));
|
||||
// num_ref_idx_l0_default_active_minus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&pps.num_ref_idx_l0_default_active_minus1));
|
||||
// num_ref_idx_l1_default_active_minus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&pps.num_ref_idx_l1_default_active_minus1));
|
||||
// init_qp_minus26: se(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadSignedExponentialGolomb(&pps.pic_init_qp_minus26));
|
||||
// Sanity-check parsed value
|
||||
if (pps.pic_init_qp_minus26 > kMaxPicInitQpDeltaValue ||
|
||||
pps.pic_init_qp_minus26 < kMinPicInitQpDeltaValue) {
|
||||
RETURN_EMPTY_ON_FAIL(false);
|
||||
}
|
||||
// constrained_intra_pred_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// transform_skip_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// cu_qp_delta_enabled_flag: u(1)
|
||||
uint32_t cu_qp_delta_enabled_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&cu_qp_delta_enabled_flag, 1));
|
||||
if (cu_qp_delta_enabled_flag) {
|
||||
// diff_cu_qp_delta_depth: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
}
|
||||
// pps_cb_qp_offset: se(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadSignedExponentialGolomb(&signed_golomb_ignored));
|
||||
// pps_cr_qp_offset: se(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadSignedExponentialGolomb(&signed_golomb_ignored));
|
||||
// pps_slice_chroma_qp_offsets_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// weighted_pred_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.weighted_pred_flag, 1));
|
||||
// weighted_bipred_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.weighted_bipred_flag, 1));
|
||||
// transquant_bypass_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// tiles_enabled_flag: u(1)
|
||||
uint32_t tiles_enabled_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&tiles_enabled_flag, 1));
|
||||
// entropy_coding_sync_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
if (tiles_enabled_flag) {
|
||||
// num_tile_columns_minus1: ue(v)
|
||||
uint32_t num_tile_columns_minus1 = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&num_tile_columns_minus1));
|
||||
// num_tile_rows_minus1: ue(v)
|
||||
uint32_t num_tile_rows_minus1 = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&num_tile_rows_minus1));
|
||||
// uniform_spacing_flag: u(1)
|
||||
uint32_t uniform_spacing_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&uniform_spacing_flag, 1));
|
||||
if (!uniform_spacing_flag) {
|
||||
for (uint32_t i = 0; i < num_tile_columns_minus1; i++) {
|
||||
// column_width_minus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
}
|
||||
for (uint32_t i = 0; i < num_tile_rows_minus1; i++) {
|
||||
// row_height_minus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
}
|
||||
// loop_filter_across_tiles_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
}
|
||||
// pps_loop_filter_across_slices_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// deblocking_filter_control_present_flag: u(1)
|
||||
uint32_t deblocking_filter_control_present_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&deblocking_filter_control_present_flag, 1));
|
||||
if (deblocking_filter_control_present_flag) {
|
||||
// deblocking_filter_override_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
// pps_deblocking_filter_disabled_flag: u(1)
|
||||
uint32_t pps_deblocking_filter_disabled_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps_deblocking_filter_disabled_flag, 1));
|
||||
if (!pps_deblocking_filter_disabled_flag) {
|
||||
// pps_beta_offset_div2: se(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadSignedExponentialGolomb(&signed_golomb_ignored));
|
||||
// pps_tc_offset_div2: se(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadSignedExponentialGolomb(&signed_golomb_ignored));
|
||||
}
|
||||
}
|
||||
// pps_scaling_list_data_present_flag: u(1)
|
||||
uint32_t pps_scaling_list_data_present_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps_scaling_list_data_present_flag, 1));
|
||||
if (pps_scaling_list_data_present_flag) {
|
||||
// scaling_list_data()
|
||||
if (!H265SpsParser::ParseScalingListData(bit_buffer)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
// lists_modification_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&pps.lists_modification_present_flag, 1));
|
||||
// log2_parallel_merge_level_minus2: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// slice_segment_header_extension_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(&bits_tmp, 1));
|
||||
|
||||
return pps;
|
||||
}
|
||||
|
||||
bool H265PpsParser::ParsePpsIdsInternal(rtc::BitBuffer* bit_buffer,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id) {
|
||||
// pic_parameter_set_id: ue(v)
|
||||
if (!bit_buffer->ReadExponentialGolomb(pps_id))
|
||||
return false;
|
||||
// seq_parameter_set_id: ue(v)
|
||||
if (!bit_buffer->ReadExponentialGolomb(sps_id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 COMMON_VIDEO_H265_PPS_PARSER_H_
|
||||
#define COMMON_VIDEO_H265_PPS_PARSER_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace rtc {
|
||||
class BitBuffer;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class for parsing out picture parameter set (PPS) data from a H265 NALU.
|
||||
class H265PpsParser {
|
||||
public:
|
||||
// The parsed state of the PPS. Only some select values are stored.
|
||||
// Add more as they are actually needed.
|
||||
struct PpsState {
|
||||
PpsState() = default;
|
||||
|
||||
uint32_t dependent_slice_segments_enabled_flag = 0;
|
||||
uint32_t cabac_init_present_flag = 0;
|
||||
uint32_t output_flag_present_flag = 0;
|
||||
uint32_t num_extra_slice_header_bits = 0;
|
||||
uint32_t num_ref_idx_l0_default_active_minus1 = 0;
|
||||
uint32_t num_ref_idx_l1_default_active_minus1 = 0;
|
||||
int32_t pic_init_qp_minus26 = 0;
|
||||
uint32_t weighted_pred_flag = 0;
|
||||
uint32_t weighted_bipred_flag = 0;
|
||||
uint32_t lists_modification_present_flag = 0;
|
||||
uint32_t id = 0;
|
||||
uint32_t sps_id = 0;
|
||||
};
|
||||
|
||||
// Unpack RBSP and parse PPS state from the supplied buffer.
|
||||
static absl::optional<PpsState> ParsePps(const uint8_t* data, size_t length);
|
||||
|
||||
static bool ParsePpsIds(const uint8_t* data,
|
||||
size_t length,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id);
|
||||
|
||||
static absl::optional<uint32_t> ParsePpsIdFromSliceSegmentLayerRbsp(
|
||||
const uint8_t* data,
|
||||
size_t length,
|
||||
uint8_t nalu_type);
|
||||
|
||||
protected:
|
||||
// Parse the PPS state, for a bit buffer where RBSP decoding has already been
|
||||
// performed.
|
||||
static absl::optional<PpsState> ParseInternal(rtc::BitBuffer* bit_buffer);
|
||||
static bool ParsePpsIdsInternal(rtc::BitBuffer* bit_buffer,
|
||||
uint32_t* pps_id,
|
||||
uint32_t* sps_id);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_H265_PPS_PARSER_H_
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h265/h265_common.h"
|
||||
#include "common_video/h265/h265_sps_parser.h"
|
||||
#include "common_video/h265/legacy_bit_buffer.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace {
|
||||
typedef absl::optional<webrtc::H265SpsParser::SpsState> OptionalSps;
|
||||
typedef absl::optional<webrtc::H265SpsParser::ShortTermRefPicSet> OptionalShortTermRefPicSet;
|
||||
|
||||
#define RETURN_EMPTY_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
return OptionalSps(); \
|
||||
}
|
||||
|
||||
#define RETURN_FALSE_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define RETURN_EMPTY2_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
return OptionalShortTermRefPicSet(); \
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
H265SpsParser::SpsState::SpsState() = default;
|
||||
|
||||
H265SpsParser::ShortTermRefPicSet::ShortTermRefPicSet() = default;
|
||||
|
||||
// General note: this is based off the 06/2019 version of the H.265 standard.
|
||||
// You can find it on this page:
|
||||
// http://www.itu.int/rec/T-REC-H.265
|
||||
|
||||
// Unpack RBSP and parse SPS state from the supplied buffer.
|
||||
absl::optional<H265SpsParser::SpsState> H265SpsParser::ParseSps(
|
||||
const uint8_t* data,
|
||||
size_t length) {
|
||||
std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data, length);
|
||||
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
|
||||
return ParseSpsInternal(&bit_buffer);
|
||||
}
|
||||
|
||||
bool H265SpsParser::ParseScalingListData(rtc::BitBuffer* buffer) {
|
||||
uint32_t scaling_list_pred_mode_flag[4][6];
|
||||
uint32_t scaling_list_pred_matrix_id_delta[4][6];
|
||||
int32_t scaling_list_dc_coef_minus8[4][6];
|
||||
int32_t scaling_list[4][6][64];
|
||||
for (int size_id = 0; size_id < 4; size_id++) {
|
||||
for (int matrix_id = 0; matrix_id < 6; matrix_id += (size_id == 3) ? 3 : 1) {
|
||||
// scaling_list_pred_mode_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(buffer->ReadBits(&scaling_list_pred_mode_flag[size_id][matrix_id], 1));
|
||||
if (!scaling_list_pred_mode_flag[size_id][matrix_id]) {
|
||||
// scaling_list_pred_matrix_id_delta: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(buffer->ReadExponentialGolomb(&scaling_list_pred_matrix_id_delta[size_id][matrix_id]));
|
||||
} else {
|
||||
int32_t next_coef = 8;
|
||||
uint32_t coef_num = std::min(64, 1 << (4 + (size_id << 1)));
|
||||
if (size_id > 1) {
|
||||
// scaling_list_dc_coef_minus8: se(v)
|
||||
RETURN_FALSE_ON_FAIL(buffer->ReadSignedExponentialGolomb(&scaling_list_dc_coef_minus8[size_id - 2][matrix_id]));
|
||||
next_coef = scaling_list_dc_coef_minus8[size_id - 2][matrix_id];
|
||||
}
|
||||
for (uint32_t i = 0; i < coef_num; i++) {
|
||||
// scaling_list_delta_coef: se(v)
|
||||
int32_t scaling_list_delta_coef = 0;
|
||||
RETURN_FALSE_ON_FAIL(buffer->ReadSignedExponentialGolomb(&scaling_list_delta_coef));
|
||||
next_coef = (next_coef + scaling_list_delta_coef + 256) % 256;
|
||||
scaling_list[size_id][matrix_id][i] = next_coef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<H265SpsParser::ShortTermRefPicSet> H265SpsParser::ParseShortTermRefPicSet(
|
||||
uint32_t st_rps_idx, uint32_t num_short_term_ref_pic_sets,
|
||||
const std::vector<H265SpsParser::ShortTermRefPicSet>& short_term_ref_pic_set,
|
||||
H265SpsParser::SpsState& sps, rtc::BitBuffer* buffer) {
|
||||
H265SpsParser::ShortTermRefPicSet ref_pic_set;
|
||||
|
||||
uint32_t inter_ref_pic_set_prediction_flag = 0;
|
||||
if (st_rps_idx != 0) {
|
||||
// inter_ref_pic_set_prediction_flag: u(1)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&inter_ref_pic_set_prediction_flag, 1));
|
||||
}
|
||||
if (inter_ref_pic_set_prediction_flag) {
|
||||
uint32_t delta_idx_minus1 = 0;
|
||||
if (st_rps_idx == num_short_term_ref_pic_sets) {
|
||||
// delta_idx_minus1: ue(v)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&delta_idx_minus1));
|
||||
}
|
||||
// delta_rps_sign: u(1)
|
||||
uint32_t delta_rps_sign = 0;
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&delta_rps_sign, 1));
|
||||
// abs_delta_rps_minus1: ue(v)
|
||||
uint32_t abs_delta_rps_minus1 = 0;
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&abs_delta_rps_minus1));
|
||||
uint32_t ref_rps_idx = st_rps_idx - (delta_idx_minus1 + 1);
|
||||
uint32_t num_delta_pocs = 0;
|
||||
if (short_term_ref_pic_set[ref_rps_idx].inter_ref_pic_set_prediction_flag) {
|
||||
auto& used_by_curr_pic_flag = short_term_ref_pic_set[ref_rps_idx].used_by_curr_pic_flag;
|
||||
auto& use_delta_flag = short_term_ref_pic_set[ref_rps_idx].use_delta_flag;
|
||||
if (used_by_curr_pic_flag.size() != use_delta_flag.size()) {
|
||||
return OptionalShortTermRefPicSet();
|
||||
}
|
||||
for (uint32_t i = 0; i < used_by_curr_pic_flag.size(); i++) {
|
||||
if (used_by_curr_pic_flag[i] || use_delta_flag[i]) {
|
||||
num_delta_pocs++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
num_delta_pocs = short_term_ref_pic_set[ref_rps_idx].num_negative_pics + short_term_ref_pic_set[ref_rps_idx].num_positive_pics;
|
||||
}
|
||||
ref_pic_set.used_by_curr_pic_flag.resize(num_delta_pocs + 1, 0);
|
||||
ref_pic_set.use_delta_flag.resize(num_delta_pocs + 1, 1);
|
||||
for (uint32_t j = 0; j <= num_delta_pocs; j++) {
|
||||
// used_by_curr_pic_flag: u(1)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&ref_pic_set.used_by_curr_pic_flag[j], 1));
|
||||
if (!ref_pic_set.used_by_curr_pic_flag[j]) {
|
||||
// use_delta_flag: u(1)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&ref_pic_set.use_delta_flag[j], 1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// num_negative_pics: ue(v)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&ref_pic_set.num_negative_pics));
|
||||
// num_positive_pics: ue(v)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&ref_pic_set.num_positive_pics));
|
||||
|
||||
ref_pic_set.delta_poc_s0_minus1.resize(ref_pic_set.num_negative_pics, 0);
|
||||
ref_pic_set.used_by_curr_pic_s0_flag.resize(ref_pic_set.num_negative_pics, 0);
|
||||
for (uint32_t i = 0; i < ref_pic_set.num_negative_pics; i++) {
|
||||
// delta_poc_s0_minus1: ue(v)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&ref_pic_set.delta_poc_s0_minus1[i]));
|
||||
// used_by_curr_pic_s0_flag: u(1)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&ref_pic_set.used_by_curr_pic_s0_flag[i], 1));
|
||||
}
|
||||
ref_pic_set.delta_poc_s1_minus1.resize(ref_pic_set.num_positive_pics, 0);
|
||||
ref_pic_set.used_by_curr_pic_s1_flag.resize(ref_pic_set.num_positive_pics, 0);
|
||||
for (uint32_t i = 0; i < ref_pic_set.num_positive_pics; i++) {
|
||||
// delta_poc_s1_minus1: ue(v)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadExponentialGolomb(&ref_pic_set.delta_poc_s1_minus1[i]));
|
||||
// used_by_curr_pic_s1_flag: u(1)
|
||||
RETURN_EMPTY2_ON_FAIL(buffer->ReadBits(&ref_pic_set.used_by_curr_pic_s1_flag[i], 1));
|
||||
}
|
||||
}
|
||||
|
||||
return OptionalShortTermRefPicSet(ref_pic_set);
|
||||
}
|
||||
|
||||
absl::optional<H265SpsParser::SpsState> H265SpsParser::ParseSpsInternal(
|
||||
rtc::BitBuffer* buffer) {
|
||||
// Now, we need to use a bit buffer to parse through the actual HEVC SPS
|
||||
// format. See Section 7.3.2.2.1 ("General sequence parameter set data
|
||||
// syntax") of the H.265 standard for a complete description.
|
||||
// Since we only care about resolution, we ignore the majority of fields, but
|
||||
// we still have to actively parse through a lot of the data, since many of
|
||||
// the fields have variable size.
|
||||
// We're particularly interested in:
|
||||
// chroma_format_idc -> affects crop units
|
||||
// pic_{width,height}_* -> resolution of the frame in macroblocks (16x16).
|
||||
// frame_crop_*_offset -> crop information
|
||||
|
||||
SpsState sps;
|
||||
|
||||
// The golomb values we have to read, not just consume.
|
||||
uint32_t golomb_ignored;
|
||||
|
||||
// sps_video_parameter_set_id: u(4)
|
||||
uint32_t sps_video_parameter_set_id = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps_video_parameter_set_id, 4));
|
||||
// sps_max_sub_layers_minus1: u(3)
|
||||
uint32_t sps_max_sub_layers_minus1 = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps_max_sub_layers_minus1, 3));
|
||||
sps.sps_max_sub_layers_minus1 = sps_max_sub_layers_minus1;
|
||||
sps.sps_max_dec_pic_buffering_minus1.resize(sps_max_sub_layers_minus1 + 1, 0);
|
||||
// sps_temporal_id_nesting_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1));
|
||||
// profile_tier_level(1, sps_max_sub_layers_minus1). We are acutally not
|
||||
// using them, so read/skip over it.
|
||||
// general_profile_space+general_tier_flag+general_prfile_idc: u(8)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1));
|
||||
// general_profile_compatabilitiy_flag[32]
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(4));
|
||||
// general_progressive_source_flag + interlaced_source_flag+
|
||||
// non-packed_constraint flag + frame_only_constraint_flag: u(4)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(4));
|
||||
// general_profile_idc decided flags or reserved. u(43)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(43));
|
||||
// general_inbld_flag or reserved 0: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1));
|
||||
// general_level_idc: u(8)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1));
|
||||
// if max_sub_layers_minus1 >=1, read the sublayer profile information
|
||||
std::vector<uint32_t> sub_layer_profile_present_flags;
|
||||
std::vector<uint32_t> sub_layer_level_present_flags;
|
||||
uint32_t sub_layer_profile_present = 0;
|
||||
uint32_t sub_layer_level_present = 0;
|
||||
for (uint32_t i = 0; i < sps_max_sub_layers_minus1; i++) {
|
||||
// sublayer_profile_present_flag and sublayer_level_presnet_flag: u(2)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sub_layer_profile_present, 1));
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sub_layer_level_present, 1));
|
||||
sub_layer_profile_present_flags.push_back(sub_layer_profile_present);
|
||||
sub_layer_level_present_flags.push_back(sub_layer_level_present);
|
||||
}
|
||||
if (sps_max_sub_layers_minus1 > 0) {
|
||||
for (uint32_t j = sps_max_sub_layers_minus1; j < 8; j++) {
|
||||
// reserved 2 bits: u(2)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(2));
|
||||
}
|
||||
}
|
||||
for (uint32_t k = 0; k < sps_max_sub_layers_minus1; k++) {
|
||||
if (sub_layer_profile_present_flags[k]) { //
|
||||
// sub_layer profile_space/tier_flag/profile_idc. ignored. u(8)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1));
|
||||
// profile_compatability_flag: u(32)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(4));
|
||||
// sub_layer progressive_source_flag/interlaced_source_flag/
|
||||
// non_packed_constraint_flag/frame_only_constraint_flag: u(4)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(4));
|
||||
// following 43-bits are profile_idc specific. We simply read/skip it.
|
||||
// u(43)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(43));
|
||||
// 1-bit profile_idc specific inbld flag. We simply read/skip it. u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1));
|
||||
}
|
||||
if (sub_layer_level_present_flags[k]) {
|
||||
// sub_layer_level_idc: u(8)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1));
|
||||
}
|
||||
}
|
||||
// sps_seq_parameter_set_id: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.id));
|
||||
// chrome_format_idc: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.chroma_format_idc));
|
||||
if (sps.chroma_format_idc == 3) {
|
||||
// seperate_colour_plane_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps.separate_colour_plane_flag, 1));
|
||||
}
|
||||
uint32_t pic_width_in_luma_samples = 0;
|
||||
uint32_t pic_height_in_luma_samples = 0;
|
||||
// pic_width_in_luma_samples: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(
|
||||
buffer->ReadExponentialGolomb(&pic_width_in_luma_samples));
|
||||
// pic_height_in_luma_samples: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(
|
||||
buffer->ReadExponentialGolomb(&pic_height_in_luma_samples));
|
||||
// conformance_window_flag: u(1)
|
||||
uint32_t conformance_window_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&conformance_window_flag, 1));
|
||||
|
||||
uint32_t conf_win_left_offset = 0;
|
||||
uint32_t conf_win_right_offset = 0;
|
||||
uint32_t conf_win_top_offset = 0;
|
||||
uint32_t conf_win_bottom_offset = 0;
|
||||
if (conformance_window_flag) {
|
||||
// conf_win_left_offset: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&conf_win_left_offset));
|
||||
// conf_win_right_offset: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&conf_win_right_offset));
|
||||
// conf_win_top_offset: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&conf_win_top_offset));
|
||||
// conf_win_bottom_offset: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(
|
||||
buffer->ReadExponentialGolomb(&conf_win_bottom_offset));
|
||||
}
|
||||
|
||||
// bit_depth_luma_minus8: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// bit_depth_chroma_minus8: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// log2_max_pic_order_cnt_lsb_minus4: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.log2_max_pic_order_cnt_lsb_minus4));
|
||||
uint32_t sps_sub_layer_ordering_info_present_flag = 0;
|
||||
// sps_sub_layer_ordering_info_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps_sub_layer_ordering_info_present_flag, 1));
|
||||
for (uint32_t i = (sps_sub_layer_ordering_info_present_flag != 0) ? 0 : sps_max_sub_layers_minus1;
|
||||
i <= sps_max_sub_layers_minus1; i++) {
|
||||
// sps_max_dec_pic_buffering_minus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.sps_max_dec_pic_buffering_minus1[i]));
|
||||
// sps_max_num_reorder_pics: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// sps_max_latency_increase_plus1: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
}
|
||||
// log2_min_luma_coding_block_size_minus3: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.log2_min_luma_coding_block_size_minus3));
|
||||
// log2_diff_max_min_luma_coding_block_size: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.log2_diff_max_min_luma_coding_block_size));
|
||||
// log2_min_luma_transform_block_size_minus2: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// log2_diff_max_min_luma_transform_block_size: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// max_transform_hierarchy_depth_inter: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// max_transform_hierarchy_depth_intra: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// scaling_list_enabled_flag: u(1)
|
||||
uint32_t scaling_list_enabled_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&scaling_list_enabled_flag, 1));
|
||||
if (scaling_list_enabled_flag) {
|
||||
// sps_scaling_list_data_present_flag: u(1)
|
||||
uint32_t sps_scaling_list_data_present_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps_scaling_list_data_present_flag, 1));
|
||||
if (sps_scaling_list_data_present_flag) {
|
||||
// scaling_list_data()
|
||||
if (!ParseScalingListData(buffer)) {
|
||||
return OptionalSps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// amp_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1));
|
||||
// sample_adaptive_offset_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps.sample_adaptive_offset_enabled_flag, 1));
|
||||
// pcm_enabled_flag: u(1)
|
||||
uint32_t pcm_enabled_flag = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&pcm_enabled_flag, 1));
|
||||
if (pcm_enabled_flag) {
|
||||
// pcm_sample_bit_depth_luma_minus1: u(4)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(4));
|
||||
// pcm_sample_bit_depth_chroma_minus1: u(4)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(4));
|
||||
// log2_min_pcm_luma_coding_block_size_minus3: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// log2_diff_max_min_pcm_luma_coding_block_size: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&golomb_ignored));
|
||||
// pcm_loop_filter_disabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1));
|
||||
}
|
||||
|
||||
// num_short_term_ref_pic_sets: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.num_short_term_ref_pic_sets));
|
||||
sps.short_term_ref_pic_set.resize(sps.num_short_term_ref_pic_sets);
|
||||
for (uint32_t st_rps_idx = 0; st_rps_idx < sps.num_short_term_ref_pic_sets; st_rps_idx++) {
|
||||
// st_ref_pic_set()
|
||||
OptionalShortTermRefPicSet ref_pic_set = ParseShortTermRefPicSet(
|
||||
st_rps_idx, sps.num_short_term_ref_pic_sets, sps.short_term_ref_pic_set, sps, buffer);
|
||||
if (ref_pic_set) {
|
||||
sps.short_term_ref_pic_set[st_rps_idx] = *ref_pic_set;
|
||||
} else {
|
||||
return OptionalSps();
|
||||
}
|
||||
}
|
||||
|
||||
// long_term_ref_pics_present_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps.long_term_ref_pics_present_flag, 1));
|
||||
if (sps.long_term_ref_pics_present_flag) {
|
||||
// num_long_term_ref_pics_sps: ue(v)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(&sps.num_long_term_ref_pics_sps));
|
||||
sps.used_by_curr_pic_lt_sps_flag.resize(sps.num_long_term_ref_pics_sps, 0);
|
||||
for (uint32_t i = 0; i < sps.num_long_term_ref_pics_sps; i++) {
|
||||
// lt_ref_pic_poc_lsb_sps: u(v)
|
||||
uint32_t lt_ref_pic_poc_lsb_sps_bits = sps.log2_max_pic_order_cnt_lsb_minus4 + 4;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(lt_ref_pic_poc_lsb_sps_bits));
|
||||
// used_by_curr_pic_lt_sps_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps.used_by_curr_pic_lt_sps_flag[i], 1));
|
||||
}
|
||||
}
|
||||
|
||||
// sps_temporal_mvp_enabled_flag: u(1)
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&sps.sps_temporal_mvp_enabled_flag, 1));
|
||||
|
||||
// Far enough! We don't use the rest of the SPS.
|
||||
|
||||
sps.vps_id = sps_video_parameter_set_id;
|
||||
|
||||
sps.pic_width_in_luma_samples = pic_width_in_luma_samples;
|
||||
sps.pic_height_in_luma_samples = pic_height_in_luma_samples;
|
||||
|
||||
// Start with the resolution determined by the pic_width/pic_height fields.
|
||||
sps.width = pic_width_in_luma_samples;
|
||||
sps.height = pic_height_in_luma_samples;
|
||||
|
||||
if (conformance_window_flag) {
|
||||
int sub_width_c = ((1 == sps.chroma_format_idc) || (2 == sps.chroma_format_idc)) &&
|
||||
(0 == sps.separate_colour_plane_flag)
|
||||
? 2
|
||||
: 1;
|
||||
int sub_height_c =
|
||||
(1 == sps.chroma_format_idc) && (0 == sps.separate_colour_plane_flag) ? 2 : 1;
|
||||
// the offset includes the pixel within conformance window. so don't need to
|
||||
// +1 as per spec
|
||||
sps.width -= sub_width_c * (conf_win_right_offset + conf_win_left_offset);
|
||||
sps.height -= sub_height_c * (conf_win_top_offset + conf_win_bottom_offset);
|
||||
}
|
||||
|
||||
return OptionalSps(sps);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 COMMON_VIDEO_H265_H265_SPS_PARSER_H_
|
||||
#define COMMON_VIDEO_H265_H265_SPS_PARSER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace rtc {
|
||||
class BitBuffer;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class for parsing out sequence parameter set (SPS) data from an H265 NALU.
|
||||
class H265SpsParser {
|
||||
public:
|
||||
|
||||
struct ShortTermRefPicSet {
|
||||
ShortTermRefPicSet();
|
||||
|
||||
uint32_t inter_ref_pic_set_prediction_flag = 0;
|
||||
std::vector<uint32_t> used_by_curr_pic_flag;
|
||||
std::vector<uint32_t> use_delta_flag;
|
||||
uint32_t num_negative_pics = 0;
|
||||
uint32_t num_positive_pics = 0;
|
||||
std::vector<uint32_t> delta_poc_s0_minus1;
|
||||
std::vector<uint32_t> used_by_curr_pic_s0_flag;
|
||||
std::vector<uint32_t> delta_poc_s1_minus1;
|
||||
std::vector<uint32_t> used_by_curr_pic_s1_flag;
|
||||
};
|
||||
|
||||
// The parsed state of the SPS. Only some select values are stored.
|
||||
// Add more as they are actually needed.
|
||||
struct SpsState {
|
||||
SpsState();
|
||||
|
||||
uint32_t sps_max_sub_layers_minus1;
|
||||
uint32_t chroma_format_idc = 0;
|
||||
uint32_t separate_colour_plane_flag = 0;
|
||||
uint32_t pic_width_in_luma_samples = 0;
|
||||
uint32_t pic_height_in_luma_samples = 0;
|
||||
uint32_t log2_max_pic_order_cnt_lsb_minus4 = 0;
|
||||
std::vector<uint32_t> sps_max_dec_pic_buffering_minus1;
|
||||
uint32_t log2_min_luma_coding_block_size_minus3 = 0;
|
||||
uint32_t log2_diff_max_min_luma_coding_block_size = 0;
|
||||
uint32_t sample_adaptive_offset_enabled_flag = 0;
|
||||
uint32_t num_short_term_ref_pic_sets = 0;
|
||||
std::vector<H265SpsParser::ShortTermRefPicSet> short_term_ref_pic_set;
|
||||
uint32_t long_term_ref_pics_present_flag = 0;
|
||||
uint32_t num_long_term_ref_pics_sps = 0;
|
||||
std::vector<uint32_t> used_by_curr_pic_lt_sps_flag;
|
||||
uint32_t sps_temporal_mvp_enabled_flag = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t id = 0;
|
||||
uint32_t vps_id = 0;
|
||||
};
|
||||
|
||||
// Unpack RBSP and parse SPS state from the supplied buffer.
|
||||
static absl::optional<SpsState> ParseSps(const uint8_t* data, size_t length);
|
||||
|
||||
static bool ParseScalingListData(rtc::BitBuffer* buffer);
|
||||
|
||||
static absl::optional<ShortTermRefPicSet> ParseShortTermRefPicSet(
|
||||
uint32_t st_rps_idx, uint32_t num_short_term_ref_pic_sets,
|
||||
const std::vector<ShortTermRefPicSet>& ref_pic_sets,
|
||||
SpsState& sps, rtc::BitBuffer* buffer);
|
||||
|
||||
protected:
|
||||
// Parse the SPS state, for a bit buffer where RBSP decoding has already been
|
||||
// performed.
|
||||
static absl::optional<SpsState> ParseSpsInternal(rtc::BitBuffer* buffer);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // COMMON_VIDEO_H265_H265_SPS_PARSER_H_
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_video/h265/h265_common.h"
|
||||
#include "common_video/h265/h265_vps_parser.h"
|
||||
#include "common_video/h265/legacy_bit_buffer.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace {
|
||||
typedef absl::optional<webrtc::H265VpsParser::VpsState> OptionalVps;
|
||||
|
||||
#define RETURN_EMPTY_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
return OptionalVps(); \
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
H265VpsParser::VpsState::VpsState() = default;
|
||||
|
||||
// General note: this is based off the 06/2019 version of the H.265 standard.
|
||||
// You can find it on this page:
|
||||
// http://www.itu.int/rec/T-REC-H.265
|
||||
|
||||
// Unpack RBSP and parse SPS state from the supplied buffer.
|
||||
absl::optional<H265VpsParser::VpsState> H265VpsParser::ParseVps(
|
||||
const uint8_t* data,
|
||||
size_t length) {
|
||||
std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data, length);
|
||||
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
|
||||
return ParseInternal(&bit_buffer);
|
||||
}
|
||||
|
||||
absl::optional<H265VpsParser::VpsState> H265VpsParser::ParseInternal(
|
||||
rtc::BitBuffer* buffer) {
|
||||
// Now, we need to use a bit buffer to parse through the actual HEVC VPS
|
||||
// format. See Section 7.3.2.1 ("Video parameter set RBSP syntax") of the
|
||||
// H.265 standard for a complete description.
|
||||
|
||||
VpsState vps;
|
||||
|
||||
// vps_video_parameter_set_id: u(4)
|
||||
vps.id = 0;
|
||||
RETURN_EMPTY_ON_FAIL(buffer->ReadBits(&vps.id, 4));
|
||||
|
||||
return OptionalVps(vps);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 COMMON_VIDEO_H265_H265_VPS_PARSER_H_
|
||||
#define COMMON_VIDEO_H265_H265_VPS_PARSER_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace rtc {
|
||||
class BitBuffer;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A class for parsing out sequence parameter set (VPS) data from an H265 NALU.
|
||||
class H265VpsParser {
|
||||
public:
|
||||
// The parsed state of the VPS. Only some select values are stored.
|
||||
// Add more as they are actually needed.
|
||||
struct VpsState {
|
||||
VpsState();
|
||||
|
||||
uint32_t id = 0;
|
||||
};
|
||||
|
||||
// Unpack RBSP and parse VPS state from the supplied buffer.
|
||||
static absl::optional<VpsState> ParseVps(const uint8_t* data, size_t length);
|
||||
|
||||
protected:
|
||||
// Parse the VPS state, for a bit buffer where RBSP decoding has already been
|
||||
// performed.
|
||||
static absl::optional<VpsState> ParseInternal(rtc::BitBuffer* bit_buffer);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // COMMON_VIDEO_H265_H265_VPS_PARSER_H_
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright 2015 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 "common_video/h265/legacy_bit_buffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns the lowest (right-most) |bit_count| bits in |byte|.
|
||||
uint8_t LowestBits(uint8_t byte, size_t bit_count) {
|
||||
RTC_DCHECK_LE(bit_count, 8);
|
||||
return byte & ((1 << bit_count) - 1);
|
||||
}
|
||||
|
||||
// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
|
||||
// lowest bits (to the right).
|
||||
uint8_t HighestBits(uint8_t byte, size_t bit_count) {
|
||||
RTC_DCHECK_LE(bit_count, 8);
|
||||
uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
|
||||
uint8_t mask = 0xFF << shift;
|
||||
return (byte & mask) >> shift;
|
||||
}
|
||||
|
||||
// Counts the number of bits used in the binary representation of val.
|
||||
size_t CountBits(uint64_t val) {
|
||||
size_t bit_count = 0;
|
||||
while (val != 0) {
|
||||
bit_count++;
|
||||
val >>= 1;
|
||||
}
|
||||
return bit_count;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace rtc {
|
||||
|
||||
BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
|
||||
: bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
|
||||
RTC_DCHECK(static_cast<uint64_t>(byte_count_) <=
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
|
||||
uint64_t BitBuffer::RemainingBitCount() const {
|
||||
return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt8(uint8_t* val) {
|
||||
uint32_t bit_val;
|
||||
if (!ReadBits(&bit_val, sizeof(uint8_t) * 8)) {
|
||||
return false;
|
||||
}
|
||||
RTC_DCHECK(bit_val <= std::numeric_limits<uint8_t>::max());
|
||||
*val = static_cast<uint8_t>(bit_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt16(uint16_t* val) {
|
||||
uint32_t bit_val;
|
||||
if (!ReadBits(&bit_val, sizeof(uint16_t) * 8)) {
|
||||
return false;
|
||||
}
|
||||
RTC_DCHECK(bit_val <= std::numeric_limits<uint16_t>::max());
|
||||
*val = static_cast<uint16_t>(bit_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt32(uint32_t* val) {
|
||||
return ReadBits(val, sizeof(uint32_t) * 8);
|
||||
}
|
||||
|
||||
bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
|
||||
// TODO(nisse): Could allow bit_count == 0 and always return success. But
|
||||
// current code reads one byte beyond end of buffer in the case that
|
||||
// RemainingBitCount() == 0 and bit_count == 0.
|
||||
RTC_DCHECK(bit_count > 0);
|
||||
if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* bytes = bytes_ + byte_offset_;
|
||||
size_t remaining_bits_in_current_byte = 8 - bit_offset_;
|
||||
uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
|
||||
// If we're reading fewer bits than what's left in the current byte, just
|
||||
// return the portion of this byte that we need.
|
||||
if (bit_count < remaining_bits_in_current_byte) {
|
||||
*val = HighestBits(bits, bit_offset_ + bit_count);
|
||||
return true;
|
||||
}
|
||||
// Otherwise, subtract what we've read from the bit count and read as many
|
||||
// full bytes as we can into bits.
|
||||
bit_count -= remaining_bits_in_current_byte;
|
||||
while (bit_count >= 8) {
|
||||
bits = (bits << 8) | *bytes++;
|
||||
bit_count -= 8;
|
||||
}
|
||||
// Whatever we have left is smaller than a byte, so grab just the bits we need
|
||||
// and shift them into the lowest bits.
|
||||
if (bit_count > 0) {
|
||||
bits <<= bit_count;
|
||||
bits |= HighestBits(*bytes, bit_count);
|
||||
}
|
||||
*val = bits;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
|
||||
return PeekBits(val, bit_count) && ConsumeBits(bit_count);
|
||||
}
|
||||
|
||||
bool BitBuffer::ConsumeBytes(size_t byte_count) {
|
||||
return ConsumeBits(byte_count * 8);
|
||||
}
|
||||
|
||||
bool BitBuffer::ConsumeBits(size_t bit_count) {
|
||||
if (bit_count > RemainingBitCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte_offset_ += (bit_offset_ + bit_count) / 8;
|
||||
bit_offset_ = (bit_offset_ + bit_count) % 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadNonSymmetric(uint32_t* val, uint32_t num_values) {
|
||||
RTC_DCHECK_GT(num_values, 0);
|
||||
RTC_DCHECK_LE(num_values, uint32_t{1} << 31);
|
||||
if (num_values == 1) {
|
||||
// When there is only one possible value, it requires zero bits to store it.
|
||||
// But ReadBits doesn't support reading zero bits.
|
||||
*val = 0;
|
||||
return true;
|
||||
}
|
||||
size_t count_bits = CountBits(num_values);
|
||||
uint32_t num_min_bits_values = (uint32_t{1} << count_bits) - num_values;
|
||||
|
||||
if (!ReadBits(val, count_bits - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*val < num_min_bits_values) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t extra_bit;
|
||||
if (!ReadBits(&extra_bit, /*bit_count=*/1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*val = (*val << 1) + extra_bit - num_min_bits_values;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
|
||||
if (!val) {
|
||||
return false;
|
||||
}
|
||||
// Store off the current byte/bit offset, in case we want to restore them due
|
||||
// to a failed parse.
|
||||
size_t original_byte_offset = byte_offset_;
|
||||
size_t original_bit_offset = bit_offset_;
|
||||
|
||||
// Count the number of leading 0 bits by peeking/consuming them one at a time.
|
||||
size_t zero_bit_count = 0;
|
||||
uint32_t peeked_bit;
|
||||
while (PeekBits(&peeked_bit, 1) && peeked_bit == 0) {
|
||||
zero_bit_count++;
|
||||
ConsumeBits(1);
|
||||
}
|
||||
|
||||
// We should either be at the end of the stream, or the next bit should be 1.
|
||||
RTC_DCHECK(!PeekBits(&peeked_bit, 1) || peeked_bit == 1);
|
||||
|
||||
// The bit count of the value is the number of zeros + 1. Make sure that many
|
||||
// bits fits in a uint32_t and that we have enough bits left for it, and then
|
||||
// read the value.
|
||||
size_t value_bit_count = zero_bit_count + 1;
|
||||
if (value_bit_count > 32 || !ReadBits(val, value_bit_count)) {
|
||||
RTC_CHECK(Seek(original_byte_offset, original_bit_offset));
|
||||
return false;
|
||||
}
|
||||
*val -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadSignedExponentialGolomb(int32_t* val) {
|
||||
uint32_t unsigned_val;
|
||||
if (!ReadExponentialGolomb(&unsigned_val)) {
|
||||
return false;
|
||||
}
|
||||
if ((unsigned_val & 1) == 0) {
|
||||
*val = -static_cast<int32_t>(unsigned_val / 2);
|
||||
} else {
|
||||
*val = (unsigned_val + 1) / 2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitBuffer::GetCurrentOffset(size_t* out_byte_offset,
|
||||
size_t* out_bit_offset) {
|
||||
RTC_CHECK(out_byte_offset != nullptr);
|
||||
RTC_CHECK(out_bit_offset != nullptr);
|
||||
*out_byte_offset = byte_offset_;
|
||||
*out_bit_offset = bit_offset_;
|
||||
}
|
||||
|
||||
bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) {
|
||||
if (byte_offset > byte_count_ || bit_offset > 7 ||
|
||||
(byte_offset == byte_count_ && bit_offset > 0)) {
|
||||
return false;
|
||||
}
|
||||
byte_offset_ = byte_offset;
|
||||
bit_offset_ = bit_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 COMMON_VIDEO_H265_LEGACY_BIT_BUFFER_H_
|
||||
#define COMMON_VIDEO_H265_LEGACY_BIT_BUFFER_H_
|
||||
|
||||
#include <stddef.h> // For size_t.
|
||||
#include <stdint.h> // For integer types.
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// A class, similar to ByteBuffer, that can parse bit-sized data out of a set of
|
||||
// bytes. Has a similar API to ByteBuffer, plus methods for reading bit-sized
|
||||
// and exponential golomb encoded data. For a writable version, use
|
||||
// BitBufferWriter. Unlike ByteBuffer, this class doesn't make a copy of the
|
||||
// source bytes, so it can be used on read-only data.
|
||||
// Sizes/counts specify bits/bytes, for clarity.
|
||||
// Byte order is assumed big-endian/network.
|
||||
class BitBuffer {
|
||||
public:
|
||||
BitBuffer(const uint8_t* bytes, size_t byte_count);
|
||||
|
||||
BitBuffer(const BitBuffer&) = delete;
|
||||
BitBuffer& operator=(const BitBuffer&) = delete;
|
||||
|
||||
// Gets the current offset, in bytes/bits, from the start of the buffer. The
|
||||
// bit offset is the offset into the current byte, in the range [0,7].
|
||||
void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
|
||||
|
||||
// The remaining bits in the byte buffer.
|
||||
uint64_t RemainingBitCount() const;
|
||||
|
||||
// Reads byte-sized values from the buffer. Returns false if there isn't
|
||||
// enough data left for the specified type.
|
||||
bool ReadUInt8(uint8_t* val);
|
||||
bool ReadUInt16(uint16_t* val);
|
||||
bool ReadUInt32(uint32_t* val);
|
||||
|
||||
// Reads bit-sized values from the buffer. Returns false if there isn't enough
|
||||
// data left for the specified bit count.
|
||||
bool ReadBits(uint32_t* val, size_t bit_count);
|
||||
|
||||
// Peeks bit-sized values from the buffer. Returns false if there isn't enough
|
||||
// data left for the specified number of bits. Doesn't move the current
|
||||
// offset.
|
||||
bool PeekBits(uint32_t* val, size_t bit_count);
|
||||
|
||||
// Reads value in range [0, num_values - 1].
|
||||
// This encoding is similar to ReadBits(val, Ceil(Log2(num_values)),
|
||||
// but reduces wastage incurred when encoding non-power of two value ranges
|
||||
// Non symmetric values are encoded as:
|
||||
// 1) n = countbits(num_values)
|
||||
// 2) k = (1 << n) - num_values
|
||||
// Value v in range [0, k - 1] is encoded in (n-1) bits.
|
||||
// Value v in range [k, num_values - 1] is encoded as (v+k) in n bits.
|
||||
// https://aomediacodec.github.io/av1-spec/#nsn
|
||||
// Returns false if there isn't enough data left.
|
||||
bool ReadNonSymmetric(uint32_t* val, uint32_t num_values);
|
||||
|
||||
// Reads the exponential golomb encoded value at the current offset.
|
||||
// Exponential golomb values are encoded as:
|
||||
// 1) x = source val + 1
|
||||
// 2) In binary, write [countbits(x) - 1] 0s, then x
|
||||
// To decode, we count the number of leading 0 bits, read that many + 1 bits,
|
||||
// and increment the result by 1.
|
||||
// Returns false if there isn't enough data left for the specified type, or if
|
||||
// the value wouldn't fit in a uint32_t.
|
||||
bool ReadExponentialGolomb(uint32_t* val);
|
||||
// Reads signed exponential golomb values at the current offset. Signed
|
||||
// exponential golomb values are just the unsigned values mapped to the
|
||||
// sequence 0, 1, -1, 2, -2, etc. in order.
|
||||
bool ReadSignedExponentialGolomb(int32_t* val);
|
||||
|
||||
// Moves current position |byte_count| bytes forward. Returns false if
|
||||
// there aren't enough bytes left in the buffer.
|
||||
bool ConsumeBytes(size_t byte_count);
|
||||
// Moves current position |bit_count| bits forward. Returns false if
|
||||
// there aren't enough bits left in the buffer.
|
||||
bool ConsumeBits(size_t bit_count);
|
||||
|
||||
// Sets the current offset to the provied byte/bit offsets. The bit
|
||||
// offset is from the given byte, in the range [0,7].
|
||||
bool Seek(size_t byte_offset, size_t bit_offset);
|
||||
|
||||
protected:
|
||||
const uint8_t* const bytes_;
|
||||
// The total size of |bytes_|.
|
||||
size_t byte_count_;
|
||||
// The current offset, in bytes, from the start of |bytes_|.
|
||||
size_t byte_offset_;
|
||||
// The current offset, in bits, into the current byte.
|
||||
size_t bit_offset_;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // COMMON_VIDEO_H265_LEGACY_BIT_BUFFER_H_
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2016 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 COMMON_VIDEO_INCLUDE_BITRATE_ADJUSTER_H_
|
||||
#define COMMON_VIDEO_INCLUDE_BITRATE_ADJUSTER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "rtc_base/rate_statistics.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Certain hardware encoders tend to consistently overshoot the bitrate that
|
||||
// they are configured to encode at. This class estimates an adjusted bitrate
|
||||
// that when set on the encoder will produce the desired bitrate.
|
||||
class RTC_EXPORT BitrateAdjuster {
|
||||
public:
|
||||
// min_adjusted_bitrate_pct and max_adjusted_bitrate_pct are the lower and
|
||||
// upper bound outputted adjusted bitrates as a percentage of the target
|
||||
// bitrate.
|
||||
BitrateAdjuster(float min_adjusted_bitrate_pct,
|
||||
float max_adjusted_bitrate_pct);
|
||||
virtual ~BitrateAdjuster() {}
|
||||
|
||||
static const uint32_t kBitrateUpdateIntervalMs;
|
||||
static const uint32_t kBitrateUpdateFrameInterval;
|
||||
static const float kBitrateTolerancePct;
|
||||
static const float kBytesPerMsToBitsPerSecond;
|
||||
|
||||
// Sets the desired bitrate in bps (bits per second).
|
||||
// Should be called at least once before Update.
|
||||
void SetTargetBitrateBps(uint32_t bitrate_bps);
|
||||
uint32_t GetTargetBitrateBps() const;
|
||||
|
||||
// Returns the adjusted bitrate in bps.
|
||||
uint32_t GetAdjustedBitrateBps() const;
|
||||
|
||||
// Returns what we think the current bitrate is.
|
||||
absl::optional<uint32_t> GetEstimatedBitrateBps();
|
||||
|
||||
// This should be called after each frame is encoded. The timestamp at which
|
||||
// it is called is used to estimate the output bitrate of the encoder.
|
||||
// Should be called from only one thread.
|
||||
void Update(size_t frame_size);
|
||||
|
||||
private:
|
||||
// Returns true if the bitrate is within kBitrateTolerancePct of bitrate_bps.
|
||||
bool IsWithinTolerance(uint32_t bitrate_bps, uint32_t target_bitrate_bps);
|
||||
|
||||
// Returns smallest possible adjusted value.
|
||||
uint32_t GetMinAdjustedBitrateBps() const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
// Returns largest possible adjusted value.
|
||||
uint32_t GetMaxAdjustedBitrateBps() const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
void Reset();
|
||||
void UpdateBitrate(uint32_t current_time_ms)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
mutable Mutex mutex_;
|
||||
const float min_adjusted_bitrate_pct_;
|
||||
const float max_adjusted_bitrate_pct_;
|
||||
// The bitrate we want.
|
||||
volatile uint32_t target_bitrate_bps_ RTC_GUARDED_BY(mutex_);
|
||||
// The bitrate we use to get what we want.
|
||||
volatile uint32_t adjusted_bitrate_bps_ RTC_GUARDED_BY(mutex_);
|
||||
// The target bitrate that the adjusted bitrate was computed from.
|
||||
volatile uint32_t last_adjusted_target_bitrate_bps_ RTC_GUARDED_BY(mutex_);
|
||||
// Used to estimate bitrate.
|
||||
RateStatistics bitrate_tracker_ RTC_GUARDED_BY(mutex_);
|
||||
// The last time we tried to adjust the bitrate.
|
||||
uint32_t last_bitrate_update_time_ms_ RTC_GUARDED_BY(mutex_);
|
||||
// The number of frames since the last time we tried to adjust the bitrate.
|
||||
uint32_t frames_since_last_update_ RTC_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_INCLUDE_BITRATE_ADJUSTER_H_
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 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 COMMON_VIDEO_INCLUDE_QUALITY_LIMITATION_REASON_H_
|
||||
#define COMMON_VIDEO_INCLUDE_QUALITY_LIMITATION_REASON_H_
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// https://w3c.github.io/webrtc-stats/#rtcqualitylimitationreason-enum
|
||||
enum class QualityLimitationReason {
|
||||
kNone,
|
||||
kCpu,
|
||||
kBandwidth,
|
||||
kOther,
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_INCLUDE_QUALITY_LIMITATION_REASON_H_
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_H_
|
||||
#define COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> WrapI420Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I422BufferInterface> WrapI422Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I444BufferInterface> WrapI444Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I420ABufferInterface> WrapI420ABuffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
const uint8_t* a_plane,
|
||||
int a_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<PlanarYuvBuffer> WrapYuvBuffer(
|
||||
VideoFrameBuffer::Type type,
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I010BufferInterface> WrapI010Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I210BufferInterface> WrapI210Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
|
||||
rtc::scoped_refptr<I410BufferInterface> WrapI410Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used);
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_H_
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_
|
||||
#define COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/i010_buffer.h"
|
||||
#include "api/video/i210_buffer.h"
|
||||
#include "api/video/i410_buffer.h"
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video/i422_buffer.h"
|
||||
#include "api/video/i444_buffer.h"
|
||||
#include "api/video/nv12_buffer.h"
|
||||
#include "rtc_base/race_checker.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Simple buffer pool to avoid unnecessary allocations of video frame buffers.
|
||||
// The pool manages the memory of the I420Buffer/NV12Buffer returned from
|
||||
// Create(I420|NV12)Buffer. When the buffer is destructed, the memory is
|
||||
// returned to the pool for use by subsequent calls to Create(I420|NV12)Buffer.
|
||||
// If the resolution passed to Create(I420|NV12)Buffer changes or requested
|
||||
// pixel format changes, old buffers will be purged from the pool.
|
||||
// Note that Create(I420|NV12)Buffer will crash if more than
|
||||
// kMaxNumberOfFramesBeforeCrash are created. This is to prevent memory leaks
|
||||
// where frames are not returned.
|
||||
class VideoFrameBufferPool {
|
||||
public:
|
||||
VideoFrameBufferPool();
|
||||
explicit VideoFrameBufferPool(bool zero_initialize);
|
||||
VideoFrameBufferPool(bool zero_initialize, size_t max_number_of_buffers);
|
||||
~VideoFrameBufferPool();
|
||||
|
||||
// Returns a buffer from the pool. If no suitable buffer exist in the pool
|
||||
// and there are less than `max_number_of_buffers` pending, a buffer is
|
||||
// created. Returns null otherwise.
|
||||
rtc::scoped_refptr<I420Buffer> CreateI420Buffer(int width, int height);
|
||||
rtc::scoped_refptr<I422Buffer> CreateI422Buffer(int width, int height);
|
||||
rtc::scoped_refptr<I444Buffer> CreateI444Buffer(int width, int height);
|
||||
rtc::scoped_refptr<I010Buffer> CreateI010Buffer(int width, int height);
|
||||
rtc::scoped_refptr<I210Buffer> CreateI210Buffer(int width, int height);
|
||||
rtc::scoped_refptr<I410Buffer> CreateI410Buffer(int width, int height);
|
||||
rtc::scoped_refptr<NV12Buffer> CreateNV12Buffer(int width, int height);
|
||||
|
||||
// Changes the max amount of buffers in the pool to the new value.
|
||||
// Returns true if change was successful and false if the amount of already
|
||||
// allocated buffers is bigger than new value.
|
||||
bool Resize(size_t max_number_of_buffers);
|
||||
|
||||
// Clears buffers_ and detaches the thread checker so that it can be reused
|
||||
// later from another thread.
|
||||
void Release();
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<VideoFrameBuffer>
|
||||
GetExistingBuffer(int width, int height, VideoFrameBuffer::Type type);
|
||||
|
||||
rtc::RaceChecker race_checker_;
|
||||
std::list<rtc::scoped_refptr<VideoFrameBuffer>> buffers_;
|
||||
// If true, newly allocated buffers are zero-initialized. Note that recycled
|
||||
// buffers are not zero'd before reuse. This is required of buffers used by
|
||||
// FFmpeg according to http://crbug.com/390941, which only requires it for the
|
||||
// initial allocation (as shown by FFmpeg's own buffer allocation code). It
|
||||
// has to do with "Use-of-uninitialized-value" on "Linux_msan_chrome".
|
||||
const bool zero_initialize_;
|
||||
// Max number of buffers this pool can have pending.
|
||||
size_t max_number_of_buffers_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2012 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* WebRTC's wrapper to libyuv.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_VIDEO_LIBYUV_INCLUDE_WEBRTC_LIBYUV_H_
|
||||
#define COMMON_VIDEO_LIBYUV_INCLUDE_WEBRTC_LIBYUV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum class VideoType {
|
||||
kUnknown,
|
||||
kI420,
|
||||
kIYUV,
|
||||
kRGB24,
|
||||
kBGR24,
|
||||
kARGB,
|
||||
kABGR,
|
||||
kRGB565,
|
||||
kYUY2,
|
||||
kYV12,
|
||||
kUYVY,
|
||||
kMJPEG,
|
||||
kBGRA,
|
||||
kNV12,
|
||||
};
|
||||
|
||||
// This is the max PSNR value our algorithms can return.
|
||||
const double kPerfectPSNR = 48.0f;
|
||||
|
||||
// Calculate the required buffer size.
|
||||
// Input:
|
||||
// - type :The type of the designated video frame.
|
||||
// - width :frame width in pixels.
|
||||
// - height :frame height in pixels.
|
||||
// Return value: :The required size in bytes to accommodate the specified
|
||||
// video frame.
|
||||
size_t CalcBufferSize(VideoType type, int width, int height);
|
||||
|
||||
// Extract buffer from VideoFrame or I420BufferInterface (consecutive
|
||||
// planes, no stride)
|
||||
// Input:
|
||||
// - frame : Reference to video frame.
|
||||
// - size : pointer to the size of the allocated buffer. If size is
|
||||
// insufficient, an error will be returned.
|
||||
// - buffer : Pointer to buffer
|
||||
// Return value: length of buffer if OK, < 0 otherwise.
|
||||
int ExtractBuffer(const rtc::scoped_refptr<I420BufferInterface>& input_frame,
|
||||
size_t size,
|
||||
uint8_t* buffer);
|
||||
int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer);
|
||||
// Convert From I420
|
||||
// Input:
|
||||
// - src_frame : Reference to a source frame.
|
||||
// - dst_video_type : Type of output video.
|
||||
// - dst_sample_size : Required only for the parsing of MJPG.
|
||||
// - dst_frame : Pointer to a destination frame.
|
||||
// Return value: 0 if OK, < 0 otherwise.
|
||||
// It is assumed that source and destination have equal height.
|
||||
int ConvertFromI420(const VideoFrame& src_frame,
|
||||
VideoType dst_video_type,
|
||||
int dst_sample_size,
|
||||
uint8_t* dst_frame);
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> ScaleVideoFrameBuffer(
|
||||
const I420BufferInterface& source,
|
||||
int dst_width,
|
||||
int dst_height);
|
||||
|
||||
double I420SSE(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer);
|
||||
|
||||
// Compute PSNR for an I420 frame (all planes).
|
||||
// Returns the PSNR in decibel, to a maximum of kPerfectPSNR.
|
||||
double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame);
|
||||
double I420PSNR(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer);
|
||||
|
||||
// Computes the weighted PSNR-YUV for an I420 buffer.
|
||||
//
|
||||
// For the definition and motivation, see
|
||||
// J. Ohm, G. J. Sullivan, H. Schwarz, T. K. Tan and T. Wiegand,
|
||||
// "Comparison of the Coding Efficiency of Video Coding Standards—Including
|
||||
// High Efficiency Video Coding (HEVC)," in IEEE Transactions on Circuits and
|
||||
// Systems for Video Technology, vol. 22, no. 12, pp. 1669-1684, Dec. 2012
|
||||
// doi: 10.1109/TCSVT.2012.2221192.
|
||||
//
|
||||
// Returns the PSNR-YUV in decibel, to a maximum of kPerfectPSNR.
|
||||
double I420WeightedPSNR(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer);
|
||||
|
||||
// Compute SSIM for an I420 frame (all planes).
|
||||
double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame);
|
||||
double I420SSIM(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer);
|
||||
|
||||
// Helper function for scaling NV12 to NV12.
|
||||
// If the `src_width` and `src_height` matches the `dst_width` and `dst_height`,
|
||||
// then `tmp_buffer` is not used. In other cases, the minimum size of
|
||||
// `tmp_buffer` should be:
|
||||
// (src_width/2) * (src_height/2) * 2 + (dst_width/2) * (dst_height/2) * 2
|
||||
void NV12Scale(uint8_t* tmp_buffer,
|
||||
const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int dst_width,
|
||||
int dst_height);
|
||||
|
||||
// Helper class for directly converting and scaling NV12 to I420. The Y-plane
|
||||
// will be scaled directly to the I420 destination, which makes this faster
|
||||
// than separate NV12->I420 + I420->I420 scaling.
|
||||
class RTC_EXPORT NV12ToI420Scaler {
|
||||
public:
|
||||
NV12ToI420Scaler();
|
||||
~NV12ToI420Scaler();
|
||||
void NV12ToI420Scale(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int dst_width,
|
||||
int dst_height);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> tmp_uv_planes_;
|
||||
};
|
||||
|
||||
// Convert VideoType to libyuv FourCC type
|
||||
int ConvertVideoType(VideoType video_type);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_VIDEO_LIBYUV_INCLUDE_WEBRTC_LIBYUV_H_
|
||||
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Copyright (c) 2012 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 "common_video/libyuv/include/webrtc_libyuv.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "common_video/include/video_frame_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "third_party/libyuv/include/libyuv.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
size_t CalcBufferSize(VideoType type, int width, int height) {
|
||||
RTC_DCHECK_GE(width, 0);
|
||||
RTC_DCHECK_GE(height, 0);
|
||||
switch (type) {
|
||||
case VideoType::kI420:
|
||||
case VideoType::kIYUV:
|
||||
case VideoType::kYV12:
|
||||
case VideoType::kNV12: {
|
||||
int half_width = (width + 1) >> 1;
|
||||
int half_height = (height + 1) >> 1;
|
||||
return width * height + half_width * half_height * 2;
|
||||
}
|
||||
case VideoType::kRGB565:
|
||||
case VideoType::kYUY2:
|
||||
case VideoType::kUYVY:
|
||||
return width * height * 2;
|
||||
case VideoType::kRGB24:
|
||||
case VideoType::kBGR24:
|
||||
return width * height * 3;
|
||||
case VideoType::kBGRA:
|
||||
case VideoType::kARGB:
|
||||
case VideoType::kABGR:
|
||||
return width * height * 4;
|
||||
case VideoType::kMJPEG:
|
||||
case VideoType::kUnknown:
|
||||
break;
|
||||
}
|
||||
RTC_DCHECK_NOTREACHED() << "Unexpected pixel format " << type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExtractBuffer(const rtc::scoped_refptr<I420BufferInterface>& input_frame,
|
||||
size_t size,
|
||||
uint8_t* buffer) {
|
||||
RTC_DCHECK(buffer);
|
||||
if (!input_frame)
|
||||
return -1;
|
||||
int width = input_frame->width();
|
||||
int height = input_frame->height();
|
||||
size_t length = CalcBufferSize(VideoType::kI420, width, height);
|
||||
if (size < length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int chroma_width = input_frame->ChromaWidth();
|
||||
int chroma_height = input_frame->ChromaHeight();
|
||||
|
||||
libyuv::I420Copy(input_frame->DataY(), input_frame->StrideY(),
|
||||
input_frame->DataU(), input_frame->StrideU(),
|
||||
input_frame->DataV(), input_frame->StrideV(), buffer, width,
|
||||
buffer + width * height, chroma_width,
|
||||
buffer + width * height + chroma_width * chroma_height,
|
||||
chroma_width, width, height);
|
||||
|
||||
return static_cast<int>(length);
|
||||
}
|
||||
|
||||
int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer) {
|
||||
return ExtractBuffer(input_frame.video_frame_buffer()->ToI420(), size,
|
||||
buffer);
|
||||
}
|
||||
|
||||
int ConvertVideoType(VideoType video_type) {
|
||||
switch (video_type) {
|
||||
case VideoType::kUnknown:
|
||||
return libyuv::FOURCC_ANY;
|
||||
case VideoType::kI420:
|
||||
return libyuv::FOURCC_I420;
|
||||
case VideoType::kIYUV: // same as VideoType::kYV12
|
||||
case VideoType::kYV12:
|
||||
return libyuv::FOURCC_YV12;
|
||||
case VideoType::kRGB24:
|
||||
return libyuv::FOURCC_24BG;
|
||||
case VideoType::kBGR24:
|
||||
return libyuv::FOURCC_RAW;
|
||||
case VideoType::kABGR:
|
||||
return libyuv::FOURCC_ABGR;
|
||||
case VideoType::kRGB565:
|
||||
return libyuv::FOURCC_RGBP;
|
||||
case VideoType::kYUY2:
|
||||
return libyuv::FOURCC_YUY2;
|
||||
case VideoType::kUYVY:
|
||||
return libyuv::FOURCC_UYVY;
|
||||
case VideoType::kMJPEG:
|
||||
return libyuv::FOURCC_MJPG;
|
||||
case VideoType::kARGB:
|
||||
return libyuv::FOURCC_ARGB;
|
||||
case VideoType::kBGRA:
|
||||
return libyuv::FOURCC_BGRA;
|
||||
case VideoType::kNV12:
|
||||
return libyuv::FOURCC_NV12;
|
||||
}
|
||||
RTC_DCHECK_NOTREACHED() << "Unexpected pixel format " << video_type;
|
||||
return libyuv::FOURCC_ANY;
|
||||
}
|
||||
|
||||
int ConvertFromI420(const VideoFrame& src_frame,
|
||||
VideoType dst_video_type,
|
||||
int dst_sample_size,
|
||||
uint8_t* dst_frame) {
|
||||
rtc::scoped_refptr<I420BufferInterface> i420_buffer =
|
||||
src_frame.video_frame_buffer()->ToI420();
|
||||
return libyuv::ConvertFromI420(
|
||||
i420_buffer->DataY(), i420_buffer->StrideY(), i420_buffer->DataU(),
|
||||
i420_buffer->StrideU(), i420_buffer->DataV(), i420_buffer->StrideV(),
|
||||
dst_frame, dst_sample_size, src_frame.width(), src_frame.height(),
|
||||
ConvertVideoType(dst_video_type));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I420ABufferInterface> ScaleI420ABuffer(
|
||||
const I420ABufferInterface& buffer,
|
||||
int target_width,
|
||||
int target_height) {
|
||||
rtc::scoped_refptr<I420Buffer> yuv_buffer =
|
||||
I420Buffer::Create(target_width, target_height);
|
||||
yuv_buffer->ScaleFrom(buffer);
|
||||
rtc::scoped_refptr<I420Buffer> axx_buffer =
|
||||
I420Buffer::Create(target_width, target_height);
|
||||
libyuv::ScalePlane(buffer.DataA(), buffer.StrideA(), buffer.width(),
|
||||
buffer.height(), axx_buffer->MutableDataY(),
|
||||
axx_buffer->StrideY(), target_width, target_height,
|
||||
libyuv::kFilterBox);
|
||||
rtc::scoped_refptr<I420ABufferInterface> merged_buffer = WrapI420ABuffer(
|
||||
yuv_buffer->width(), yuv_buffer->height(), yuv_buffer->DataY(),
|
||||
yuv_buffer->StrideY(), yuv_buffer->DataU(), yuv_buffer->StrideU(),
|
||||
yuv_buffer->DataV(), yuv_buffer->StrideV(), axx_buffer->DataY(),
|
||||
axx_buffer->StrideY(),
|
||||
// To keep references alive.
|
||||
[yuv_buffer, axx_buffer] {});
|
||||
return merged_buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> ScaleVideoFrameBuffer(
|
||||
const I420BufferInterface& source,
|
||||
int dst_width,
|
||||
int dst_height) {
|
||||
rtc::scoped_refptr<I420Buffer> scaled_buffer =
|
||||
I420Buffer::Create(dst_width, dst_height);
|
||||
scaled_buffer->ScaleFrom(source);
|
||||
return scaled_buffer;
|
||||
}
|
||||
|
||||
double I420SSE(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer) {
|
||||
RTC_DCHECK_EQ(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_EQ(ref_buffer.height(), test_buffer.height());
|
||||
const uint64_t width = test_buffer.width();
|
||||
const uint64_t height = test_buffer.height();
|
||||
const uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
|
||||
test_buffer.StrideY(), width, height);
|
||||
const int width_uv = (width + 1) >> 1;
|
||||
const int height_uv = (height + 1) >> 1;
|
||||
const uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), width_uv, height_uv);
|
||||
const uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
|
||||
test_buffer.StrideV(), width_uv, height_uv);
|
||||
const double samples = width * height + 2 * (width_uv * height_uv);
|
||||
const double sse = sse_y + sse_u + sse_v;
|
||||
return sse / (samples * 255.0 * 255.0);
|
||||
}
|
||||
|
||||
// Compute PSNR for an I420A frame (all planes). Can upscale test frame.
|
||||
double I420APSNR(const I420ABufferInterface& ref_buffer,
|
||||
const I420ABufferInterface& test_buffer) {
|
||||
RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
|
||||
if ((ref_buffer.width() != test_buffer.width()) ||
|
||||
(ref_buffer.height() != test_buffer.height())) {
|
||||
rtc::scoped_refptr<I420ABufferInterface> scaled_buffer =
|
||||
ScaleI420ABuffer(test_buffer, ref_buffer.width(), ref_buffer.height());
|
||||
return I420APSNR(ref_buffer, *scaled_buffer);
|
||||
}
|
||||
const int width = test_buffer.width();
|
||||
const int height = test_buffer.height();
|
||||
const uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
|
||||
test_buffer.StrideY(), width, height);
|
||||
const int width_uv = (width + 1) >> 1;
|
||||
const int height_uv = (height + 1) >> 1;
|
||||
const uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), width_uv, height_uv);
|
||||
const uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
|
||||
test_buffer.StrideV(), width_uv, height_uv);
|
||||
const uint64_t sse_a = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataA(), ref_buffer.StrideA(), test_buffer.DataA(),
|
||||
test_buffer.StrideA(), width, height);
|
||||
const uint64_t samples = 2 * (uint64_t)width * (uint64_t)height +
|
||||
2 * ((uint64_t)width_uv * (uint64_t)height_uv);
|
||||
const uint64_t sse = sse_y + sse_u + sse_v + sse_a;
|
||||
const double psnr = libyuv::SumSquareErrorToPsnr(sse, samples);
|
||||
return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
|
||||
}
|
||||
|
||||
// Compute PSNR for an I420A frame (all planes)
|
||||
double I420APSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
|
||||
if (!ref_frame || !test_frame)
|
||||
return -1;
|
||||
RTC_DCHECK(ref_frame->video_frame_buffer()->type() ==
|
||||
VideoFrameBuffer::Type::kI420A);
|
||||
RTC_DCHECK(test_frame->video_frame_buffer()->type() ==
|
||||
VideoFrameBuffer::Type::kI420A);
|
||||
return I420APSNR(*ref_frame->video_frame_buffer()->GetI420A(),
|
||||
*test_frame->video_frame_buffer()->GetI420A());
|
||||
}
|
||||
|
||||
// Compute PSNR for an I420 frame (all planes). Can upscale test frame.
|
||||
double I420PSNR(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer) {
|
||||
RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
|
||||
if ((ref_buffer.width() != test_buffer.width()) ||
|
||||
(ref_buffer.height() != test_buffer.height())) {
|
||||
rtc::scoped_refptr<I420Buffer> scaled_buffer =
|
||||
I420Buffer::Create(ref_buffer.width(), ref_buffer.height());
|
||||
scaled_buffer->ScaleFrom(test_buffer);
|
||||
return I420PSNR(ref_buffer, *scaled_buffer);
|
||||
}
|
||||
double psnr = libyuv::I420Psnr(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
|
||||
ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
|
||||
test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
|
||||
test_buffer.width(), test_buffer.height());
|
||||
// LibYuv sets the max psnr value to 128, we restrict it here.
|
||||
// In case of 0 mse in one frame, 128 can skew the results significantly.
|
||||
return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
|
||||
}
|
||||
|
||||
// Compute PSNR for an I420 frame (all planes)
|
||||
double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
|
||||
if (!ref_frame || !test_frame)
|
||||
return -1;
|
||||
return I420PSNR(*ref_frame->video_frame_buffer()->ToI420(),
|
||||
*test_frame->video_frame_buffer()->ToI420());
|
||||
}
|
||||
|
||||
double I420WeightedPSNR(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer) {
|
||||
RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
|
||||
if ((ref_buffer.width() != test_buffer.width()) ||
|
||||
(ref_buffer.height() != test_buffer.height())) {
|
||||
rtc::scoped_refptr<I420Buffer> scaled_ref_buffer =
|
||||
I420Buffer::Create(test_buffer.width(), test_buffer.height());
|
||||
scaled_ref_buffer->ScaleFrom(ref_buffer);
|
||||
return I420WeightedPSNR(*scaled_ref_buffer, test_buffer);
|
||||
}
|
||||
|
||||
// Luma.
|
||||
int width_y = test_buffer.width();
|
||||
int height_y = test_buffer.height();
|
||||
uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
|
||||
test_buffer.StrideY(), width_y, height_y);
|
||||
uint64_t num_samples_y = (uint64_t)width_y * (uint64_t)height_y;
|
||||
double psnr_y = libyuv::SumSquareErrorToPsnr(sse_y, num_samples_y);
|
||||
|
||||
// Chroma.
|
||||
int width_uv = (width_y + 1) >> 1;
|
||||
int height_uv = (height_y + 1) >> 1;
|
||||
uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), width_uv, height_uv);
|
||||
uint64_t num_samples_uv = (uint64_t)width_uv * (uint64_t)height_uv;
|
||||
double psnr_u = libyuv::SumSquareErrorToPsnr(sse_u, num_samples_uv);
|
||||
uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
|
||||
ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
|
||||
test_buffer.StrideV(), width_uv, height_uv);
|
||||
double psnr_v = libyuv::SumSquareErrorToPsnr(sse_v, num_samples_uv);
|
||||
|
||||
// Weights from Ohm et. al 2012.
|
||||
double psnr_yuv = (6.0 * psnr_y + psnr_u + psnr_v) / 8.0;
|
||||
return (psnr_yuv > kPerfectPSNR) ? kPerfectPSNR : psnr_yuv;
|
||||
}
|
||||
|
||||
// Compute SSIM for an I420A frame (all planes). Can upscale test frame.
|
||||
double I420ASSIM(const I420ABufferInterface& ref_buffer,
|
||||
const I420ABufferInterface& test_buffer) {
|
||||
RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
|
||||
if ((ref_buffer.width() != test_buffer.width()) ||
|
||||
(ref_buffer.height() != test_buffer.height())) {
|
||||
rtc::scoped_refptr<I420ABufferInterface> scaled_buffer =
|
||||
ScaleI420ABuffer(test_buffer, ref_buffer.width(), ref_buffer.height());
|
||||
return I420ASSIM(ref_buffer, *scaled_buffer);
|
||||
}
|
||||
const double yuv_ssim = libyuv::I420Ssim(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
|
||||
ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
|
||||
test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
|
||||
test_buffer.width(), test_buffer.height());
|
||||
const double a_ssim = libyuv::CalcFrameSsim(
|
||||
ref_buffer.DataA(), ref_buffer.StrideA(), test_buffer.DataA(),
|
||||
test_buffer.StrideA(), test_buffer.width(), test_buffer.height());
|
||||
return (yuv_ssim + (a_ssim * 0.8)) / 1.8;
|
||||
}
|
||||
|
||||
// Compute SSIM for an I420A frame (all planes)
|
||||
double I420ASSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
|
||||
if (!ref_frame || !test_frame)
|
||||
return -1;
|
||||
RTC_DCHECK(ref_frame->video_frame_buffer()->type() ==
|
||||
VideoFrameBuffer::Type::kI420A);
|
||||
RTC_DCHECK(test_frame->video_frame_buffer()->type() ==
|
||||
VideoFrameBuffer::Type::kI420A);
|
||||
return I420ASSIM(*ref_frame->video_frame_buffer()->GetI420A(),
|
||||
*test_frame->video_frame_buffer()->GetI420A());
|
||||
}
|
||||
|
||||
// Compute SSIM for an I420 frame (all planes). Can upscale test_buffer.
|
||||
double I420SSIM(const I420BufferInterface& ref_buffer,
|
||||
const I420BufferInterface& test_buffer) {
|
||||
RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
|
||||
RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
|
||||
if ((ref_buffer.width() != test_buffer.width()) ||
|
||||
(ref_buffer.height() != test_buffer.height())) {
|
||||
rtc::scoped_refptr<I420Buffer> scaled_buffer =
|
||||
I420Buffer::Create(ref_buffer.width(), ref_buffer.height());
|
||||
scaled_buffer->ScaleFrom(test_buffer);
|
||||
return I420SSIM(ref_buffer, *scaled_buffer);
|
||||
}
|
||||
return libyuv::I420Ssim(
|
||||
ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
|
||||
ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
|
||||
test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
|
||||
test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
|
||||
test_buffer.width(), test_buffer.height());
|
||||
}
|
||||
|
||||
double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
|
||||
if (!ref_frame || !test_frame)
|
||||
return -1;
|
||||
return I420SSIM(*ref_frame->video_frame_buffer()->ToI420(),
|
||||
*test_frame->video_frame_buffer()->ToI420());
|
||||
}
|
||||
|
||||
void NV12Scale(uint8_t* tmp_buffer,
|
||||
const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int dst_width,
|
||||
int dst_height) {
|
||||
const int src_chroma_width = (src_width + 1) / 2;
|
||||
const int src_chroma_height = (src_height + 1) / 2;
|
||||
|
||||
if (src_width == dst_width && src_height == dst_height) {
|
||||
// No scaling.
|
||||
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, src_width,
|
||||
src_height);
|
||||
libyuv::CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv,
|
||||
src_chroma_width * 2, src_chroma_height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scaling.
|
||||
// Allocate temporary memory for spitting UV planes and scaling them.
|
||||
const int dst_chroma_width = (dst_width + 1) / 2;
|
||||
const int dst_chroma_height = (dst_height + 1) / 2;
|
||||
|
||||
uint8_t* const src_u = tmp_buffer;
|
||||
uint8_t* const src_v = src_u + src_chroma_width * src_chroma_height;
|
||||
uint8_t* const dst_u = src_v + src_chroma_width * src_chroma_height;
|
||||
uint8_t* const dst_v = dst_u + dst_chroma_width * dst_chroma_height;
|
||||
|
||||
// Split source UV plane into separate U and V plane using the temporary data.
|
||||
libyuv::SplitUVPlane(src_uv, src_stride_uv, src_u, src_chroma_width, src_v,
|
||||
src_chroma_width, src_chroma_width, src_chroma_height);
|
||||
|
||||
// Scale the planes.
|
||||
libyuv::I420Scale(
|
||||
src_y, src_stride_y, src_u, src_chroma_width, src_v, src_chroma_width,
|
||||
src_width, src_height, dst_y, dst_stride_y, dst_u, dst_chroma_width,
|
||||
dst_v, dst_chroma_width, dst_width, dst_height, libyuv::kFilterBox);
|
||||
|
||||
// Merge the UV planes into the destination.
|
||||
libyuv::MergeUVPlane(dst_u, dst_chroma_width, dst_v, dst_chroma_width, dst_uv,
|
||||
dst_stride_uv, dst_chroma_width, dst_chroma_height);
|
||||
}
|
||||
|
||||
NV12ToI420Scaler::NV12ToI420Scaler() = default;
|
||||
NV12ToI420Scaler::~NV12ToI420Scaler() = default;
|
||||
|
||||
void NV12ToI420Scaler::NV12ToI420Scale(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int dst_width,
|
||||
int dst_height) {
|
||||
if (src_width == dst_width && src_height == dst_height) {
|
||||
// No scaling.
|
||||
tmp_uv_planes_.clear();
|
||||
tmp_uv_planes_.shrink_to_fit();
|
||||
libyuv::NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
|
||||
src_width, src_height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scaling.
|
||||
// Allocate temporary memory for spitting UV planes.
|
||||
const int src_uv_width = (src_width + 1) / 2;
|
||||
const int src_uv_height = (src_height + 1) / 2;
|
||||
tmp_uv_planes_.resize(src_uv_width * src_uv_height * 2);
|
||||
tmp_uv_planes_.shrink_to_fit();
|
||||
|
||||
// Split source UV plane into separate U and V plane using the temporary data.
|
||||
uint8_t* const src_u = tmp_uv_planes_.data();
|
||||
uint8_t* const src_v = tmp_uv_planes_.data() + src_uv_width * src_uv_height;
|
||||
libyuv::SplitUVPlane(src_uv, src_stride_uv, src_u, src_uv_width, src_v,
|
||||
src_uv_width, src_uv_width, src_uv_height);
|
||||
|
||||
// Scale the planes into the destination.
|
||||
libyuv::I420Scale(src_y, src_stride_y, src_u, src_uv_width, src_v,
|
||||
src_uv_width, src_width, src_height, dst_y, dst_stride_y,
|
||||
dst_u, dst_stride_u, dst_v, dst_stride_v, dst_width,
|
||||
dst_height, libyuv::kFilterBox);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
388
TMessagesProj/jni/voip/webrtc/common_video/video_frame_buffer.cc
Normal file
388
TMessagesProj/jni/voip/webrtc/common_video/video_frame_buffer.cc
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 "common_video/include/video_frame_buffer.h"
|
||||
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
// Template to implement a wrapped buffer for a I4??BufferInterface.
|
||||
template <typename Base>
|
||||
class WrappedYuvBuffer : public Base {
|
||||
public:
|
||||
WrappedYuvBuffer(int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
y_plane_(y_plane),
|
||||
u_plane_(u_plane),
|
||||
v_plane_(v_plane),
|
||||
y_stride_(y_stride),
|
||||
u_stride_(u_stride),
|
||||
v_stride_(v_stride),
|
||||
no_longer_used_cb_(no_longer_used) {}
|
||||
|
||||
~WrappedYuvBuffer() override { no_longer_used_cb_(); }
|
||||
|
||||
int width() const override { return width_; }
|
||||
|
||||
int height() const override { return height_; }
|
||||
|
||||
const uint8_t* DataY() const override { return y_plane_; }
|
||||
|
||||
const uint8_t* DataU() const override { return u_plane_; }
|
||||
|
||||
const uint8_t* DataV() const override { return v_plane_; }
|
||||
|
||||
int StrideY() const override { return y_stride_; }
|
||||
|
||||
int StrideU() const override { return u_stride_; }
|
||||
|
||||
int StrideV() const override { return v_stride_; }
|
||||
|
||||
private:
|
||||
friend class rtc::RefCountedObject<WrappedYuvBuffer>;
|
||||
|
||||
const int width_;
|
||||
const int height_;
|
||||
const uint8_t* const y_plane_;
|
||||
const uint8_t* const u_plane_;
|
||||
const uint8_t* const v_plane_;
|
||||
const int y_stride_;
|
||||
const int u_stride_;
|
||||
const int v_stride_;
|
||||
std::function<void()> no_longer_used_cb_;
|
||||
};
|
||||
|
||||
// Template to implement a wrapped buffer for a I4??BufferInterface.
|
||||
template <typename BaseWithA>
|
||||
class WrappedYuvaBuffer : public WrappedYuvBuffer<BaseWithA> {
|
||||
public:
|
||||
WrappedYuvaBuffer(int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
const uint8_t* a_plane,
|
||||
int a_stride,
|
||||
std::function<void()> no_longer_used)
|
||||
: WrappedYuvBuffer<BaseWithA>(width,
|
||||
height,
|
||||
y_plane,
|
||||
y_stride,
|
||||
u_plane,
|
||||
u_stride,
|
||||
v_plane,
|
||||
v_stride,
|
||||
no_longer_used),
|
||||
a_plane_(a_plane),
|
||||
a_stride_(a_stride) {}
|
||||
|
||||
const uint8_t* DataA() const override { return a_plane_; }
|
||||
int StrideA() const override { return a_stride_; }
|
||||
|
||||
private:
|
||||
const uint8_t* const a_plane_;
|
||||
const int a_stride_;
|
||||
};
|
||||
|
||||
class I444BufferBase : public I444BufferInterface {
|
||||
public:
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
};
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I444BufferBase::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
class I422BufferBase : public I422BufferInterface {
|
||||
public:
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
};
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I422BufferBase::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I422ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
// Template to implement a wrapped buffer for a PlanarYuv16BBuffer.
|
||||
template <typename Base>
|
||||
class WrappedYuv16BBuffer : public Base {
|
||||
public:
|
||||
WrappedYuv16BBuffer(int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
y_plane_(y_plane),
|
||||
u_plane_(u_plane),
|
||||
v_plane_(v_plane),
|
||||
y_stride_(y_stride),
|
||||
u_stride_(u_stride),
|
||||
v_stride_(v_stride),
|
||||
no_longer_used_cb_(no_longer_used) {}
|
||||
|
||||
~WrappedYuv16BBuffer() override { no_longer_used_cb_(); }
|
||||
|
||||
int width() const override { return width_; }
|
||||
|
||||
int height() const override { return height_; }
|
||||
|
||||
const uint16_t* DataY() const override { return y_plane_; }
|
||||
|
||||
const uint16_t* DataU() const override { return u_plane_; }
|
||||
|
||||
const uint16_t* DataV() const override { return v_plane_; }
|
||||
|
||||
int StrideY() const override { return y_stride_; }
|
||||
|
||||
int StrideU() const override { return u_stride_; }
|
||||
|
||||
int StrideV() const override { return v_stride_; }
|
||||
|
||||
private:
|
||||
friend class rtc::RefCountedObject<WrappedYuv16BBuffer>;
|
||||
|
||||
const int width_;
|
||||
const int height_;
|
||||
const uint16_t* const y_plane_;
|
||||
const uint16_t* const u_plane_;
|
||||
const uint16_t* const v_plane_;
|
||||
const int y_stride_;
|
||||
const int u_stride_;
|
||||
const int v_stride_;
|
||||
std::function<void()> no_longer_used_cb_;
|
||||
};
|
||||
|
||||
class I010BufferBase : public I010BufferInterface {
|
||||
public:
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
};
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I010BufferBase::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I010ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
class I210BufferBase : public I210BufferInterface {
|
||||
public:
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
};
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I210BufferBase::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I210ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
class I410BufferBase : public I410BufferInterface {
|
||||
public:
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
};
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I410BufferBase::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I410ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> WrapI420Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I420BufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuvBuffer<I420BufferInterface>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I420ABufferInterface> WrapI420ABuffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
const uint8_t* a_plane,
|
||||
int a_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I420ABufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuvaBuffer<I420ABufferInterface>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, a_plane, a_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I422BufferInterface> WrapI422Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I422BufferBase>(
|
||||
rtc::make_ref_counted<WrappedYuvBuffer<I422BufferBase>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I444BufferInterface> WrapI444Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I444BufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuvBuffer<I444BufferBase>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<PlanarYuvBuffer> WrapYuvBuffer(
|
||||
VideoFrameBuffer::Type type,
|
||||
int width,
|
||||
int height,
|
||||
const uint8_t* y_plane,
|
||||
int y_stride,
|
||||
const uint8_t* u_plane,
|
||||
int u_stride,
|
||||
const uint8_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
switch (type) {
|
||||
case VideoFrameBuffer::Type::kI420:
|
||||
return WrapI420Buffer(width, height, y_plane, y_stride, u_plane, u_stride,
|
||||
v_plane, v_stride, no_longer_used);
|
||||
case VideoFrameBuffer::Type::kI422:
|
||||
return WrapI422Buffer(width, height, y_plane, y_stride, u_plane, u_stride,
|
||||
v_plane, v_stride, no_longer_used);
|
||||
case VideoFrameBuffer::Type::kI444:
|
||||
return WrapI444Buffer(width, height, y_plane, y_stride, u_plane, u_stride,
|
||||
v_plane, v_stride, no_longer_used);
|
||||
default:
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I010BufferInterface> WrapI010Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I010BufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuv16BBuffer<I010BufferBase>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I210BufferInterface> WrapI210Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I210BufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuv16BBuffer<I210BufferBase>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I410BufferInterface> WrapI410Buffer(
|
||||
int width,
|
||||
int height,
|
||||
const uint16_t* y_plane,
|
||||
int y_stride,
|
||||
const uint16_t* u_plane,
|
||||
int u_stride,
|
||||
const uint16_t* v_plane,
|
||||
int v_stride,
|
||||
std::function<void()> no_longer_used) {
|
||||
return rtc::scoped_refptr<I410BufferInterface>(
|
||||
rtc::make_ref_counted<WrappedYuv16BBuffer<I410BufferBase>>(
|
||||
width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
|
||||
v_stride, no_longer_used));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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 "common_video/include/video_frame_buffer_pool.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) {
|
||||
// Cast to rtc::RefCountedObject is safe because this function is only called
|
||||
// on locally created VideoFrameBuffers, which are either
|
||||
// `rtc::RefCountedObject<I420Buffer>`, `rtc::RefCountedObject<I444Buffer>` or
|
||||
// `rtc::RefCountedObject<NV12Buffer>`.
|
||||
switch (buffer->type()) {
|
||||
case VideoFrameBuffer::Type::kI420: {
|
||||
return static_cast<rtc::RefCountedObject<I420Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kI444: {
|
||||
return static_cast<rtc::RefCountedObject<I444Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kI422: {
|
||||
return static_cast<rtc::RefCountedObject<I422Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kI010: {
|
||||
return static_cast<rtc::RefCountedObject<I010Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kI210: {
|
||||
return static_cast<rtc::RefCountedObject<I210Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kI410: {
|
||||
return static_cast<rtc::RefCountedObject<I410Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kNV12: {
|
||||
return static_cast<rtc::RefCountedObject<NV12Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {}
|
||||
|
||||
VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize)
|
||||
: VideoFrameBufferPool(zero_initialize,
|
||||
std::numeric_limits<size_t>::max()) {}
|
||||
|
||||
VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize,
|
||||
size_t max_number_of_buffers)
|
||||
: zero_initialize_(zero_initialize),
|
||||
max_number_of_buffers_(max_number_of_buffers) {}
|
||||
|
||||
VideoFrameBufferPool::~VideoFrameBufferPool() = default;
|
||||
|
||||
void VideoFrameBufferPool::Release() {
|
||||
buffers_.clear();
|
||||
}
|
||||
|
||||
bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
size_t used_buffers_count = 0;
|
||||
for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
|
||||
// If the buffer is in use, the ref count will be >= 2, one from the list we
|
||||
// are looping over and one from the application. If the ref count is 1,
|
||||
// then the list we are looping over holds the only reference and it's safe
|
||||
// to reuse.
|
||||
if (!HasOneRef(buffer)) {
|
||||
used_buffers_count++;
|
||||
}
|
||||
}
|
||||
if (used_buffers_count > max_number_of_buffers) {
|
||||
return false;
|
||||
}
|
||||
max_number_of_buffers_ = max_number_of_buffers;
|
||||
|
||||
size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_;
|
||||
auto iter = buffers_.begin();
|
||||
while (iter != buffers_.end() && buffers_to_purge > 0) {
|
||||
if (HasOneRef(*iter)) {
|
||||
iter = buffers_.erase(iter);
|
||||
buffers_to_purge--;
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI420 buffer is created is
|
||||
// in the same function below, where `RefCountedObject<I420Buffer>` is
|
||||
// created.
|
||||
rtc::RefCountedObject<I420Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I420Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I420Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I420Buffer> buffer =
|
||||
rtc::make_ref_counted<I420Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I444Buffer> VideoFrameBufferPool::CreateI444Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI444);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI444 buffer is created is
|
||||
// in the same function below, where |RefCountedObject<I444Buffer>|
|
||||
// is created.
|
||||
rtc::RefCountedObject<I444Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I444Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I444Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I444Buffer> buffer =
|
||||
rtc::make_ref_counted<I444Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I422Buffer> VideoFrameBufferPool::CreateI422Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI422);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI422 buffer is created is
|
||||
// in the same function below, where |RefCountedObject<I422Buffer>|
|
||||
// is created.
|
||||
rtc::RefCountedObject<I422Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I422Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I422Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I422Buffer> buffer =
|
||||
rtc::make_ref_counted<I422Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI420 buffer is created is
|
||||
// in the same function below, where `RefCountedObject<I420Buffer>` is
|
||||
// created.
|
||||
rtc::RefCountedObject<NV12Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<NV12Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<NV12Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<NV12Buffer> buffer =
|
||||
rtc::make_ref_counted<NV12Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I010Buffer> VideoFrameBufferPool::CreateI010Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI010);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI010 buffer is created is
|
||||
// in the same function below, where |RefCountedObject<I010Buffer>|
|
||||
// is created.
|
||||
rtc::RefCountedObject<I010Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I010Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I010Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I010Buffer> buffer = I010Buffer::Create(width, height);
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I210Buffer> VideoFrameBufferPool::CreateI210Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI210);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI210 buffer is created is
|
||||
// in the same function below, where |RefCountedObject<I210Buffer>|
|
||||
// is created.
|
||||
rtc::RefCountedObject<I210Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I210Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I210Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I210Buffer> buffer = I210Buffer::Create(width, height);
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I410Buffer> VideoFrameBufferPool::CreateI410Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
|
||||
GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI410);
|
||||
if (existing_buffer) {
|
||||
// Cast is safe because the only way kI410 buffer is created is
|
||||
// in the same function below, where |RefCountedObject<I410Buffer>|
|
||||
// is created.
|
||||
rtc::RefCountedObject<I410Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I410Buffer>*>(existing_buffer.get());
|
||||
// Creates a new scoped_refptr, which is also pointing to the same
|
||||
// RefCountedObject as buffer, increasing ref count.
|
||||
return rtc::scoped_refptr<I410Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I410Buffer> buffer = I410Buffer::Create(width, height);
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBufferPool::GetExistingBuffer(
|
||||
int width,
|
||||
int height,
|
||||
VideoFrameBuffer::Type type) {
|
||||
// Release buffers with wrong resolution or different type.
|
||||
for (auto it = buffers_.begin(); it != buffers_.end();) {
|
||||
const auto& buffer = *it;
|
||||
if (buffer->width() != width || buffer->height() != height ||
|
||||
buffer->type() != type) {
|
||||
it = buffers_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// Look for a free buffer.
|
||||
for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
|
||||
// If the buffer is in use, the ref count will be >= 2, one from the list we
|
||||
// are looping over and one from the application. If the ref count is 1,
|
||||
// then the list we are looping over holds the only reference and it's safe
|
||||
// to reuse.
|
||||
if (HasOneRef(buffer)) {
|
||||
RTC_CHECK(buffer->type() == type);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
Add table
Add a link
Reference in a new issue