fix flush in vdec + some minor fixes (#4669)

This commit is contained in:
Vladislav Mikhalin 2026-07-05 08:46:19 +03:00 committed by GitHub
parent 3e1f92353c
commit f668febfeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -41,9 +41,7 @@ VdecDecoder::~VdecDecoder() {
s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn,
OrbisVideodecFrameBuffer& pFrameBufferInOut,
OrbisVideodecPictureInfo& pPictureInfoOut) {
pPictureInfoOut.thisSize = sizeof(OrbisVideodecPictureInfo);
pPictureInfoOut.isValid = false;
pPictureInfoOut.isErrorPic = true;
if (!pInputDataIn.pAuData) {
return ORBIS_VIDEODEC_ERROR_AU_POINTER;
@ -76,40 +74,37 @@ s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn,
av_packet_free(&packet);
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
int frameCount = 0;
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
pPictureInfoOut.codecType = 0;
pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16);
pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
frameCount++;
if (frameCount > 1) {
LOG_WARNING(Lib_Videodec, "We have more than 1 frame");
}
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
} else if (ret < 0) {
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
pPictureInfoOut.codecType = 0;
pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16);
pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
pPictureInfoOut.attachedData = pInputDataIn.attachedData;
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
@ -117,9 +112,7 @@ s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn,
s32 VdecDecoder::Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut,
OrbisVideodecPictureInfo& pPictureInfoOut) {
pPictureInfoOut.thisSize = sizeof(pPictureInfoOut);
pPictureInfoOut.isValid = false;
pPictureInfoOut.isErrorPic = true;
AVFrame* frame = av_frame_alloc();
if (!frame) {
@ -127,49 +120,49 @@ s32 VdecDecoder::Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut,
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
int frameCount = 0;
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
pPictureInfoOut.codecType = 0;
pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16);
pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
u32 width = Common::AlignUp((u32)frame->width, 16);
u32 height = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.codec.avc.frameCropLeftOffset = u32(frame->crop_left);
pPictureInfoOut.codec.avc.frameCropRightOffset =
u32(frame->crop_right + (width - frame->width));
pPictureInfoOut.codec.avc.frameCropTopOffset = u32(frame->crop_top);
pPictureInfoOut.codec.avc.frameCropBottomOffset =
u32(frame->crop_bottom + (height - frame->height));
// TODO maybe more avc?
if (frameCount > 1) {
LOG_WARNING(Lib_Videodec, "We have more than 1 frame");
}
// avcodec_send_packet with packet set to nullptr results in codec flush.
// This flush can produce multiple frames but we can only return one.
// We cannot skip frames, some games hang if we do so we return only 1st.
// Games can send multiple consecutive flushes, but ffmpeg doesn't work
// this way and returns error. There is no way to make it flush only one
// frame so all we can do is just ignore the result and pray.
avcodec_send_packet(mCodecContext, nullptr);
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_frame_free(&frame);
return ORBIS_OK;
} else if (ret < 0) {
LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame);
pPictureInfoOut.codecType = 0;
pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16);
pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.framePitch = frame->linesize[0];
pPictureInfoOut.isValid = true;
pPictureInfoOut.isErrorPic = false;
u32 width = Common::AlignUp((u32)frame->width, 16);
u32 height = Common::AlignUp((u32)frame->height, 16);
pPictureInfoOut.codec.avc.frameCropLeftOffset = u32(frame->crop_left);
pPictureInfoOut.codec.avc.frameCropRightOffset =
u32(frame->crop_right + (width - frame->width));
pPictureInfoOut.codec.avc.frameCropTopOffset = u32(frame->crop_top);
pPictureInfoOut.codec.avc.frameCropBottomOffset =
u32(frame->crop_bottom + (height - frame->height));
av_frame_free(&frame);
return ORBIS_OK;
}