FFmpeg C-API解码1200x1600视频出现伪影,是否操作有误?
问题:FFmpeg解码1200x1600视频出现周期性伪影
我在Android平台使用FFmpeg 5.1的C-API结合libx264编码H.264视频,现需在Linux的C++应用中回放这些视频。这些视频在浏览器、mplayer、ffplay等播放器中均可正常播放,使用命令ffmpeg -i recording.mp4 -start_number 0 -qscale:v 5 %06d.jpg提取的帧也显示正常。
但在我的C++应用中,会周期性且可复现地出现伪影(如画面上方的亮像素),伪影不会累积,且与关键帧无关,不会影响后续帧。我使用OpenCV可视化输出,确认问题并非出在BGR格式转换——仅显示Y通道(亮度灰度图)时伪影已存在。
伪影仅出现在1200x1600分辨率的视频中,需注意1200无法被32整除,FFmpeg会添加填充,但我已处理该问题。1920x1440分辨率的视频回放无伪影。
以下是我使用的解码代码:
extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> } #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> int main(int argc, char** argv) { int ret; auto pkt = av_packet_alloc(); if (!pkt) { std::cerr << "Failed av_packet_alloc()" << std::endl; exit(1); } AVFormatContext* av_format = avformat_alloc_context(); ret = avformat_open_input(&av_format, FILE_NAME, nullptr, nullptr); if (ret < 0) { std::cerr << "Failed avformat_open_input, Error: " << ret << std::endl; exit(1); } av_dump_format(av_format, 0, FILE_NAME, 0); auto video_st_number = av_find_best_stream(av_format, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); if (video_st_number < 0) { std::cerr << "av_find_best_stream couldn't find video stream" << std::endl; exit(1); } auto video_st = av_format->streams[video_st_number]; auto codec_id = video_st->codecpar->codec_id; std::cout << "Duration " << video_st->duration << std::endl; std::cout << "n_frames " << video_st->nb_frames << std::endl; auto frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); } auto codec = avcodec_find_decoder(codec_id); if (!codec) { fprintf(stderr, "Codec not found\n"); exit(1); } auto c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate video codec context\n"); exit(1); } if ((ret = avcodec_parameters_to_context(c, video_st->codecpar))) { fprintf(stderr, "Failed avcodec_parameters_to_context\n"); exit(1); } c->pix_fmt = AV_PIX_FMT_YUV420P;///Not really necessary c->thread_count = 1;///No impact /* open it */ if (avcodec_open2(c, codec, NULL) < 0) { fprintf(stderr, "Could not open codec\n "); exit(1); } std::size_t counter = 0; std::size_t n_keyframes = 0; while (ret >= 0) { ret = av_read_frame(av_format, pkt); if (pkt->size == 0) { std::cout << "Skipping packet of size zero" << std::endl; av_packet_unref(pkt); continue; } while (avcodec_send_packet(c, pkt) != 0) { if (avcodec_receive_frame(c, frame) != 0) { std::cerr << "Error receiving frame" << std::endl; exit(1); } else { n_keyframes += frame->key_frame; std::cout << "Decoded " << ++counter << " frames. Frame No. " << frame->pts / pkt->duration << " " << frame->decode_error_flags << " " << frame->key_frame << " " << n_keyframes << " " << frame->pkt_dts << std::endl; } display(frame); } av_packet_unref(pkt); } avcodec_send_packet(c, nullptr); std::cout << "Flushing decoder" << std::endl; while (avcodec_receive_frame(c, frame) == 0) { n_keyframes += frame->key_frame; std::cout << "Decoded " << ++counter << " frames. Frame No. " << frame->pts << " " << frame->decode_error_flags << " " << frame->key_frame << " " << n_keyframes << " " << frame->pkt_dts << std::endl; display(frame); } avcodec_free_context(&c); avformat_free_context(av_format); av_frame_free(&frame); av_packet_free(&pkt); return 0; }
解码画面对比


为完整起见,以下是使用OpenCV的display函数:
void display(const AVFrame* frame) { static std::vector<uint8_t> yuv_buffer; yuv_buffer.resize(frame->linesize[0] * 3 / 2 * frame->width); cv::Mat mYUV(frame->height * 3 / 2, frame->width, CV_8UC1, yuv_buffer.data(), frame->linesize[0]); memcpy(mYUV.ptr(), frame->data[0], frame->linesize[0] * frame->height); //cv::imshow("grayscale", mYUV.rowRange(0, frame->height)); //cv::imshow("u", cv::Mat(frame->height / 2, frame->width / 2, CV_8UC1, frame->data[1], frame->linesize[1])); //cv::imshow("v", cv::Mat(frame->height / 2, frame->width / 2, CV_8UC1, frame->data[2], frame->linesize[2])); int dest_row = frame->height; for (int j = 0; j < frame->height / 2; j++) { memcpy(mYUV.ptr(dest_row), frame->data[1] + frame->linesize[1] * j, frame->width); j++; memcpy(mYUV.ptr(dest_row) + frame->width / 2, frame->data[1] + frame->linesize[1] * j, frame->width); dest_row++; } for (int j = 0; j < frame->height / 2; j++) { memcpy(mYUV.ptr(dest_row), frame->data[2] + frame->linesize[2] * j, frame->width); j++; memcpy(mYUV.ptr(dest_row) + frame->width / 2, frame->data[2] + frame->linesize[2] * j, frame->width); dest_row++; } cv::Mat mRGB(frame->height, frame->width, CV_8UC3); cvtColor(mYUV, mRGB, cv::COLOR_YUV2BGR_I420, 3); cv::imshow("Video", mRGB); cv::waitKey(0); }
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

