【问题标题】:Video encoding and keyframes视频编码和关键帧
【发布时间】:2013-02-24 20:26:33
【问题描述】:

我正在逐帧转码视频并使用 x264+ffmpeg 进行编码。原始视频播放良好,但我转码视频的前几帧显示灰色伪影。我知道这是因为时间压缩,这些伪影在几帧后消失了。

看这两张图片,分别是第一帧和第二帧。第三帧是正常的(即没有灰色伪影,也没有像第二帧那样模糊)

如何强制第一帧成为关键帧(即在我的输出视频中完全编码)以使这些伪影不显示?

编辑 - 更多详情

这是我正在做的更多细节。我使用 bit form differents 教程逐帧读取视频并将每一帧重新编码为新视频。我的编码参数如下:

avcodec_get_context_defaults3(c, *codec);
c->codec_id = codec_id;
c->bit_rate = output_bitrate;
/* Resolution must be a multiple of two. */
c->width    = output_width;
c->height   = output_height;
/* timebase: This is the fundamental unit of time (in seconds) in terms
 * of which frame timestamps are represented. For fixed-fps content,
 * timebase should be 1/framerate and timestamp increments should be
 * identical to 1. */
st->r_frame_rate.num = output_framerate_num;
st->r_frame_rate.den = output_framerate_den;
c->time_base.den = output_timebase_den;
c->time_base.num = output_timebase_num;
c->gop_size      = 3; /* emit one intra frame every twelve frames at most */
c->pix_fmt       = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
    /* just for testing, we also add B frames */
    c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
    /* Needed to avoid using macroblocks in which some coeffs overflow.
     * This does not happen with normal video, it just happens here as
     * the motion of the chroma plane does not match the luma plane. */
    c->mb_decision = 2;
}
c->max_b_frames = 2;
c->scenechange_threshold = 0;
c->rc_buffer_size = 0;
c->me_method = ME_ZERO;

然后我处理每一帧,可能在那里做错了什么。解码位:

while(av_read_frame(gFormatCtx, &packet)>=0) {
    // Is this a packet from the video stream?
    if(packet.stream_index==gVideoStreamIndex) {
        // Decode video frame
        avcodec_decode_video2(gVideoCodecCtx, pCurrentFrame, &frameFinished, &packet);
        // Did we get a video frame?
        if(frameFinished) {
            [...]
            if(firstPts == -999) /*Initial value*/
                firstPts = packet.pts;
            deltaPts = packet.pts - firstPts;
            double seconds = deltaPts*av_q2d(gFormatCtx->streams[gVideoStreamIndex]->time_base);
            [...]
            muxing_writeVideoFrame(pCurrentFrame, packet.pts);
        }
    }
}

实际写作:

int muxing_writeVideoFrame(AVFrame *frame, int64_t pts)
{
frameCount = frameCount +1;
if(frameCount > 0)
{
    if (video_st)
        video_pts = (double)video_st->pts.val * video_st->time_base.num /
                    video_st->time_base.den;
    else
        video_pts = 0.0;

    if (video_st && !(video_st && audio_st && audio_pts < video_pts))
    {
        frame->pts = pts;//av_rescale_q(frame_count, video_st->codec->time_base, video_st->time_base);
        write_video_frame(oc, video_st, frame);
    }
}

return 0;
}

static int write_video_frame(AVFormatContext *oc, AVStream *st, AVFrame *frame)
{
    int ret;
    static struct SwsContext *sws_ctx;
    //LOGI(10, frame_count);
    AVCodecContext *c = st->codec;

    /* encode the image */
    AVPacket pkt;
    int got_output;
    av_init_packet(&pkt);
    pkt.data = NULL;    // packet data will be allocated by the encoder
    pkt.size = 0;
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
    fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
    exit(1);
}
/* If size is zero, it means the image was buffered. */
if (got_output) {
    if (c->coded_frame->key_frame)
        pkt.flags |= AV_PKT_FLAG_KEY;
    pkt.stream_index = st->index;
    /* Write the compressed frame to the media file. */
    ret = av_interleaved_write_frame(oc, &pkt);
} else {
    ret = 0;
}

    if (ret != 0) {
        LOGI(10, av_err2str(ret));
        exit(1);
    }
    frame_count++;
    return got_output;
}

【问题讨论】:

  • 除非你显示 ffmpeg 命令和完整的控制台输出,否则我们只能猜测。

标签: encoding ffmpeg x264 keyframe


【解决方案1】:

您的问题很可能出在解码部分而不是编码部分(x264 无法产生此类伪影)。正如上面所说,您似乎不是从关键帧开始解码(或者可能是您的流是 open-gop 并且您没有丢弃第一个 B 帧)。

【讨论】:

  • 为了确认这一点,我已经尝试丢弃我解码的前 10 帧 - 这比我在转码视频中得到的损坏帧的数量要多得多。这给出了完全相同的结果。我还尝试在屏幕上显示所有解码的帧,它们看起来不错。事情的编码方面一定有问题。
  • 在某处上传小型编码样本。但我仍然认为这是解码问题,或者您错误地使用了 x264 API(在转码链中间的某处发送帧损坏)。
  • 感谢您的回复。我发布了一个example original file(在红色框中选择“Valider et telecharger le fichier”)以及显示工件的结果transcoded file
  • 看起来大约 4 个第一帧获得了非常高的 QP 值(非常低的比特率),因此被有效地抽取为纯灰色块。所以我猜你在ffmpeg中使用libx264的方式有问题。由于您的样本是 VFR,因此很可能发送了不正确的 PTS 值,因此它会破坏 libx264 的速率控制(不正确的帧持续时间,因此比特率错误)。顺便说一句,“--keyint 3”对于任何用例来说都是非常短的 GOP。
  • 另外我建议阅读 ffmpeg/libx264 写入日志 (av_log_set_callback) 的内容。我相信那里有很多警告。因为看起来您以某种方式强制帧类型(只有第一帧是 I 帧,其他帧是 P 帧),因此违反了您设置的 GOP 大小(--keyint 3),应该对此发出警告。
【解决方案2】:

这是因为您的视频不是以 keyframe。为了解决这个问题,我使用AviDemux。从 2.6 版开始

  • MP4 复用器
  • MP4v2 复用器

并且可能更多支持在没有关键帧的情况下开始视频。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-12
    • 2012-11-12
    • 2012-02-22
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多