Кодирование необработанного видео в h264 не воспроизводится - PullRequest
3 голосов
/ 25 февраля 2020

Я пытаюсь добиться, независимо от того, используется ли камера или кодирование потока 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 и журнал

1 Ответ

1 голос
/ 26 февраля 2020

Исправлено, то, что я сделал, это

Добавлен 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;

с (возьмите его из источника ffmpeg)

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

Теперь у меня отлично работает .mp4 с правильными временными метками.

...