You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

解码画面对比

解码后带伪影的画面
FFmpeg命令行提取的画面

为完整起见,以下是使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:51:31