FFmpeg - pts и dts не увеличиваются должным образом для видео, но увеличиваются для звука - PullRequest
0 голосов
/ 28 мая 2020

Я пытаюсь снять два видео и объединить их в одно видео. Однако, когда я запускаю свой код, я получаю эту ошибку при декодировании / кодировании второго видео:

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

Когда код завершается, первое видео полностью в порядке, а второе видео - нет. Лучший результат, который мне удалось получить, - это когда вторая половина второго видео идет сразу после первого видео. Самое смешное, что со звуком все в порядке, и он такой, каким должен быть.

Раньше я мог использовать свой код декодирования / кодирования, чтобы просто копировать видео (как видео, так и аудио).

Я искал в Интернете эту конкретную проблему c и пробовал предлагаемые решения, но, похоже, ни одно из них не решило мою проблему. Это потоки, на которые я смотрел:

FFmpeg - Что означает немонотонно увеличивающийся dts?

Как использовать libavformat для объединения 2 видео файлы с тем же кодом c (повторное мультиплексирование)?

Немонотонно увеличивающееся dts в мультиплексор в потоке

Это текущий код, который у меня есть написано:

Структуры Video и ClipSequence:

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;

Декодирование и кодирование (тот же код для аудио):

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

Я считаю, что у меня правильный лог c, когда я увеличиваю pts и dts. Я не уверен, что именно мне не хватает.

Спасибо.

1 Ответ

1 голос
/ 28 мая 2020
   // Increment pts and dts by the last pts and dts in the current video
    sequence->lastpts += sequence->currentpts;
    sequence->lastdts += sequence->currentdts;

Это неправильно. Во-первых, пока игнорируйте PTS и работайте только с DTS.

DTS - это не относительное число, это абсолютное число. Сложение увеличивающихся чисел вместе создает экспоненциальную последовательность. Например: если это видео со скоростью 30 кадров в секунду и временная база 1/30, каждый кадр DTS будет увеличиваться на один. например, 0, 1, 2, 3, 4, 5, 6, 7, 9

Если вы продолжите складывать их вместе, вы получите: 0, 1, 3, 6, 10, 15, 21, 28 , 36, 45

Итак, sequence->lastdts = sequence->currentdts; not sequence->lastdts += sequence->currentdts;

Когда вы устанавливаете новый DTS, вам нужно добавить длительность кадра, например packet->dts = sequence->lastdts + frame_duration;

В противном случае этот кадр будет иметь тот же DTS, что и предыдущий, когда он должен быть на 1 кадр больше

Следующий PTS:

PTS не монотонный c и может go назад в время. Вы и не можете отследить это таким образом, потому что следующий PTS может быть более низкой отметкой времени. Чтобы решить эту проблему, вам нужно преобразовать PTS в CTS, а затем обратно:

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

Также установка packet->flags |= AV_PKT_FLAG_KEY; для каждого кадра вызовет проблемы при поиске

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...