将原始视频编码为h264格式后无法播放

4

我想要实现的是,无论相机或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);

编辑: 建议如下,我提供了示例mp4和日志


4
FFmpeg 总是会在 stderr 输出一些内容,请把它们贴出来。同时,也请提供一个播放不了的文件示例。 - szatmary
@szatmary编辑了帖子并提供了两个版本。 - Expressingx
extradata字段被设置不正确,而mdat包含一个annex-b流。 - szatmary
好的,我会手动设置extradata。关于annex-b流不是很确定,但我会查看并尝试弄清楚。 - Expressingx
1个回答

1
这是固定的,我所做的是:
添加了 pVideoEncodeCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;,这解决了 avcC 原子不完整的问题。
同时替换了

//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文件,时间戳正确。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接