Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用ffmpeg-rockchip对H264数据进行硬件解码失败 #688

Open
CETC-AIoT opened this issue Sep 23, 2024 · 6 comments
Open

使用ffmpeg-rockchip对H264数据进行硬件解码失败 #688

CETC-AIoT opened this issue Sep 23, 2024 · 6 comments

Comments

@CETC-AIoT
Copy link

CETC-AIoT commented Sep 23, 2024

硬件平台:RK3568
内核:4.19.192/5.10.198
mpp版本:2023-12-14版本及最新版本

使用avcodec_send_packet方法(h264_rkmpp)进行解码,其中avpacket是通过av_parser_parse2方法解析内存得到的,
但是这个方法返回报错,先是返回-11,然后返回 -542398533(Generic error in an external library.)
把h264码流保存为文件,然后使用mpi_dec_test进行解码,则有些帧可以解,大部分解不了,显示err 10 discard 0.
而使用AV_CODEC_ID_H264对avpacket进行软解则没有问题,请问一下是什么问题?是mpp的兼容性问题么?
另外使用软件的时候,有时解码之后的格式是NV12,有时候是YUV420P,请问这个是怎么设置的?

h264视频文件:
video.zip

@HermanChen
Copy link
Collaborator

默认 develop 分支对 ffmpeg 没有支持,要用 ffmpeg 的话试下这个
https://github.com/nyanmisaka/ffmpeg-rockchip
是整合得比较好的仓库

@CETC-AIoT
Copy link
Author

您好,我们使用的就是nyanmisaka的版本,我也在他那里提了issue,它给的反馈是ffmpeg调用的是mpp的接口
image

@CETC-AIoT
Copy link
Author

谢谢回复。
我发现一个奇怪的事情,我跳过开始的60帧数据,然后开始解码,这时候出错的概率很高,大概140多次EAGAIN错误之后就会报"Generic error in an extenal library"的错误,但是我从第一帧开始解码,成功的概率就会高很多,一旦成功后面的数据就能正确解码。
另外我把两次的数据保存到文件,然后使用mpi_dec_test进行解码,结果也是随机的,并且并不是每一帧都能正确解码。
视频流中GOP=30,每一帧结尾都有 AUD信息-固定 6 字节:0x00 0x00 0x00 0x01 0x09 0x10
我使用av_parser_parse2对每一帧数据进行处理,它会把数据帧分为两部分,第一部分长度不定,第二部分长度是237字节,我是对第一部分进行解码。
文件大于25MB,不能上传,如果需要可以发到您的邮箱。
2a7aabe611c84f29b969650d16da8580

@HermanChen
Copy link
Collaborator

解码器一般都是要从 I 帧开始解码,sps/pps 这些头信息也是跟着 I 帧走的,从半当中开始的话,会缺少这些头信息,导致无法解码

@CETC-AIoT
Copy link
Author

从第一帧开始去解码,成功概率提升,大概平均3次里面会有两次会成功,还有一次会失败。跳过前面60帧,可能10多次里面会有一次成功。
在ffmpeg中调用mpp的api大概140多次EAGAIN错误之后就会报"Generic error in an extenal library"的错误,之后就会一直报错,请问一下这个可以把帧数提高一点再报错么?保存为文件之后,有时200+帧的时候就可以正常解码了

@CETC-AIoT
Copy link
Author

CETC-AIoT commented Sep 25, 2024

我存为文件进行解码,结果随机,有时候可以正确解码,有时候会出错,但出错的时候,一定是第147个包
image
代码如下:
#include
#include
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
}
int main() {
long cnt = 0, size=0;
// 初始化FFmpeg库
//av_register_all();
// 打开输入文件
AVFormatContext* formatContext = nullptr;
// std::string inputFilename = "input.mp4";
std::string inputFilename = "../video-0924-bad.h264";
if (avformat_open_input(&formatContext, inputFilename.c_str(), nullptr, nullptr) != 0) {
std::cerr << "无法打开输入文件" << std::endl;
return -1;
}
// 获取音视频流信息
if (avformat_find_stream_info(formatContext, nullptr) < 0) {
std::cerr << "无法获取流信息" << std::endl;
return -1;
}
// 查找第一个视频流索引
int videoStreamIndex = -1;
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
std::cerr << "未找到视频流" << std::endl;
return -1;
}
// 获取视频解码器上下文指针
AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
// AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
const AVCodec* codec = avcodec_find_decoder_by_name("h264_rkmpp");
if (!codec) {
std::cerr << "未找到解码器" << std::endl;
return -1;
}
AVCodecContext* codecContext = avcodec_alloc_context3(codec);
if (!codecContext) {
std::cerr << "无法分配解码器上下文" << std::endl;
return -1;
}
// 设置解码器上下文参数
if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
std::cerr << "无法设置解码器上下文参数" << std::endl;
return -1;
}
// 打开解码器
if (avcodec_open2(codecContext, codec, nullptr) < 0) {
std::cerr << "无法打开解码器" << std::endl;
return -1;
}
// 分配AVPacket和AVFrame内存
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
// 循环读取数据包并进行解码
while (av_read_frame(formatContext, packet) >= 0) {
// 判断数据包是否属于视频流
if (packet->stream_index == videoStreamIndex) {
size += packet->size;
cnt++;
printf("cnt:%d, %02x %02x %02x %02x,%02x\n", cnt,
packet->data[0], packet->data[1], packet->data[2], packet->data[3], packet->data[4]);
// 发送数据包到解码器进行解码
int sendResult = avcodec_send_packet(codecContext, packet);
if (sendResult != 0) {
std::cerr << "发送数据包到解码器时出错:" << sendResult << std::endl;
break;
}
// 接收从解码器返回的帧数据
int receiveResult = avcodec_receive_frame(codecContext, frame);
if (receiveResult == AVERROR(EAGAIN)) { // 需要更多数据
std::cerr << "EAGAIN" << std::endl;
continue;
} else if (receiveResult == AVERROR_EOF) { // 解码完成
break;
} else if (receiveResult < 0) { // 解码出错
std::cerr << "从解码器接收帧时出错:" << receiveResult << std::endl;
break;
}
// 在这里可以对解码后的帧数据进行处理,如渲染、保存等
printf("decoding OK,Y:%d, U:%d, V:%d, w:%d, h:%d, f:%d.\n",
frame->linesize[0], frame->linesize[1], frame->linesize[2],
frame->width, frame->height, frame->format);
}

    av_packet_unref(packet);  // 释放数据包引用计数
}
// 释放资源
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
printf("file size:%ld\n", size);
return 0;

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants