【问题标题】:Encoding raw video to h264 is not playable将原始视频编码为 h264 无法播放
【发布时间】:2020-06-09 11:41:23
【问题描述】:

我想要实现的是,无论是相机还是 rtsp 流编码都可以对其进行解码,然后将其编码为 H264 并将其保存在mp4 容器中。问题是视频无法播放,而没有抛出错误。我可以看到文件在增长,但是什么也没有。我错过了什么?

AVFormatContext* pInputFmtCtx = avformat_alloc_context();
AVInputFormat* inputFormat = av_find_input_format(Format); // format = dshow
avformat_open_input(&pInputFmtCtx, StreamUrl, inputFormat, &options); // StreamUrl is = "video=Logitech Cam" or similiar

...... find stream info and video index

// find decoder, for that particular camera it is RAW_VIDEO
AVCodecParameters* videoCodecParams = pInputFmtCtx->streams[_vidStreamIndex]->codecpar;
AVCodec* videoDecoder = avcodec_find_decoder(videoCodecParams->codec_id);

//init and open VIDEO codec context
pVideoCodecContext = avcodec_alloc_context3(videoDecoder);
avcodec_parameters_to_context(pVideoCodecContext, videoCodecParams);
avcodec_open2(pVideoCodecContext, videoDecoder, null)

// now output format
AVFormatContext* pOutputFmtCtx = null;
avformat_alloc_output_context2(&pOutputFmtCtx, null, null, fileName); // filename is always .mp4

// iterate over pInputFmtCtx->nb_streams
// create new stream and H264 encoder
AVStream* out_stream = avformat_new_stream(pOutputFmtCtx, null);

// init video encoder
AVCodec* videoEncoder = avcodec_find_encoder_by_name("libx264");
pVideoEncodeCodecContext = ffmpeg.avcodec_alloc_context3(videoEncoder);

pVideoEncodeCodecContext->width = pVideoCodecContext->width;
pVideoEncodeCodecContext->height = pVideoCodecContext->height;
pVideoEncodeCodecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
pVideoEncodeCodecContext->bit_rate = 2 * 1000 * 1000;
pVideoEncodeCodecContext->rc_buffer_size = 4 * 1000 * 1000;
pVideoEncodeCodecContext->rc_max_rate = 2 * 1000 * 1000;
pVideoEncodeCodecContext->rc_min_rate = 3 * 1000 * 1000;
pVideoEncodeCodecContext->framerate = framerate;
pVideoEncodeCodecContext->max_b_frames = 0;
pVideoEncodeCodecContext->time_base = av_inv_q(framerate);

av_opt_set(pVideoEncodeCodecContext->priv_data, "preset", "slow", 0);
av_opt_set(pVideoEncodeCodecContext->priv_data, "tune", "zerolatency", 0);
av_opt_set(pVideoEncodeCodecContext->priv_data, "vprofile", "baseline", 0);

// and open it and copy params 
avcodec_open2(pVideoEncodeCodecContext, videoEncoder, null);
avcodec_parameters_from_context(out_stream->codecpar, pVideoEncodeCodecContext)

// open file and write header
avio_open(&pOutputFmtCtx->pb, fileName, AVIO_FLAG_WRITE);
avformat_write_header(pOutputFormatContext, null);

// now reading 
AVPacket* pkt = ffmpeg.av_packet_alloc();
AVFrame* frame = ffmpeg.av_frame_alloc();
AVPacket* out_pkt = ffmpeg.av_packet_alloc();
while (av_read_frame(pInputFmtCtx, pkt) >= 0) 
{
   avcodec_send_packet(pVideoCodecContext, pkt);
   avcodec_receive_frame(pVideoCodecContext, frame);

   // using sws_getContext and sws_scale the frame is converted to YUV_420P
   // which is fine, because I also have preview and I can see the frames fine
   var yuvFrame = _frameConverter.Convert(frame);

   yuvFrame->pts = frame_count++; 
   int ret = avcodec_send_frame(pVideoEncodeCodecContext, yuvFrame);
   while (ret >= 0)
   {
      ret = avcodec_receive_packet(pVideoEncodeCodecContext, out_pkt);

      if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
      {
          break;
      }

      int out_stream_index = _streamMapping[out_pkt->stream_index];
      AVStream* in_stream = pInputFormatContext->streams[out_pkt->stream_index];
      AVStream* out_stream = pOutputFormatContext->streams[out_stream_index];

      //rescale the input timestamps to output timestamps
      out_pkt->pts = av_rescale_q_rnd(out_pkt->pts, in_stream->time_base, pVideoEncodeCodecContext->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
      out_pkt->dts = av_rescale_q_rnd(out_pkt->dts, in_stream->time_base, pVideoEncodeCodecContext->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
      out_pkt->duration = ffmpeg.av_rescale_q(out_pkt->duration, in_stream->time_base, out_stream->time_base);
      out_pkt->stream_index = out_stream_index;
      out_pkt->pos = -1;

      ret = av_interleaved_write_frame(pOutputFormatContext, out_pkt);

      av_packet_unref(out_pkt);
  }
}

// later on
av_write_trailer(pOutputFormatContext);

编辑:按照建议,我提供sample mp4 and log

【问题讨论】:

  • Ffmpeg 总是有一些输出到标准错误。请张贴。同时发布一个无法播放的文件示例。
  • @szatmary 编辑了帖子并提供了两者
  • extradata字段设置错误,mdat包含附件-b流
  • 好的,我会手动设置额外数据。关于附件-b流不太确定,但会看一下并尝试弄清楚。

标签: c ffmpeg libav


【解决方案1】:

已经修好了,我做的是

添加了pVideoEncodeCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;,它修复了avcC atom 未满。

也换了

//rescale the input timestamps to output timestamps
  out_pkt->pts = av_rescale_q_rnd(out_pkt->pts, in_stream->time_base, pVideoEncodeCodecContext->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
  out_pkt->dts = av_rescale_q_rnd(out_pkt->dts, in_stream->time_base, pVideoEncodeCodecContext->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
  out_pkt->duration = ffmpeg.av_rescale_q(out_pkt->duration, in_stream->time_base, out_stream->time_base);
  out_pkt->stream_index = out_stream_index;
  out_pkt->pos = -1;

with(取自 ffmpeg 源码)

av_packet_rescale_ts(out_pkt, pVideoEncodeCodecContext->time_base, out_stream->time_base);

现在我可以使用正确的时间戳完美地工作 .mp4

【讨论】:

    猜你喜欢
    • 2016-07-17
    • 1970-01-01
    • 2018-10-15
    • 2020-02-17
    • 2015-04-13
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多