NvDecoderImpl.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #include "pch.h"
  2. #include <api/video/i420_buffer.h>
  3. #include <api/video/video_codec_type.h>
  4. #include <modules/video_coding/include/video_error_codes.h>
  5. #include <third_party/libyuv/include/libyuv/convert.h>
  6. #include "NvCodecUtils.h"
  7. #include "NvDecoder/NvDecoder.h"
  8. #include "NvDecoderImpl.h"
  9. #include "ProfilerMarkerFactory.h"
  10. #include "ScopedProfiler.h"
  11. namespace unity
  12. {
  13. namespace webrtc
  14. {
  15. using namespace ::webrtc;
  16. ColorSpace ExtractH264ColorSpace(const CUVIDEOFORMAT& format)
  17. {
  18. return ColorSpace(
  19. static_cast<ColorSpace::PrimaryID>(format.video_signal_description.color_primaries),
  20. static_cast<ColorSpace::TransferID>(format.video_signal_description.transfer_characteristics),
  21. static_cast<ColorSpace::MatrixID>(format.video_signal_description.matrix_coefficients),
  22. static_cast<ColorSpace::RangeID>(format.video_signal_description.video_full_range_flag));
  23. }
  24. NvDecoderImpl::NvDecoderImpl(CUcontext context, ProfilerMarkerFactory* profiler)
  25. : m_context(context)
  26. , m_decoder(nullptr)
  27. , m_isConfiguredDecoder(false)
  28. , m_decodedCompleteCallback(nullptr)
  29. , m_buffer_pool(false)
  30. , m_profiler(profiler)
  31. {
  32. if (profiler)
  33. m_marker = profiler->CreateMarker(
  34. "NvDecoderImpl.ConvertNV12ToI420", kUnityProfilerCategoryOther, kUnityProfilerMarkerFlagDefault, 0);
  35. }
  36. NvDecoderImpl::~NvDecoderImpl() { Release(); }
  37. VideoDecoder::DecoderInfo NvDecoderImpl::GetDecoderInfo() const
  38. {
  39. VideoDecoder::DecoderInfo info;
  40. info.implementation_name = "NvCodec";
  41. info.is_hardware_accelerated = true;
  42. return info;
  43. }
  44. int NvDecoderImpl::InitDecode(const VideoCodec* codec_settings, int32_t number_of_cores)
  45. {
  46. if (codec_settings == nullptr)
  47. {
  48. RTC_LOG(LS_ERROR) << "initialization failed on codec_settings is null ";
  49. return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  50. }
  51. if (codec_settings->codecType != kVideoCodecH264)
  52. {
  53. RTC_LOG(LS_ERROR) << "initialization failed on codectype is not kVideoCodecH264";
  54. return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  55. }
  56. if (codec_settings->width < 1 || codec_settings->height < 1)
  57. {
  58. RTC_LOG(LS_ERROR) << "initialization failed on codec_settings width < 0 or height < 0";
  59. return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  60. }
  61. m_codec = *codec_settings;
  62. const CUresult result = cuCtxSetCurrent(m_context);
  63. if (!ck(result))
  64. {
  65. RTC_LOG(LS_ERROR) << "initialization failed on cuCtxSetCurrent result" << result;
  66. return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
  67. }
  68. // todo(kazuki): Max resolution is differred each architecture.
  69. // Refer to the table in Video Decoder Capabilities.
  70. // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide
  71. int maxWidth = 4096;
  72. int maxHeight = 4096;
  73. // bUseDeviceFrame: allocate in memory or cuda device memory
  74. m_decoder = std::make_unique<NvDecoderInternal>(
  75. m_context, false, cudaVideoCodec_H264, true, false, nullptr, nullptr, maxWidth, maxHeight);
  76. return WEBRTC_VIDEO_CODEC_OK;
  77. }
  78. int32_t NvDecoderImpl::RegisterDecodeCompleteCallback(DecodedImageCallback* callback)
  79. {
  80. this->m_decodedCompleteCallback = callback;
  81. return WEBRTC_VIDEO_CODEC_OK;
  82. }
  83. int32_t NvDecoderImpl::Release()
  84. {
  85. m_buffer_pool.Release();
  86. return WEBRTC_VIDEO_CODEC_OK;
  87. }
  88. int32_t NvDecoderImpl::Decode(const EncodedImage& input_image, bool missing_frames, int64_t render_time_ms)
  89. {
  90. CUcontext current;
  91. if (!ck(cuCtxGetCurrent(&current)))
  92. {
  93. RTC_LOG(LS_ERROR) << "decode failed on cuCtxGetCurrent is failed";
  94. return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
  95. }
  96. if (current != m_context)
  97. {
  98. RTC_LOG(LS_ERROR) << "decode failed on not match current context and hold context";
  99. return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
  100. }
  101. if (m_decodedCompleteCallback == nullptr)
  102. {
  103. RTC_LOG(LS_ERROR) << "decode failed on not set m_decodedCompleteCallback";
  104. return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
  105. }
  106. if (!input_image.data() || !input_image.size())
  107. {
  108. RTC_LOG(LS_ERROR) << "decode failed on input image is null";
  109. return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  110. }
  111. m_h264_bitstream_parser.ParseBitstream(input_image);
  112. absl::optional<int> qp = m_h264_bitstream_parser.GetLastSliceQp();
  113. absl::optional<SpsParser::SpsState> sps = m_h264_bitstream_parser.sps();
  114. if (m_isConfiguredDecoder)
  115. {
  116. if (!sps || sps.value().width != static_cast<uint32_t>(m_decoder->GetWidth()) ||
  117. sps.value().height != static_cast<uint32_t>(m_decoder->GetHeight()))
  118. {
  119. m_decoder->setReconfigParams(nullptr, nullptr);
  120. }
  121. }
  122. int nFrameReturnd = 0;
  123. do
  124. {
  125. nFrameReturnd = m_decoder->Decode(
  126. input_image.data(), static_cast<int>(input_image.size()), CUVID_PKT_TIMESTAMP, input_image.Timestamp());
  127. } while (nFrameReturnd == 0);
  128. m_isConfiguredDecoder = true;
  129. // todo: support other output format
  130. // Chromium's H264 Encoder is output on NV12, so currently only NV12 is supported.
  131. if (m_decoder->GetOutputFormat() != cudaVideoSurfaceFormat_NV12)
  132. {
  133. RTC_LOG(LS_ERROR) << "not supported this format: " << m_decoder->GetOutputFormat();
  134. return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  135. }
  136. // Pass on color space from input frame if explicitly specified.
  137. const ColorSpace& color_space = input_image.ColorSpace()
  138. ? *input_image.ColorSpace()
  139. : ExtractH264ColorSpace(m_decoder->GetVideoFormatInfo());
  140. for (int i = 0; i < nFrameReturnd; i++)
  141. {
  142. int64_t timeStamp;
  143. uint8_t* pFrame = m_decoder->GetFrame(&timeStamp);
  144. rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer =
  145. m_buffer_pool.CreateI420Buffer(m_decoder->GetWidth(), m_decoder->GetHeight());
  146. int result;
  147. {
  148. std::unique_ptr<const ScopedProfiler> profiler;
  149. if (m_profiler)
  150. profiler = m_profiler->CreateScopedProfiler(*m_marker);
  151. result = libyuv::NV12ToI420(
  152. pFrame,
  153. m_decoder->GetDeviceFramePitch(),
  154. pFrame + m_decoder->GetHeight() * m_decoder->GetDeviceFramePitch(),
  155. m_decoder->GetDeviceFramePitch(),
  156. i420_buffer->MutableDataY(),
  157. i420_buffer->StrideY(),
  158. i420_buffer->MutableDataU(),
  159. i420_buffer->StrideU(),
  160. i420_buffer->MutableDataV(),
  161. i420_buffer->StrideV(),
  162. m_decoder->GetWidth(),
  163. m_decoder->GetHeight());
  164. }
  165. if (result)
  166. {
  167. RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result;
  168. }
  169. VideoFrame decoded_frame = VideoFrame::Builder()
  170. .set_video_frame_buffer(i420_buffer)
  171. .set_timestamp_rtp(static_cast<uint32_t>(timeStamp))
  172. .set_color_space(color_space)
  173. .build();
  174. // todo: measurement decoding time
  175. absl::optional<int32_t> decodetime;
  176. m_decodedCompleteCallback->Decoded(decoded_frame, decodetime, qp);
  177. }
  178. return WEBRTC_VIDEO_CODEC_OK;
  179. }
  180. } // end namespace webrtc
  181. } // end namespace unity