Что делать, если last_pts › current_pts с использованием ffmpeg libs (C++)

Мне трудно понять, где найти об этом ..

Я создаю простой рекордер, чтобы узнать об этой вселенной сжатия видео, и я столкнулся с некоторыми странными поступками.

Прежде всего, мне нужно объяснить сценарий...

Это очень просто... каждый раз, когда я вызываю av_read_frame(input_context, input_packet), я сохраняю точки в переменной last_pts...

So...

Что меня беспокоит, так это тот факт, что около 10% моих вызовов av_read_frame я получаю input_packet.pts> last_pts

В результате появляется сообщение об ошибке от кодировщика, когда я пытаюсь это сделать... Имея это в виду, я решил просто отбрасывать эти кадры, когда это происходит....

Я думаю, что не стоит просто отбрасывать кадры, потому что, если я их получу, это как-то нужно...

Итак... что делать, когда last_pts > current_pts?

Я вставлю свой тестовый код, который я использую для захвата видео с веб-камеры и сохранения в файл mp4 с кодировщиком libx264.

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

person Rafael Fontes    schedule 11.03.2015    source источник


Ответы (1)


Эти кадры являются кадрами B. B-кадры сохраняются в потоке в порядке декодирования, а не в порядке представления. Если вы посмотрите на DTS, это, вероятно, будет выглядеть нормально. задачей декодера является изменение порядка кадров в соответствии с порядком представления после их декодирования.

РЕДАКТИРОВАТЬ. чтобы исправить свой код, используйте PTS из декодированного кадра, а не из пакета.

person szatmary    schedule 11.03.2015
comment
+1 за то, что направили меня в правильном направлении .. Оказалось, проблема была с моей камерой и / или драйвером. Смена обстановки и камеры сделали свое дело. - person Rafael Fontes; 18.03.2015