0

我很难弄清楚在哪里可以找到这个..

我正在构建一个简单的记录器来了解这个视频压缩宇宙,我面临着一些奇怪的行为..

在此之前,我需要解释一下场景......

它非常简单......每次我调用 av_read_frame( input_context, input_packet ) 我将 pts 保存到 last_pts 变量中......

所以...

困扰我的是,我对 av_read_frame 的调用中约有 10% 得到 input_packet.pts > last_pts

当我尝试这样做时,导致来自编码器的错误消息......考虑到这一点,我决定在它发生时丢弃这些帧......

我认为仅仅丢帧不是一个好主意,因为如果我得到它们,它就需要以某种方式......

那么......当 last_pts > current_pts 时该怎么办?

我将粘贴我的测试代码,我使用从网络摄像头捕获视频并使用 libx264 编码器保存到 mp4 文件

#include <QCoreApplication>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavdevice/avdevice.h>
}

#include <QTime>
#include <QThread>
#include <QDebug>

#define SM_DEBUG

static const double max_fps = 30;
static const double min_loop_duration = 1000 / max_fps;
static const double max_duration = 5; // in seconds

static void sleep_if_needed(const int &elapsed) {
    int sleep_duration = min_loop_duration - elapsed;

    if (sleep_duration > 0)  {
        QThread::msleep(sleep_duration);
    }
}

#ifdef SM_DEBUG
static void log_packet(const AVPacket *pkt,
                       const AVRational &time_base,
                       int is_input=0)
{

    qDebug() << ((is_input) ? QString(">>") : QString("<<"))  << "Size:" << QString::number(pkt->size) <<
        "pts:" << QString::number(pkt->pts) <<
        "pts_time:" << QString::number(av_q2d(time_base) * pkt->pts) <<
        "dts:" << QString::number(pkt->dts) <<
        "dts_time:" << QString::number(av_q2d(time_base) * pkt->dts);
}
#endif

int main()
{
    int input_w, input_h, output_w = 640, output_h = 480;

    av_register_all();
    avdevice_register_all();
    avcodec_register_all();
#ifdef SM_DEBUG
    av_log_set_level(AV_LOG_DEBUG);
#else
    av_log_set_level(AV_LOG_ERROR);
#endif

    AVFormatContext *ic;
    AVFormatContext *oc;

    AVInputFormat *ifmt;

    AVDictionary *opts = 0;

    AVCodecContext* dec_ctx;
    AVCodecContext* enc_ctx;
    AVCodec *dec;
    AVCodec *enc;

    AVStream* ist;
    AVStream* ost;

    ifmt = av_find_input_format("v4l2");

    av_dict_set(&opts, "tune", "zerolatency", AV_DICT_APPEND);
    ic = avformat_alloc_context();

    ic->flags |= AVFMT_FLAG_NONBLOCK;

    avformat_open_input(&ic, "/dev/video0", ifmt, &opts);

    avformat_find_stream_info(ic, NULL);

    av_dump_format(ic, 0, ic->filename, 0);

    AVFrame *frame;
    AVFrame *tmp_frame;

    ist = ic->streams[0];

    dec_ctx =  ist->codec;

    input_w = dec_ctx->width;
    input_h = dec_ctx->height;

    dec_ctx->flags |= CODEC_FLAG_LOW_DELAY;
    dec = avcodec_find_decoder(dec_ctx->codec_id);

    av_format_set_video_codec(ic, dec);
    avcodec_open2(dec_ctx, dec, NULL);

    // output

    avformat_alloc_output_context2(&oc, NULL, "MP4", "/home/poste9/grava.mp4");

    enc = avcodec_find_encoder(AV_CODEC_ID_H264);
    ost = avformat_new_stream(oc, enc);
    enc_ctx = ost->codec;

    enc_ctx->codec_id = AV_CODEC_ID_H264;
    enc_ctx->width = output_w;
    enc_ctx->height = output_h;

    ost->time_base.num = ist->time_base.num;
    ost->time_base.den = ist->time_base.den;

    enc_ctx->time_base = ost->time_base;

    enc_ctx->gop_size = 250;
    enc_ctx->keyint_min = 25;
    enc_ctx->qmax = 51;
    enc_ctx->qmin = 30;
    enc_ctx->pix_fmt = AV_PIX_FMT_YUV422P;
    enc_ctx->max_b_frames = 6;
    enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
    enc_ctx->flags |= CODEC_FLAG_LOW_DELAY;

    avcodec_open2(enc_ctx, enc, NULL);

    avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
               &oc->interrupt_callback, NULL);

    av_dump_format(oc, 0, oc->filename, 1);

    avformat_write_header(oc, NULL);

    struct SwsContext *sws_ctx;

    sws_ctx = sws_getContext(input_w, input_h,
                             dec_ctx->pix_fmt,
                             output_w, output_h, enc_ctx->pix_fmt,
                             SWS_BICUBIC, NULL, NULL, NULL);

    frame = av_frame_alloc();
    tmp_frame = av_frame_alloc();

    frame->format = enc_ctx->pix_fmt;
    frame->width = output_w;
    frame->height = output_h;
    frame->pts = AV_NOPTS_VALUE;

    av_frame_get_buffer(frame, 32);
    av_frame_make_writable(frame);

    int got_picture=0;
    int got_packet=0;

    double recording_duration = 0;

    QTime timer;

    AVPacket pkt_out;

    av_init_packet(&pkt_out);

    timer.start();

    bool started_recording = false;

    int64_t start_time = 0;

    int64_t last_pts = INT64_MIN;

    while(1) {
        timer.restart();
        AVPacket pkt_in;

        av_read_frame(ic, &pkt_in);

        if (pkt_in.size == 0) {
            sleep_if_needed(timer.elapsed());
            continue;
        }

        avcodec_decode_video2(dec_ctx, tmp_frame, &got_picture, &pkt_in);

#ifdef SM_DEBUG
        log_packet(&pkt_in, ist->time_base, 1);
#endif

        if (!started_recording) {

            start_time = pkt_in.dts;
            started_recording = true;
        }

        if (pkt_in.pts < last_pts) {

            sleep_if_needed(timer.elapsed());

            continue;
        }

        last_pts = pkt_in.pts;

        frame->pts = (pkt_in.dts - start_time);

        if (!got_picture) {

            av_free_packet(&pkt_in);

            sleep_if_needed(timer.elapsed());

            continue;
        } else {
            sws_scale(sws_ctx, tmp_frame->data, tmp_frame->linesize,
              0, input_h, frame->data, frame->linesize);

            av_free_packet(&pkt_in);
        }

        av_init_packet(&pkt_out);

        avcodec_encode_video2(enc_ctx, &pkt_out, frame, &got_packet);

        if (got_packet) {

            if (pkt_out.pts < pkt_out.dts) {
                pkt_out.dts = pkt_out.pts;
            }

            pkt_out.stream_index = 0;

            recording_duration = pkt_out.pts * av_q2d(ost->time_base);
#ifdef SM_DEBUG
            log_packet(&pkt_out, ost->time_base, 0);
#endif

            av_interleaved_write_frame(oc, &pkt_out);

            av_free_packet(&pkt_out);
        }

        if (recording_duration >= max_duration) {

            break;

        } else {

            sleep_if_needed(timer.elapsed());
        }
    }

    av_write_trailer(oc);

    av_dict_free(&opts);

    av_frame_free(&frame);
    av_frame_free(&tmp_frame);

    sws_freeContext(sws_ctx);

    avcodec_close(dec_ctx);
    avcodec_close(enc_ctx);

    avio_close(oc->pb);
    avformat_free_context(oc);

    avformat_close_input(&ic);

    return 0;
}
4

1 回答 1

3

这些帧是B帧。B 帧以解码顺序而不是呈现顺序保存到流中。如果您查看 DTS,它可能看起来还不错。解码器的工作是在解码后将帧重新排序为呈现顺序。

编辑。要修复您的代码,请使用解码帧中的 PTS,而不是数据包。

于 2015-03-11T17:26:45.893 回答