Skip to content

Commit 942fe2a

Browse files
committed
add nv h264 decoder impl.
1 parent beb76fe commit 942fe2a

5 files changed

Lines changed: 264 additions & 20 deletions

File tree

webrtc-sys/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ fn main() {
143143
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
144144
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
145145
.file("src/nvidia/h264_encoder_impl.cpp")
146+
.file("src/nvidia/h264_decoder_impl.cpp")
146147
.file("src/nvidia/NvEncoderCudaWithCUarray.cpp")
147148
.flag("-std=c++2a")
148149
.flag("-Wno-deprecated-declarations");
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#include "h264_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+
ColorSpace ExtractH264ColorSpace(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+
NvidiaH264DecoderImpl::NvidiaH264DecoderImpl(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+
NvidiaH264DecoderImpl::~NvidiaH264DecoderImpl() {
35+
Release();
36+
}
37+
38+
VideoDecoder::DecoderInfo NvidiaH264DecoderImpl::GetDecoderInfo() const {
39+
VideoDecoder::DecoderInfo info;
40+
info.implementation_name = "NVIDIA H264 Decoder";
41+
info.is_hardware_accelerated = true;
42+
return info;
43+
}
44+
45+
bool NvidiaH264DecoderImpl::Configure(const Settings& settings) {
46+
if (settings.codec_type() != kVideoCodecH264) {
47+
RTC_LOG(LS_ERROR)
48+
<< "initialization failed on codectype is not kVideoCodecH264";
49+
return false;
50+
}
51+
if (!settings.max_render_resolution().Valid()) {
52+
RTC_LOG(LS_ERROR)
53+
<< "initialization failed on codec_settings width < 0 or height < 0";
54+
return false;
55+
}
56+
57+
settings_ = settings;
58+
59+
const CUresult result = cuCtxSetCurrent(cu_context_);
60+
if (!ck(result)) {
61+
RTC_LOG(LS_ERROR) << "initialization failed on cuCtxSetCurrent result"
62+
<< result;
63+
return false;
64+
}
65+
66+
// todo(kazuki): Max resolution is differred each architecture.
67+
// Refer to the table in Video Decoder Capabilities.
68+
// https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide
69+
int maxWidth = 4096;
70+
int maxHeight = 4096;
71+
72+
// bUseDeviceFrame: allocate in memory or cuda device memory
73+
decoder_ = std::make_unique<NvDecoder>(
74+
cu_context_, false, cudaVideoCodec_H264, true, false, nullptr, nullptr,
75+
false, maxWidth, maxHeight);
76+
return true;
77+
}
78+
79+
int32_t NvidiaH264DecoderImpl::RegisterDecodeCompleteCallback(
80+
DecodedImageCallback* callback) {
81+
this->decoded_complete_callback_ = callback;
82+
return WEBRTC_VIDEO_CODEC_OK;
83+
}
84+
85+
int32_t NvidiaH264DecoderImpl::Release() {
86+
buffer_pool_.Release();
87+
return WEBRTC_VIDEO_CODEC_OK;
88+
}
89+
90+
int32_t NvidiaH264DecoderImpl::Decode(const EncodedImage& input_image,
91+
bool missing_frames,
92+
int64_t render_time_ms) {
93+
CUcontext current;
94+
if (!ck(cuCtxGetCurrent(&current))) {
95+
RTC_LOG(LS_ERROR) << "decode failed on cuCtxGetCurrent is failed";
96+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
97+
}
98+
if (current != cu_context_) {
99+
RTC_LOG(LS_ERROR)
100+
<< "decode failed on not match current context and hold context";
101+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
102+
}
103+
if (decoded_complete_callback_ == nullptr) {
104+
RTC_LOG(LS_ERROR) << "decode failed on not set m_decodedCompleteCallback";
105+
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
106+
}
107+
if (!input_image.data() || !input_image.size()) {
108+
RTC_LOG(LS_ERROR) << "decode failed on input image is null";
109+
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
110+
}
111+
112+
h264_bitstream_parser_.ParseBitstream(input_image);
113+
absl::optional<int> qp = h264_bitstream_parser_.GetLastSliceQp();
114+
absl::optional<SpsParser::SpsState> sps = h264_bitstream_parser_.sps();
115+
116+
if (is_configured_decoder_) {
117+
if (!sps ||
118+
sps.value().width != static_cast<uint32_t>(decoder_->GetWidth()) ||
119+
sps.value().height != static_cast<uint32_t>(decoder_->GetHeight())) {
120+
decoder_->setReconfigParams(nullptr, nullptr);
121+
}
122+
}
123+
124+
int nFrameReturnd = 0;
125+
do {
126+
nFrameReturnd = decoder_->Decode(
127+
input_image.data(), static_cast<int>(input_image.size()),
128+
CUVID_PKT_TIMESTAMP, input_image.RtpTimestamp());
129+
} while (nFrameReturnd == 0);
130+
131+
is_configured_decoder_ = true;
132+
133+
// todo: support other output format
134+
// Chromium's H264 Encoder is output on NV12, so currently only NV12 is
135+
// supported.
136+
if (decoder_->GetOutputFormat() != cudaVideoSurfaceFormat_NV12) {
137+
RTC_LOG(LS_ERROR) << "not supported this format: "
138+
<< decoder_->GetOutputFormat();
139+
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
140+
}
141+
142+
// Pass on color space from input frame if explicitly specified.
143+
const ColorSpace& color_space =
144+
input_image.ColorSpace()
145+
? *input_image.ColorSpace()
146+
: ExtractH264ColorSpace(decoder_->GetVideoFormatInfo());
147+
148+
for (int i = 0; i < nFrameReturnd; i++) {
149+
int64_t timeStamp;
150+
uint8_t* pFrame = decoder_->GetFrame(&timeStamp);
151+
152+
rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer =
153+
buffer_pool_.CreateI420Buffer(decoder_->GetWidth(),
154+
decoder_->GetHeight());
155+
156+
int result;
157+
{
158+
result = libyuv::NV12ToI420(
159+
pFrame, decoder_->GetDeviceFramePitch(),
160+
pFrame + decoder_->GetHeight() * decoder_->GetDeviceFramePitch(),
161+
decoder_->GetDeviceFramePitch(), i420_buffer->MutableDataY(),
162+
i420_buffer->StrideY(), i420_buffer->MutableDataU(),
163+
i420_buffer->StrideU(), i420_buffer->MutableDataV(),
164+
i420_buffer->StrideV(), decoder_->GetWidth(), decoder_->GetHeight());
165+
}
166+
167+
if (result) {
168+
RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result;
169+
}
170+
171+
VideoFrame decoded_frame =
172+
VideoFrame::Builder()
173+
.set_video_frame_buffer(i420_buffer)
174+
.set_timestamp_rtp(static_cast<uint32_t>(timeStamp))
175+
.set_color_space(color_space)
176+
.build();
177+
178+
// todo: measurement decoding time
179+
absl::optional<int32_t> decodetime;
180+
decoded_complete_callback_->Decoded(decoded_frame, decodetime, qp);
181+
}
182+
183+
return WEBRTC_VIDEO_CODEC_OK;
184+
}
185+
186+
} // end namespace webrtc
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
4+
#include <api/video_codecs/h264_profile_level_id.h>
5+
#include <api/video_codecs/sdp_video_format.h>
6+
#include <api/video_codecs/video_decoder.h>
7+
#include <api/video_codecs/video_decoder_factory.h>
8+
#include <api/video_codecs/video_encoder.h>
9+
#include <api/video_codecs/video_encoder_factory.h>
10+
#include <common_video/h264/pps_parser.h>
11+
#include <common_video/h264/sps_parser.h>
12+
#include <common_video/h264/h264_bitstream_parser.h>
13+
#include <common_video/include/video_frame_buffer_pool.h>
14+
15+
#include <cuda.h>
16+
#include <media/base/codec.h>
17+
18+
#include "NvDecoder/NvDecoder.h"
19+
20+
namespace webrtc {
21+
22+
class H264BitstreamParserEx : public ::webrtc::H264BitstreamParser {
23+
public:
24+
absl::optional<SpsParser::SpsState> sps() { return sps_; }
25+
absl::optional<PpsParser::PpsState> pps() { return pps_; }
26+
};
27+
28+
class NvidiaH264DecoderImpl : public VideoDecoder {
29+
public:
30+
NvidiaH264DecoderImpl(CUcontext context);
31+
NvidiaH264DecoderImpl(const NvidiaH264DecoderImpl&) = delete;
32+
NvidiaH264DecoderImpl& operator=(const NvidiaH264DecoderImpl&) = delete;
33+
~NvidiaH264DecoderImpl() override;
34+
35+
bool Configure(const Settings& settings) override;
36+
int32_t Decode(const EncodedImage& input_image,
37+
bool missing_frames,
38+
int64_t render_time_ms) override;
39+
int32_t RegisterDecodeCompleteCallback(
40+
DecodedImageCallback* callback) override;
41+
int32_t Release() override;
42+
DecoderInfo GetDecoderInfo() const override;
43+
44+
private:
45+
CUcontext cu_context_;
46+
std::unique_ptr<NvDecoder> decoder_;
47+
bool is_configured_decoder_;
48+
49+
Settings settings_;
50+
51+
DecodedImageCallback* decoded_complete_callback_ = nullptr;
52+
webrtc::VideoFrameBufferPool buffer_pool_;
53+
H264BitstreamParserEx h264_bitstream_parser_;
54+
};
55+
56+
} // end namespace webrtc

webrtc-sys/src/nvidia/h264_encoder_impl.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#include "h264_encoder_impl.h"
22

3-
#include <common_video/h264/h264_common.h>
43

54
#include <algorithm>
65
#include <limits>
76
#include <string>
87

9-
#include "NvEncoderCudaWithCUarray.h"
108
#include "absl/strings/match.h"
119
#include "absl/types/optional.h"
1210
#include "api/video/video_codec_constants.h"
1311
#include "api/video_codecs/scalability_mode.h"
12+
#include <common_video/h264/h264_common.h>
1413
#include "common_video/libyuv/include/webrtc_libyuv.h"
1514
#include "modules/video_coding/include/video_codec_interface.h"
1615
#include "modules/video_coding/include/video_error_codes.h"
@@ -24,7 +23,8 @@
2423
#include "third_party/libyuv/include/libyuv/convert.h"
2524
#include "third_party/libyuv/include/libyuv/scale.h"
2625

27-
#define VA_FOURCC_I420 0x30323449 // I420
26+
27+
#include "NvEncoderCudaWithCUarray.h"
2828

2929
namespace webrtc {
3030

@@ -35,7 +35,7 @@ enum H264EncoderImplEvent {
3535
kH264EncoderEventMax = 16,
3636
};
3737

38-
NvidiaH264EncoderWrapper::NvidiaH264EncoderWrapper(
38+
NvidiaH264EncoderImpl::NvidiaH264EncoderImpl(
3939
const webrtc::Environment& env,
4040
CUcontext context,
4141
CUmemorytype memory_type,
@@ -60,27 +60,27 @@ NvidiaH264EncoderWrapper::NvidiaH264EncoderWrapper(
6060
RTC_CHECK_NE(cu_memory_type_, CU_MEMORYTYPE_HOST);
6161
}
6262

63-
NvidiaH264EncoderWrapper::~NvidiaH264EncoderWrapper() {
63+
NvidiaH264EncoderImpl::~NvidiaH264EncoderImpl() {
6464
Release();
6565
}
6666

67-
void NvidiaH264EncoderWrapper::ReportInit() {
67+
void NvidiaH264EncoderImpl::ReportInit() {
6868
if (has_reported_init_)
6969
return;
7070
RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event",
7171
kH264EncoderEventInit, kH264EncoderEventMax);
7272
has_reported_init_ = true;
7373
}
7474

75-
void NvidiaH264EncoderWrapper::ReportError() {
75+
void NvidiaH264EncoderImpl::ReportError() {
7676
if (has_reported_error_)
7777
return;
7878
RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event",
7979
kH264EncoderEventError, kH264EncoderEventMax);
8080
has_reported_error_ = true;
8181
}
8282

83-
int32_t NvidiaH264EncoderWrapper::InitEncode(
83+
int32_t NvidiaH264EncoderImpl::InitEncode(
8484
const VideoCodec* inst,
8585
const VideoEncoder::Settings& settings) {
8686
if (!inst || inst->codecType != kVideoCodecH264) {
@@ -202,14 +202,14 @@ int32_t NvidiaH264EncoderWrapper::InitEncode(
202202
return WEBRTC_VIDEO_CODEC_OK;
203203
}
204204

205-
int32_t NvidiaH264EncoderWrapper::RegisterEncodeCompleteCallback(
205+
int32_t NvidiaH264EncoderImpl::RegisterEncodeCompleteCallback(
206206
EncodedImageCallback* callback) {
207207
RTC_DCHECK(callback);
208208
encoded_image_callback_ = callback;
209209
return WEBRTC_VIDEO_CODEC_OK;
210210
}
211211

212-
int32_t NvidiaH264EncoderWrapper::Release() {
212+
int32_t NvidiaH264EncoderImpl::Release() {
213213
if (encoder_) {
214214
encoder_->DestroyEncoder();
215215
encoder_ = nullptr;
@@ -221,7 +221,7 @@ int32_t NvidiaH264EncoderWrapper::Release() {
221221
return WEBRTC_VIDEO_CODEC_OK;
222222
}
223223

224-
int32_t NvidiaH264EncoderWrapper::Encode(
224+
int32_t NvidiaH264EncoderImpl::Encode(
225225
const VideoFrame& input_frame,
226226
const std::vector<VideoFrameType>* frame_types) {
227227
if (!encoder_) {
@@ -339,7 +339,7 @@ int32_t NvidiaH264EncoderWrapper::Encode(
339339
return WEBRTC_VIDEO_CODEC_OK;
340340
}
341341

342-
int32_t NvidiaH264EncoderWrapper::ProcessEncodedFrame(
342+
int32_t NvidiaH264EncoderImpl::ProcessEncodedFrame(
343343
std::vector<uint8_t>& packet,
344344
const ::webrtc::VideoFrame& inputFrame) {
345345
encoded_image_._encodedWidth = encoder_->GetEncodeWidth();
@@ -386,7 +386,7 @@ int32_t NvidiaH264EncoderWrapper::ProcessEncodedFrame(
386386
return WEBRTC_VIDEO_CODEC_OK;
387387
}
388388

389-
VideoEncoder::EncoderInfo NvidiaH264EncoderWrapper::GetEncoderInfo() const {
389+
VideoEncoder::EncoderInfo NvidiaH264EncoderImpl::GetEncoderInfo() const {
390390
EncoderInfo info;
391391
info.supports_native_handle = false;
392392
info.implementation_name = "NVIDIA H264 Encoder";
@@ -397,7 +397,7 @@ VideoEncoder::EncoderInfo NvidiaH264EncoderWrapper::GetEncoderInfo() const {
397397
return info;
398398
}
399399

400-
void NvidiaH264EncoderWrapper::SetRates(
400+
void NvidiaH264EncoderImpl::SetRates(
401401
const RateControlParameters& parameters) {
402402
if (!encoder_) {
403403
RTC_LOG(LS_WARNING) << "SetRates() while uninitialized.";
@@ -427,7 +427,7 @@ void NvidiaH264EncoderWrapper::SetRates(
427427
}
428428
}
429429

430-
void NvidiaH264EncoderWrapper::LayerConfig::SetStreamState(bool send_stream) {
430+
void NvidiaH264EncoderImpl::LayerConfig::SetStreamState(bool send_stream) {
431431
if (send_stream && !sending) {
432432
// Need a key frame if we have not sent this stream before.
433433
key_frame_request = true;

0 commit comments

Comments
 (0)