lundi 21 juin 2021

How do you properly mux an mp4 file with FFmpeg 4?

How do you properly mux an mp4 file with FFmpeg 4? I have an issue with the time base, I'm pretty sure. Using ffprobe, I found encoding 24 frames for a 24 FPS output video which should theoretically produce a 1-second video, instead produces a 4 millisecond, 142.61 FPS video with a bitrate 157326 kb/s. I have been stuck on this for months. What's the issue? How do I fix this?

void Camera::Encode(Frame frame) {
  if (av_frame_make_writable(frame_) < 0)
    throw std::runtime_error("failed to write to frame");

  frame_->data = frame.data;
  frame_->pts += av_rescale_q(1, context_->time_base, stream_->time_base);

  if (avcodec_send_frame(context_, frame_) < 0)
    throw std::runtime_error("failed to send a frame for encoding");

  int ret = 0;
  AVPacket packet = { 0 };
  while (ret >= 0) {
    ret = avcodec_receive_packet(context_, &packet);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
      break;
    else if (ret < 0)
      throw std::runtime_error("failed to encode frame");

    ret = av_interleaved_write_frame(format_, &packet);
    av_packet_unref(&packet);
  }
}

void Camera::Start() {
  avformat_alloc_output_context2(&format_, NULL, "mp4", output_.c_str());
  if (!format_)
    throw std::runtime_error("failed to find output format");

  stream_ = avformat_new_stream(format_, NULL);
  if (!stream_)
    throw std::runtime_error("failed to allocate video stream");

  stream_->id = format_->nb_streams-1;
  stream_->codecpar->codec_id = AV_CODEC_ID_H264;
  stream_->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
  stream_->codecpar->width = frame.width;
  stream_->codecpar->height = frame.height;
  stream_->codecpar->bit_rate = 0.28 * fps_ * stream_->codecpar->width *
                                stream_->codecpar->height;
  stream_->codecpar->format = AV_PIX_FMT_YUV420P;
  stream_->time_base = av_make_q(1, fps_);
  stream_->r_frame_rate = av_make_q(fps_, 1);
  stream_->avg_frame_rate = av_make_q(fps_, 1);

  AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
  if (!codec)
    throw std::runtime_error("failed to find codec");

  context_ = avcodec_alloc_context3(codec);
  if (!context_)
    throw std::runtime_error("failed to allocate video codec context");

  context_->width = stream_->codecpar->width;
  context_->height = stream_->codecpar->height;
  context_->bit_rate = stream_->codecpar->bit_rate;
  context_->time_base = av_make_q(1, fps_);
  context_->framerate = av_make_q(fps_, 1);
  context_->gop_size = 10;
  context_->max_b_frames = 1;
  context_->pix_fmt = AV_PIX_FMT_YUV420P;

  if (avcodec_open2(context_, codec, NULL) < 0)
    throw std::runtime_error("failed to open codec");

  if (format_->oformat->flags & AVFMT_GLOBALHEADER)
    context_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

  if (!(format_->flags & AVFMT_NOFILE))
    if (avio_open(&format_->pb, output_.c_str(), AVIO_FLAG_WRITE) < 0)
      throw std::runtime_error("failed to open video file");

  if (avformat_write_header(format_, NULL) < 0)
    throw std::runtime_error("failed to write headers");

  frame_ = av_frame_alloc();
  if (!frame_)
    throw std::runtime_error("failed to allocate video frame");

  frame_->pts = 0;
  frame_->width = context_->width;
  frame_->height = context_->height;
  frame_->format = AV_PIX_FMT_YUV420P;

  if (av_frame_get_buffer(frame_, 32) < 0)
    throw std::runtime_error("failed to allocate the video frame data");
}

Aucun commentaire:

Enregistrer un commentaire