FFmpeg-视频的pts和dts不能正确增加,音频的ptmp和dts不能正确增加 [英] FFmpeg - pts and dts not increasing properly for video, but does for audio

查看:280
本文介绍了FFmpeg-视频的pts和dts不能正确增加,音频的ptmp和dts不能正确增加的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试拍摄两个视频,然后将它们组合成一个视频.但是,当我运行代码时,在解码/编码第二个视频时出现此错误:

I am trying to take two videos and put them together into one video. However, when I run my code, I get this error when decoding/encoding the second video:

Application provided invalid, non monotonically increasing dts to muxer in stream 0

代码完成后,第一个视频完全没问题,但是第二个视频却没问题.我能够产生的最佳结果是第二个视频的后半部分紧接在第一个视频之后.有趣的是,音频完全可以正常播放.

When the code finishes, the first video is completely fine, but the second video is not. The best result that I have been able to produce is where the second half of the second video comes right after the first video. The funny thing is, the audio is completely fine and is as it should be.

过去,我已经能够使用解码代码来简单地复制视频(视频和音频).

In the past, I have been able to use my decoding/encoding code to simply copy a video (both video and audio).

我已经在网上搜索了有关此特定问题的信息,并尝试了建议的解决方案,但似乎都没有一个能解决我的问题.这些是我一直在查看的线程:

I have searched online regarding this specific problem and tried the suggested solutions, but none of them seem to fix my issue. These are the threads that I have been looking at:

FFmpeg-非单调增加dts是什么意思?

如何使用libavformat用相同的编解码器合并2个视频文件(重新混合)?

非单调增加流向多路复用器的dts

这是我编写的当前代码:

This is the current code that I have written:

视频和ClipSequence结构:

Video and ClipSequence structs:

typedef struct Video {
    char* filename;
    AVFormatContext* inputContext;
    AVFormatContext* outputContext;
    AVCodec* videoCodec;
    AVCodec* audioCodec;
    AVStream* inputStream;
    AVStream* outputStream;
    AVCodecContext* videoCodecContext_I; // Input
    AVCodecContext* audioCodecContext_I; // Input
    AVCodecContext* videoCodecContext_O; // Output
    AVCodecContext* audioCodecContext_O; // Output
    int videoStream;
    int audioStream;
    SwrContext* swrContext;
} Video;

typedef struct ClipSequence {
    VideoList* videos;
    AVFormatContext* outputContext;
    AVStream* outputStream;
    int64_t lastpts, lastdts;
    int64_t currentpts, currentdts;
} ClipSequence;

解码和编码(相同的音频代码):

Decoding and encoding (same code for audio):

int decodeVideoSequence(ClipSequence* sequence, Video* video, AVPacket* packet, AVFrame* frame) {
    int response = avcodec_send_packet(video->videoCodecContext_I, packet);
    if (response < 0) {
        printf("[ERROR] Failed to send video packet to decoder\n");
        return response;
    }
    while (response >= 0) {
        response = avcodec_receive_frame(video->videoCodecContext_I, frame);
        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
        } else if (response < 0) {
            printf("[ERROR] Failed to receive video frame from decoder\n");
            return response;
        }
        if (response >= 0) {
            // Do stuff and encode
            sequence->currentpts = packet->pts; // Store decoded packet's pts and dts
            sequence->currentdts = packet->dts;
            if (encodeVideoSequence(sequence, video, frame) < 0) {
                printf("[ERROR] Failed to encode new video\n");
                return -1;
            }
        }
        av_frame_unref(frame);
    }
    return 0;
}

int encodeVideoSequence(ClipSequence* sequence, Video* video, AVFrame* frame) {
    AVPacket* packet = av_packet_alloc();
    if (!packet) {
        printf("[ERROR] Could not allocate memory for video output packet\n");
        return -1;
    }
    int response = avcodec_send_frame(video->videoCodecContext_O, frame);
    if (response < 0) {
        printf("[ERROR] Failed to send video frame for encoding\n");
        return response;
    }
    while (response >= 0) {
        response = avcodec_receive_packet(video->videoCodecContext_O, packet);
        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
        } else if (response < 0) {
            printf("[ERROR] Failed to receive video packet from encoder\n");
            return response;
        }
        // Set packet to have pts and dts based on the previous video's pts and dts
        packet->flags |= AV_PKT_FLAG_KEY;
        packet->pts = sequence->currentpts + sequence->lastpts;
        packet->dts = sequence->currentdts + sequence->lastdts;
        packet->stream_index = video->videoStream;
        packet->duration = 1000; // 60 fps
        response = av_interleaved_write_frame(sequence->outputContext, packet);
        if (response < 0) {
            printf("[ERROR] Failed to write video packet\n");
            break;
        }
    }
    av_packet_unref(packet);
    av_packet_free(&packet);
    return 0;
}

阅读框架:

int readSequenceFrames(ClipSequence* sequence, Video* video, AVPacket* packet, AVFrame* frame) {
    if (!packet) {
        printf("[ERROR] Packet not allocated to be read\n");
        return -1;
    }
    if (!frame) {
        printf("[ERROR] Frame not allocated to be read\n");
        return -1;
    }
    // Sets video and audio codec context parameters
    if (prepareVideoOutStream(video) < 0) {
        printf("[ERROR] Failed to prepare output video stream\n");
        return -1;
    }
    if (prepareAudioOutStream(video) < 0) {
        printf("[ERROR] Failed to prepare output audio stream\n");
        return -1;
    }
    // Prepares audio resampling
    if (initResampler(video->audioCodecContext_I, video->audioCodecContext_O, &(video->swrContext)) < 0) {
        printf("[ERROR] Failed to init audio resampler\n");
        return -1;
    }
    // Read packets
    int frameNum = 0;
    while (av_read_frame(video->inputContext, packet) >= 0) {
        printf("[READ] Reading frame %i\n", frameNum);
        if (packet->stream_index == video->videoStream) {
            if (decodeVideoSequence(sequence, video, packet, frame) < 0) {
                printf("[ERROR] Failed to decode and encode video\n");
                return -1;
            }
        } else if (packet->stream_index == video->audioStream) {
            if (decodeAudioSequence(sequence, video, packet, frame) < 0) {
                printf("[ERROR] Failed to decode and encode audio\n");
                return -1;
            }
        }
        av_packet_unref(packet);
        frameNum++;
    }
    // Increment pts and dts by the last pts and dts in the current video
    sequence->lastpts += sequence->currentpts;
    sequence->lastdts += sequence->currentdts;
    return 0;
}

我认为增加pts和dts时我有正确的逻辑.我不确定我到底想念什么.

I believe that I have the right logic when I am increasing the pts and dts. I am not sure what exactly that I am missing.

谢谢.

推荐答案

   // Increment pts and dts by the last pts and dts in the current video
    sequence->lastpts += sequence->currentpts;
    sequence->lastdts += sequence->currentdts;

这是错误的.首先,暂时忽略PTS,仅处理DTS.

This is wrong. First, ignore PTS for now and only deal with DTS.

DTS不是相对数字,而是绝对数字.将递增数字加在一起会创建一个指数序列.例如:如果这是一个30 fps的视频,并且时基是1/30,则DTS的每一帧都将前进一帧.例如0、1、2、3、4、5、6、7、9

DTS is not a relative number, it's an absolute number. Adding incrementing numbers together creates an exponential sequence. For example: if this is a 30 fps video, and the time base is 1/30, the every frame the DTS will advance by one. e.g. 0, 1, 2, 3, 4, 5, 6, 7, 9

如果继续将它们添加在一起,您将获得:0、1、3、6、10、15、21、28、36、45

If you keep adding them together, you will get: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45

因此, sequence-> lastdts =序列-> currentdts; 不是 sequence->> lastdts + =序列-> currentdts;

设置新的DTS时,您需要添加帧时长例如 packet-> dts = sequence-> lastdts + frame_duration;

When you set the new DTS, you need to add in a frame duration e.g. packet->dts = sequence->lastdts + frame_duration;

否则,如果需要多一帧,则该帧将具有与前一帧相同的DTS

Otherwise this frame will have the same DTS as the previous frame, when it needs to be 1 frame more

下一个PTS:

PTS不是单调的,并且会在时间上倒退.您也无法以这种方式跟踪它,因为下一个PTS的时间戳可能更短.为了解决这个问题,您需要将PTS转换为CTS,然后返回:

PTS is not monotonic and can go backwards in time. You and can not track it this way because the next PTS may be a lower time stamp. To solve this you need to convert PTS to CTS, and then back:

auto cts = packet->pts - packet->dts
packet->dts = // Whatever you are updating the dts to
packet->pts = packet->dts + cts

还在每帧上设置 packet->标志| = AV_PKT_FLAG_KEY; 会在搜索时引起问题

Also setting packet->flags |= AV_PKT_FLAG_KEY; on every frame will cause problems when seeking

这篇关于FFmpeg-视频的pts和dts不能正确增加,音频的ptmp和dts不能正确增加的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆