Encoding raw video to h264 is not playable

Viewed 858

What I'm trying to achieve is, no matter of the camera or rtsp stream encoding to decode it and then encode it to H264 and save it in mp4 container. The problem is that the video is not playable while there is no errors thrown. I can see the file growing, but then nothing. What am I missing?

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

EDIT: as suggested, I'm providing sample mp4 and log

1 Answers

It is fixed, what I have done is

Added pVideoEncodeCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; which fixed the avcC atom being not full.

And also replaced

//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 (tooke it from ffmpeg source)

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

Now I have perfectly working .mp4 with correct timestamps.

Related