Библиотека FFMPEG — перекодирует необработанное изображение в поток h264, и выходной файл не содержит информации о pts и dts

#c #video #ffmpeg

#c #Видео #ffmpeg

Вопрос:

Я пытаюсь с помощью библиотеки ffmpeg c преобразовать несколько необработанных изображений yuyv в поток h264, изображение поступает из памяти и передается в виде строки со скоростью около 24 кадров в секунду, я выполняю соглашение следующим образом:

  1. инициализируйте AVFormatContext, AVCodec, AVCodecContext и создайте новый AVStream. на этом шаге я в основном ссылаюсь на ffmpeg-libav-tutorial, а AVFormatContext использует write_buffer() функцию настройки (см. simplest_ffmpeg_mem_handler)
  2. получите необработанные данные кадра, установите ширину и высоту (1920×1080), а также установите pts и dts. здесь я вручную устанавливаю выходной fps равным 24 и использую глобальный счетчик для подсчета количества кадров, и pts вычисляется этим счетчиком, фрагментом кода ( video_avs является AVStream, output_fps равен 24 и time_base равен 1/24):
     input_frame->width = w;  // 1920
    input_frame->height = h;  // 1080
    input_frame->pkt_dts = input_frame->pts = global_pts;
    global_pts  = video_avs->time_base.den/video_avs->time_base.num / output_fps.num * output_fps.den;
  
  1. преобразуйте его из yuyv в yuv422 (потому что h264 не поддерживает yuyv) и измените его размер с 1920×1080 до 640×480 (потому что мне нужен вывод с таким разрешением), используйте sws_scale()

  2. используйте avcodec_send_frame() и avcodec_receive_packet() для получения выходного пакета. установите output_packet длительность и stream_index, затем используйте av_write_frame() для записи данных кадра.

     AVPacket *output_packet = av_packet_alloc();
    int response = avcodec_send_frame(encoder->video_avcc, frame);
    while (response >= 0) {
        response = avcodec_receive_packet(encoder->video_avcc, output_packet); // !! here output_packet.size is calculated
        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
        } 
        else if (response < 0) {
            printf("Error while sending packet to decoder");  // ??av_err2str(response)会报错
            return response;
        }

        // duration = next_pts - this_pts = timescale / fps = 1 / timebase / fps
        output_packet->duration = (encoder->video_avs->time_base.den / encoder->video_avs->time_base.num) / (output_fps.num / output_fps.den);
        output_packet->stream_index = 0;
        int response = av_write_frame(encoder->avfc, output_packet);  // packet order are not ensure
        if (response != 0) { printf("Error %d while receiving packet from decoder", response); return -1;}   
    }
    av_packet_unref(output_packet);
    av_packet_free(amp;output_packet);
  
  1. в write_buffer() функции вывод видеопотока сохраняется в строковую переменную, а затем я записываю эту строку в файл с ostream и суффиксом mp4.

после всех вышеуказанных шагов вывод.mp4 не может быть воспроизведен, ffprobe output.mp4 -show_frames вывод (изображение):

 Input #0, h264, from '/Users/ming/code/dev/haomo/output.mp4':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: h264 (High 4:2:2), yuv422p(progressive), 640x480, 24.92 fps, 24 tbr, 1200k tbn, 48 tbc
[FRAME]
media_type=video
stream_index=0
key_frame=1
pkt_pts=N/A
pkt_pts_time=N/A
pkt_dts=N/A
pkt_dts_time=N/A
best_effort_timestamp=N/A
best_effort_timestamp_time=N/A
  

Обратите внимание, что до и после вызова av_write_frame() на шаге 4 переданный аргумент output_packet содержит правильную информацию о pts и dts, я не могу понять, почему выходной поток потерял эту информацию.

Ответ №1:

Я понимаю, что выходной поток представляет собой необработанный поток h264, и я напрямую сохраняю этот поток в файл с суффиксом «.mp4», так что на самом деле это неправильный файл mp4.

Затем он сохраняет поток в output.h264 файл и использует ffmpeg для преобразования его в файл mp4: ffmpeg -framerate 24 -i output.h264 -c copy output.mp4 , наконец, он output.mp4 содержит правильные данные pts и может быть воспроизведен.