add h264 422 decoding

Bug: webrtc:13826
Change-Id: Ic7296be69157a9aaf5f139a18fdb011b90f4caa1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/255380
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Magnus Flodman <mflodman@webrtc.org>
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36337}
This commit is contained in:
Sergio Garcia Murillo
2022-03-25 09:04:09 +01:00
committed by WebRTC LUCI CQ
parent d8654cf636
commit b63536f5d3
14 changed files with 884 additions and 67 deletions

View File

@ -41,10 +41,9 @@ namespace webrtc {
namespace {
constexpr std::array<AVPixelFormat, 2> kPixelFormatsDefault = {
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P};
constexpr std::array<AVPixelFormat, 2> kPixelFormatsFullRange = {
AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P};
constexpr std::array<AVPixelFormat, 6> kPixelFormatsSupported = {
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P};
const size_t kYPlaneIndex = 0;
const size_t kUPlaneIndex = 1;
const size_t kVPlaneIndex = 2;
@ -78,17 +77,11 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context,
// Necessary capability to be allowed to provide our own buffers.
RTC_DCHECK(context->codec->capabilities | AV_CODEC_CAP_DR1);
// Limited or full range YUV420 or YUV444 is expected.
auto pixelFormatDefault = std::find_if(
kPixelFormatsDefault.begin(), kPixelFormatsDefault.end(),
[context](AVPixelFormat format) { return context->pix_fmt == format; });
auto pixelFormatFullRange = std::find_if(
kPixelFormatsFullRange.begin(), kPixelFormatsFullRange.end(),
auto pixelFormatSupported = std::find_if(
kPixelFormatsSupported.begin(), kPixelFormatsSupported.end(),
[context](AVPixelFormat format) { return context->pix_fmt == format; });
// Limited or full range YUV420 is expected.
RTC_CHECK(pixelFormatDefault != kPixelFormatsDefault.end() ||
pixelFormatFullRange != kPixelFormatsFullRange.end());
RTC_CHECK(pixelFormatSupported != kPixelFormatsSupported.end());
// `av_frame->width` and `av_frame->height` are set by FFmpeg. These are the
// actual image's dimensions and may be different from `context->width` and
@ -125,6 +118,7 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context,
rtc::scoped_refptr<PlanarYuv8Buffer> frame_buffer;
rtc::scoped_refptr<I444Buffer> i444_buffer;
rtc::scoped_refptr<I420Buffer> i420_buffer;
rtc::scoped_refptr<I422Buffer> i422_buffer;
switch (context->pix_fmt) {
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
@ -153,6 +147,19 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context,
av_frame->linesize[kVPlaneIndex] = i444_buffer->StrideV();
frame_buffer = i444_buffer;
break;
case AV_PIX_FMT_YUV422P:
case AV_PIX_FMT_YUVJ422P:
i422_buffer =
decoder->ffmpeg_buffer_pool_.CreateI422Buffer(width, height);
// Set `av_frame` members as required by FFmpeg.
av_frame->data[kYPlaneIndex] = i422_buffer->MutableDataY();
av_frame->linesize[kYPlaneIndex] = i422_buffer->StrideY();
av_frame->data[kUPlaneIndex] = i422_buffer->MutableDataU();
av_frame->linesize[kUPlaneIndex] = i422_buffer->StrideU();
av_frame->data[kVPlaneIndex] = i422_buffer->MutableDataV();
av_frame->linesize[kVPlaneIndex] = i422_buffer->StrideV();
frame_buffer = i422_buffer;
break;
default:
RTC_LOG(LS_ERROR) << "Unsupported buffer type " << context->pix_fmt
<< ". Check supported supported pixel formats!";
@ -363,9 +370,12 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image,
case VideoFrameBuffer::Type::kI444:
planar_yuv8_buffer = frame_buffer->GetI444();
break;
case VideoFrameBuffer::Type::kI422:
planar_yuv8_buffer = frame_buffer->GetI422();
break;
default:
// If this code is changed to allow other video frame buffer type,
// make sure that the code below which wraps I420/I444 buffer and
// make sure that the code below which wraps I420/I422/I444 buffer and
// code which converts to NV12 is changed
// to work with new video frame buffer type
@ -400,22 +410,40 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image,
2);
rtc::scoped_refptr<webrtc::VideoFrameBuffer> cropped_buffer;
if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) {
cropped_buffer = WrapI420Buffer(
av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
av_frame_->linesize[kVPlaneIndex],
// To keep reference alive.
[frame_buffer] {});
} else {
cropped_buffer = WrapI444Buffer(
av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
av_frame_->linesize[kVPlaneIndex],
// To keep reference alive.
[frame_buffer] {});
switch (video_frame_buffer_type) {
case VideoFrameBuffer::Type::kI420:
cropped_buffer = WrapI420Buffer(
av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
av_frame_->linesize[kVPlaneIndex],
// To keep reference alive.
[frame_buffer] {});
break;
case VideoFrameBuffer::Type::kI444:
cropped_buffer = WrapI444Buffer(
av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
av_frame_->linesize[kVPlaneIndex],
// To keep reference alive.
[frame_buffer] {});
break;
case VideoFrameBuffer::Type::kI422:
cropped_buffer = WrapI422Buffer(
av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
av_frame_->linesize[kVPlaneIndex],
// To keep reference alive.
[frame_buffer] {});
break;
default:
RTC_LOG(LS_ERROR) << "frame_buffer type: "
<< static_cast<int32_t>(video_frame_buffer_type)
<< " is not supported!";
ReportError();
return WEBRTC_VIDEO_CODEC_ERROR;
}
if (preferred_output_format_ == VideoFrameBuffer::Type::kNV12) {
@ -423,30 +451,53 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image,
cropped_buffer->width(), cropped_buffer->height());
const PlanarYuv8Buffer* cropped_planar_yuv8_buffer = nullptr;
if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) {
cropped_planar_yuv8_buffer = cropped_buffer->GetI420();
libyuv::I420ToNV12(cropped_planar_yuv8_buffer->DataY(),
cropped_planar_yuv8_buffer->StrideY(),
cropped_planar_yuv8_buffer->DataU(),
cropped_planar_yuv8_buffer->StrideU(),
cropped_planar_yuv8_buffer->DataV(),
cropped_planar_yuv8_buffer->StrideV(),
nv12_buffer->MutableDataY(), nv12_buffer->StrideY(),
nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(),
planar_yuv8_buffer->width(),
planar_yuv8_buffer->height());
} else {
cropped_planar_yuv8_buffer = cropped_buffer->GetI444();
libyuv::I444ToNV12(cropped_planar_yuv8_buffer->DataY(),
cropped_planar_yuv8_buffer->StrideY(),
cropped_planar_yuv8_buffer->DataU(),
cropped_planar_yuv8_buffer->StrideU(),
cropped_planar_yuv8_buffer->DataV(),
cropped_planar_yuv8_buffer->StrideV(),
nv12_buffer->MutableDataY(), nv12_buffer->StrideY(),
nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(),
planar_yuv8_buffer->width(),
planar_yuv8_buffer->height());
switch (video_frame_buffer_type) {
case VideoFrameBuffer::Type::kI420:
cropped_planar_yuv8_buffer = cropped_buffer->GetI420();
libyuv::I420ToNV12(cropped_planar_yuv8_buffer->DataY(),
cropped_planar_yuv8_buffer->StrideY(),
cropped_planar_yuv8_buffer->DataU(),
cropped_planar_yuv8_buffer->StrideU(),
cropped_planar_yuv8_buffer->DataV(),
cropped_planar_yuv8_buffer->StrideV(),
nv12_buffer->MutableDataY(), nv12_buffer->StrideY(),
nv12_buffer->MutableDataUV(),
nv12_buffer->StrideUV(), planar_yuv8_buffer->width(),
planar_yuv8_buffer->height());
break;
case VideoFrameBuffer::Type::kI444:
cropped_planar_yuv8_buffer = cropped_buffer->GetI444();
libyuv::I444ToNV12(cropped_planar_yuv8_buffer->DataY(),
cropped_planar_yuv8_buffer->StrideY(),
cropped_planar_yuv8_buffer->DataU(),
cropped_planar_yuv8_buffer->StrideU(),
cropped_planar_yuv8_buffer->DataV(),
cropped_planar_yuv8_buffer->StrideV(),
nv12_buffer->MutableDataY(), nv12_buffer->StrideY(),
nv12_buffer->MutableDataUV(),
nv12_buffer->StrideUV(), planar_yuv8_buffer->width(),
planar_yuv8_buffer->height());
break;
case VideoFrameBuffer::Type::kI422:
cropped_planar_yuv8_buffer = cropped_buffer->GetI422();
// Swap src_u and src_v to implement I422ToNV12.
libyuv::I422ToNV21(cropped_planar_yuv8_buffer->DataY(),
cropped_planar_yuv8_buffer->StrideY(),
cropped_planar_yuv8_buffer->DataV(),
cropped_planar_yuv8_buffer->StrideV(),
cropped_planar_yuv8_buffer->DataU(),
cropped_planar_yuv8_buffer->StrideU(),
nv12_buffer->MutableDataY(), nv12_buffer->StrideY(),
nv12_buffer->MutableDataUV(),
nv12_buffer->StrideUV(), planar_yuv8_buffer->width(),
planar_yuv8_buffer->height());
break;
default:
RTC_LOG(LS_ERROR) << "frame_buffer type: "
<< static_cast<int32_t>(video_frame_buffer_type)
<< " is not supported!";
ReportError();
return WEBRTC_VIDEO_CODEC_ERROR;
}
cropped_buffer = nv12_buffer;