Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Plugin~/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ int NvDecoder::setReconfigParams(const Rect *pCropRect, const Dim *pResizeDim)
}
else
{
delete pFrame;
delete[] pFrame;
}
}

Expand Down
54 changes: 48 additions & 6 deletions Plugin~/WebRTCPlugin/Codec/NvCodec/NvDecoderImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,19 @@ namespace webrtc
{
using namespace ::webrtc;

ColorSpace ExtractH264ColorSpace(const CUVIDEOFORMAT& format)
{
return ColorSpace(
static_cast<ColorSpace::PrimaryID>(format.video_signal_description.color_primaries),
static_cast<ColorSpace::TransferID>(format.video_signal_description.transfer_characteristics),
static_cast<ColorSpace::MatrixID>(format.video_signal_description.matrix_coefficients),
static_cast<ColorSpace::RangeID>(format.video_signal_description.video_full_range_flag));
}

NvDecoderImpl::NvDecoderImpl(CUcontext context)
: m_context(context)
, m_decoder(nullptr)
, m_isConfiguredDecoder(false)
, m_decodedCompleteCallback(nullptr)
, m_buffer_pool(false)
{
Expand Down Expand Up @@ -61,8 +71,15 @@ namespace webrtc
return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
}

// todo(kazuki): Max resolution is differred each architecture.
// Refer to the table in Video Decoder Capabilities.
// https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide
int maxWidth = 4096;
int maxHeight = 4096;

// bUseDeviceFrame: allocate in memory or cuda device memory
m_decoder = std::make_unique<NvDecoderInternal>(m_context, false, cudaVideoCodec_H264);
m_decoder = std::make_unique<NvDecoderInternal>(
m_context, false, cudaVideoCodec_H264, true, false, nullptr, nullptr, maxWidth, maxHeight);
return WEBRTC_VIDEO_CODEC_OK;
}

Expand Down Expand Up @@ -104,14 +121,26 @@ namespace webrtc

m_h264_bitstream_parser.ParseBitstream(input_image);
absl::optional<int> qp = m_h264_bitstream_parser.GetLastSliceQp();
absl::optional<SpsParser::SpsState> sps = m_h264_bitstream_parser.sps();

if (m_isConfiguredDecoder)
{
if (!sps || sps.value().width != static_cast<uint32_t>(m_decoder->GetWidth()) ||
sps.value().height != static_cast<uint32_t>(m_decoder->GetHeight()))
{
m_decoder->setReconfigParams(nullptr, nullptr);
}
}

int nFrameReturnd = 0;
do
{
nFrameReturnd =
m_decoder->Decode(input_image.data(), input_image.size(), CUVID_PKT_TIMESTAMP, input_image.Timestamp());
nFrameReturnd = m_decoder->Decode(
input_image.data(), static_cast<int>(input_image.size()), CUVID_PKT_TIMESTAMP, input_image.Timestamp());
} while (nFrameReturnd == 0);

m_isConfiguredDecoder = true;

// todo: support other output format
// Chromium's H264 Encoder is output on NV12, so currently only NV12 is supported.
if (m_decoder->GetOutputFormat() != cudaVideoSurfaceFormat_NV12)
Expand All @@ -120,6 +149,11 @@ namespace webrtc
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}

// Pass on color space from input frame if explicitly specified.
const ColorSpace& color_space = input_image.ColorSpace()
? *input_image.ColorSpace()
: ExtractH264ColorSpace(m_decoder->GetVideoFormatInfo());

for (int i = 0; i < nFrameReturnd; i++)
{
int64_t timeStamp;
Expand All @@ -128,7 +162,7 @@ namespace webrtc
rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer =
m_buffer_pool.CreateI420Buffer(m_decoder->GetWidth(), m_decoder->GetHeight());

libyuv::NV12ToI420(
int result = libyuv::NV12ToI420(
pFrame,
m_decoder->GetDeviceFramePitch(),
pFrame + m_decoder->GetHeight() * m_decoder->GetDeviceFramePitch(),
Expand All @@ -142,8 +176,16 @@ namespace webrtc
m_decoder->GetWidth(),
m_decoder->GetHeight());

VideoFrame decoded_frame =
VideoFrame::Builder().set_video_frame_buffer(i420_buffer).set_timestamp_rtp(timeStamp).build();
if (result)
{
RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result;
}

VideoFrame decoded_frame = VideoFrame::Builder()
.set_video_frame_buffer(i420_buffer)
.set_timestamp_rtp(static_cast<uint32_t>(timeStamp))
.set_color_space(color_space)
.build();

// todo: measurement decoding time
absl::optional<int32_t> decodetime;
Expand Down
10 changes: 9 additions & 1 deletion Plugin~/WebRTCPlugin/Codec/NvCodec/NvDecoderImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ namespace webrtc

using NvDecoderInternal = ::NvDecoder;

class H264BitstreamParser : public ::webrtc::H264BitstreamParser
{
public:
absl::optional<SpsParser::SpsState> sps() { return sps_; }
absl::optional<PpsParser::PpsState> pps() { return pps_; }
};

class NvDecoderImpl : public unity::webrtc::NvDecoder
{
public:
Expand All @@ -34,12 +41,13 @@ namespace webrtc
private:
CUcontext m_context;
std::unique_ptr<NvDecoderInternal> m_decoder;
bool m_isConfiguredDecoder;

VideoCodec m_codec;

DecodedImageCallback* m_decodedCompleteCallback = nullptr;
webrtc::VideoFrameBufferPool m_buffer_pool;
webrtc::H264BitstreamParser m_h264_bitstream_parser;
H264BitstreamParser m_h264_bitstream_parser;
};

} // end namespace webrtc
Expand Down
65 changes: 58 additions & 7 deletions Plugin~/WebRTCPluginTest/NvCodec/NvCodecTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,64 @@ namespace webrtc
ASSERT_TRUE(decoded_frame);

// todo: set color space data on decode frame
// const ColorSpace color_space = *decoded_frame->color_space();
// EXPECT_EQ(ColorSpace::PrimaryID::kUnspecified, color_space.primaries());
// EXPECT_EQ(ColorSpace::TransferID::kUnspecified, color_space.transfer());
// EXPECT_EQ(ColorSpace::MatrixID::kUnspecified, color_space.matrix());
// EXPECT_EQ(ColorSpace::RangeID::kInvalid, color_space.range());
// EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified, color_space.chroma_siting_horizontal());
// EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified, color_space.chroma_siting_vertical());
const ColorSpace color_space = *decoded_frame->color_space();
EXPECT_EQ(ColorSpace::PrimaryID::kUnspecified, color_space.primaries());
EXPECT_EQ(ColorSpace::TransferID::kUnspecified, color_space.transfer());
EXPECT_EQ(ColorSpace::MatrixID::kUnspecified, color_space.matrix());
EXPECT_EQ(ColorSpace::RangeID::kInvalid, color_space.range());
EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified, color_space.chroma_siting_horizontal());
EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified, color_space.chroma_siting_vertical());
}

TEST_P(NvCodecTest, ReconfigureDecoder)
{
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codecSettings_, kSettings()));
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->InitDecode(&codecSettings_, 1));

EncodedImage encoded_frame;
CodecSpecificInfo codec_specific_info;
VideoFrame frame = NextInputFrame();
EncodeAndWaitForFrame(frame, &encoded_frame, &codec_specific_info);

encoded_frame._frameType = VideoFrameType::kVideoFrameKey;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, false, 0));
std::unique_ptr<VideoFrame> decoded_frame;
absl::optional<uint8_t> decoded_qp;
ASSERT_TRUE(WaitForDecodedFrame(&decoded_frame, &decoded_qp));
ASSERT_TRUE(decoded_frame);
EXPECT_EQ(decoded_frame->width(), frame.width());
EXPECT_EQ(decoded_frame->height(), frame.height());

// change resolution
uint16_t width = codecSettings_.width / 2;
uint16_t height = codecSettings_.height / 2;
codecSettings_.width = width;
codecSettings_.height = height;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codecSettings_, kSettings()));

ChangeFrameResolution(static_cast<size_t>(width), static_cast<size_t>(height));

VideoFrame frame2 = NextInputFrame();
EncodeAndWaitForFrame(frame2, &encoded_frame, &codec_specific_info);
encoded_frame._frameType = VideoFrameType::kVideoFrameKey;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, false, 33));
ASSERT_TRUE(WaitForDecodedFrame(&decoded_frame, &decoded_qp));
ASSERT_TRUE(decoded_frame);

// todo(kazuki): `pfnSequenceCallback` in NvEncoder.cpp is not called from NvDec when
// the first frame after changing resolution, so the resolution of the first frame is old one.
EXPECT_EQ(decoded_frame->width(), frame.width());
EXPECT_EQ(decoded_frame->height(), frame.height());

// The second frame after changing resolution is fine.
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, false, 66));
ASSERT_TRUE(WaitForDecodedFrame(&decoded_frame, &decoded_qp));

EXPECT_EQ(decoded_frame->width(), frame2.width());
EXPECT_EQ(decoded_frame->height(), frame2.height());
}

TEST_P(NvCodecTest, DecodedQpEqualsEncodedQp)
Expand Down
5 changes: 5 additions & 0 deletions Plugin~/WebRTCPluginTest/VideoCodecTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ namespace webrtc
return input_frame;
}

void VideoCodecTest::ChangeFrameResolution(size_t width, size_t height)
{
inputFrameGenerator_->ChangeResolution(width, height);
}

bool VideoCodecTest::WaitForEncodedFrame(EncodedImage* frame, CodecSpecificInfo* codec_specific_info)
{
std::vector<EncodedImage> frames;
Expand Down
1 change: 1 addition & 0 deletions Plugin~/WebRTCPluginTest/VideoCodecTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ namespace webrtc
void TearDown() override;

VideoFrame NextInputFrame();
void ChangeFrameResolution(size_t width, size_t height);

// Helper method for waiting a single encoded frame.
bool WaitForEncodedFrame(EncodedImage* frame, CodecSpecificInfo* codec_specific_info);
Expand Down
Loading