Skip to content

Commit 7066875

Browse files
authored
Enable H265 encoding & decoding on Nvidia GPU (#776)
* Add h265 encoder & decoder support to webrtc-sys
1 parent d898212 commit 7066875

File tree

8 files changed

+723
-6
lines changed

8 files changed

+723
-6
lines changed

webrtc-sys/build.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ fn main() {
191191
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
192192
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
193193
.file("src/nvidia/h264_encoder_impl.cpp")
194+
.file("src/nvidia/h265_encoder_impl.cpp")
194195
.file("src/nvidia/h264_decoder_impl.cpp")
196+
.file("src/nvidia/h265_decoder_impl.cpp")
195197
.file("src/nvidia/nvidia_decoder_factory.cpp")
196198
.file("src/nvidia/nvidia_encoder_factory.cpp")
197199
.file("src/nvidia/cuda_context.cpp")

webrtc-sys/src/nvidia/h264_encoder_impl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ int32_t NvidiaH264EncoderImpl::InitEncode(
238238
return WEBRTC_VIDEO_CODEC_ERROR;
239239
}
240240

241+
RTC_LOG(LS_INFO) << "NVIDIA H264 NVENC initialized: "
242+
<< codec_.width << "x" << codec_.height
243+
<< " @ " << codec_.maxFramerate << "fps, target_bps="
244+
<< configuration_.target_bps;
245+
241246
SimulcastRateAllocator init_allocator(env_, codec_);
242247
VideoBitrateAllocation allocation =
243248
init_allocator.Allocate(VideoBitrateAllocationParameters(
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#include "h265_decoder_impl.h"
2+
3+
#include <api/video/i420_buffer.h>
4+
#include <api/video/video_codec_type.h>
5+
#include <modules/video_coding/include/video_error_codes.h>
6+
#include <third_party/libyuv/include/libyuv/convert.h>
7+
8+
#include "NvDecoder/NvDecoder.h"
9+
#include "Utils/NvCodecUtils.h"
10+
#include "rtc_base/checks.h"
11+
#include "rtc_base/logging.h"
12+
13+
namespace webrtc {
14+
15+
static ColorSpace ExtractColorSpaceFromFormat(const CUVIDEOFORMAT& format) {
16+
return ColorSpace(
17+
static_cast<ColorSpace::PrimaryID>(
18+
format.video_signal_description.color_primaries),
19+
static_cast<ColorSpace::TransferID>(
20+
format.video_signal_description.transfer_characteristics),
21+
static_cast<ColorSpace::MatrixID>(
22+
format.video_signal_description.matrix_coefficients),
23+
static_cast<ColorSpace::RangeID>(
24+
format.video_signal_description.video_full_range_flag));
25+
}
26+
27+
NvidiaH265DecoderImpl::NvidiaH265DecoderImpl(CUcontext context)
28+
: cu_context_(context),
29+
decoder_(nullptr),
30+
is_configured_decoder_(false),
31+
decoded_complete_callback_(nullptr),
32+
buffer_pool_(false) {}
33+
34+
NvidiaH265DecoderImpl::~NvidiaH265DecoderImpl() { Release(); }
35+
36+
VideoDecoder::DecoderInfo NvidiaH265DecoderImpl::GetDecoderInfo() const {
37+
VideoDecoder::DecoderInfo info;
38+
info.implementation_name = "NVIDIA H265 Decoder";
39+
info.is_hardware_accelerated = true;
40+
return info;
41+
}
42+
43+
bool NvidiaH265DecoderImpl::Configure(const Settings& settings) {
44+
if (settings.codec_type() != kVideoCodecH265) {
45+
RTC_LOG(LS_ERROR) << "initialization failed: codec type is not H265";
46+
return false;
47+
}
48+
if (!settings.max_render_resolution().Valid()) {
49+
RTC_LOG(LS_ERROR)
50+
<< "initialization failed on codec_settings width < 0 or height < 0";
51+
return false;
52+
}
53+
54+
settings_ = settings;
55+
56+
const CUresult result = cuCtxSetCurrent(cu_context_);
57+
if (!ck(result)) {
58+
RTC_LOG(LS_ERROR) << "initialization failed on cuCtxSetCurrent result"
59+
<< result;
60+
return false;
61+
}
62+
63+
int maxWidth = 4096;
64+
int maxHeight = 4096;
65+
66+
decoder_ = std::make_unique<NvDecoder>(
67+
cu_context_, false, cudaVideoCodec_HEVC, true, false, nullptr, nullptr,
68+
false, maxWidth, maxHeight);
69+
return true;
70+
}
71+
72+
int32_t NvidiaH265DecoderImpl::RegisterDecodeCompleteCallback(
73+
DecodedImageCallback* callback) {
74+
decoded_complete_callback_ = callback;
75+
return WEBRTC_VIDEO_CODEC_OK;
76+
}
77+
78+
int32_t NvidiaH265DecoderImpl::Release() {
79+
buffer_pool_.Release();
80+
return WEBRTC_VIDEO_CODEC_OK;
81+
}
82+
83+
int32_t NvidiaH265DecoderImpl::Decode(const EncodedImage& input_image,
84+
bool /*missing_frames*/,
85+
int64_t /*render_time_ms*/) {
86+
CUcontext current;
87+
if (!ck(cuCtxGetCurrent(&current))) {
88+
RTC_LOG(LS_ERROR) << "decode failed on cuCtxGetCurrent";
89+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
90+
}
91+
if (current != cu_context_) {
92+
RTC_LOG(LS_ERROR)
93+
<< "decode failed: current context does not match held context";
94+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
95+
}
96+
if (decoded_complete_callback_ == nullptr) {
97+
RTC_LOG(LS_ERROR) << "decode failed: decoded_complete_callback_ not set";
98+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
99+
}
100+
if (!input_image.data() || !input_image.size()) {
101+
RTC_LOG(LS_ERROR) << "decode failed: input image is null";
102+
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
103+
}
104+
105+
int nFrameReturned = 0;
106+
do {
107+
nFrameReturned = decoder_->Decode(
108+
input_image.data(), static_cast<int>(input_image.size()),
109+
CUVID_PKT_TIMESTAMP, input_image.RtpTimestamp());
110+
} while (nFrameReturned == 0);
111+
112+
is_configured_decoder_ = true;
113+
114+
if (decoder_->GetOutputFormat() != cudaVideoSurfaceFormat_NV12) {
115+
RTC_LOG(LS_ERROR) << "not supported output format: "
116+
<< decoder_->GetOutputFormat();
117+
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
118+
}
119+
120+
const ColorSpace& color_space =
121+
input_image.ColorSpace() ? *input_image.ColorSpace()
122+
: ExtractColorSpaceFromFormat(
123+
decoder_->GetVideoFormatInfo());
124+
125+
for (int i = 0; i < nFrameReturned; i++) {
126+
int64_t timeStamp;
127+
uint8_t* pFrame = decoder_->GetFrame(&timeStamp);
128+
129+
webrtc::scoped_refptr<webrtc::I420Buffer> i420_buffer =
130+
buffer_pool_.CreateI420Buffer(decoder_->GetWidth(),
131+
decoder_->GetHeight());
132+
133+
int result = libyuv::NV12ToI420(
134+
pFrame, decoder_->GetDeviceFramePitch(),
135+
pFrame + decoder_->GetHeight() * decoder_->GetDeviceFramePitch(),
136+
decoder_->GetDeviceFramePitch(), i420_buffer->MutableDataY(),
137+
i420_buffer->StrideY(), i420_buffer->MutableDataU(),
138+
i420_buffer->StrideU(), i420_buffer->MutableDataV(),
139+
i420_buffer->StrideV(), decoder_->GetWidth(), decoder_->GetHeight());
140+
141+
if (result) {
142+
RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result;
143+
}
144+
145+
VideoFrame decoded_frame = VideoFrame::Builder()
146+
.set_video_frame_buffer(i420_buffer)
147+
.set_timestamp_rtp(static_cast<uint32_t>(
148+
timeStamp))
149+
.set_color_space(color_space)
150+
.build();
151+
152+
std::optional<int32_t> decodetime;
153+
std::optional<int> qp; // Not parsed for H265 currently
154+
decoded_complete_callback_->Decoded(decoded_frame, decodetime, qp);
155+
}
156+
157+
return WEBRTC_VIDEO_CODEC_OK;
158+
}
159+
160+
} // end namespace webrtc
161+
162+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef WEBRTC_NVIDIA_H265_DECODER_IMPL_H_
2+
#define WEBRTC_NVIDIA_H265_DECODER_IMPL_H_
3+
4+
#include <api/video_codecs/sdp_video_format.h>
5+
#include <api/video_codecs/video_decoder.h>
6+
#include <api/video_codecs/video_decoder_factory.h>
7+
#include <api/video/video_codec_type.h>
8+
#include <common_video/include/video_frame_buffer_pool.h>
9+
#include <cuda.h>
10+
#include <media/base/codec.h>
11+
12+
#include "NvDecoder/NvDecoder.h"
13+
14+
namespace webrtc {
15+
16+
class NvidiaH265DecoderImpl : public VideoDecoder {
17+
public:
18+
explicit NvidiaH265DecoderImpl(CUcontext context);
19+
NvidiaH265DecoderImpl(const NvidiaH265DecoderImpl&) = delete;
20+
NvidiaH265DecoderImpl& operator=(const NvidiaH265DecoderImpl&) = delete;
21+
~NvidiaH265DecoderImpl() override;
22+
23+
bool Configure(const Settings& settings) override;
24+
int32_t Decode(const EncodedImage& input_image,
25+
bool missing_frames,
26+
int64_t render_time_ms) override;
27+
int32_t RegisterDecodeCompleteCallback(
28+
DecodedImageCallback* callback) override;
29+
int32_t Release() override;
30+
DecoderInfo GetDecoderInfo() const override;
31+
32+
private:
33+
CUcontext cu_context_;
34+
std::unique_ptr<NvDecoder> decoder_;
35+
bool is_configured_decoder_;
36+
37+
Settings settings_;
38+
39+
DecodedImageCallback* decoded_complete_callback_ = nullptr;
40+
webrtc::VideoFrameBufferPool buffer_pool_;
41+
};
42+
43+
} // end namespace webrtc
44+
45+
#endif // WEBRTC_NVIDIA_H265_DECODER_IMPL_H_
46+
47+

0 commit comments

Comments
 (0)