title: streaming FLV to RTMP with FFMpeg using H264 codec and C++ API to flv.js
body: <p>I would like to stream live video from webcam using OpenCV using H264 codec and converting that to FLV then stream over RTMP server and catch the stream in browser with flv.js. Basically I have everything working except that I cannot read stream in flv.js. I can open stream with <code>ffplay</code> so I think at least most of the things are set correctly.</p>
  
  <p>My current implementation:</p>
  
  <pre><code>#include &lt;iostream&gt;
  #include &lt;vector&gt;
  
  #include &lt;opencv2/highgui.hpp&gt;
  #include &lt;opencv2/video.hpp&gt;
  
  extern "C" {
  #include &lt;libavformat/avformat.h&gt;
  #include &lt;libavcodec/avcodec.h&gt;
  #include &lt;libavutil/avutil.h&gt;
  #include &lt;libavutil/pixdesc.h&gt;
  #include &lt;libavutil/imgutils.h&gt;
  #include &lt;libswscale/swscale.h&gt;
  }
  
  void stream_video(double width, double height, int fps, int camID)
  {
    av_register_all();
    avformat_network_init();
  
    const char *output = "rtmp://localhost/live/stream";
    const AVRational dst_fps = {fps, 1};
    int ret;
  
    // initialize video capture device
    cv::VideoCapture cam(camID);
    if (!cam.isOpened())
    {
      std::cout &lt;&lt; "Failed to open video capture device!" &lt;&lt; std::endl;
      exit(1);
    }
  
    cam.set(cv::CAP_PROP_FRAME_WIDTH, width);
    cam.set(cv::CAP_PROP_FRAME_HEIGHT, height);
  
    // allocate cv::Mat with extra bytes (required by AVFrame::data)
    std::vector&lt;uint8_t&gt; imgbuf(height * width * 3 + 16);
    cv::Mat image(height, width, CV_8UC3, imgbuf.data(), width * 3);
  
    // open output format context
    AVFormatContext *outctx = nullptr;
    ret = avformat_alloc_output_context2(&amp;outctx, nullptr, "flv", output);
    if (ret &lt; 0)
    {
      std::cout &lt;&lt; "Could not allocate output format context!" &lt;&lt; std::endl;
      exit(1);
    }
  
    // open output IO context
    if (!(outctx-&gt;oformat-&gt;flags &amp; AVFMT_NOFILE))
    {
      ret = avio_open2(&amp;outctx-&gt;pb, output, AVIO_FLAG_WRITE, nullptr, nullptr);
      if (ret &lt; 0)
      {
        std::cout &lt;&lt; "Could not open output IO context!" &lt;&lt; std::endl;
        exit(1);
      }
    }
  
    // create new video stream
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    AVStream *strm = avformat_new_stream(outctx, codec);
    AVCodecContext *avctx = avcodec_alloc_context3(codec);
  
    avctx-&gt;codec_id = AV_CODEC_ID_H264;
    avctx-&gt;width = width;
    avctx-&gt;height = height;
    avctx-&gt;pix_fmt = AV_PIX_FMT_YUV420P;
    avctx-&gt;framerate = dst_fps;
    avctx-&gt;time_base = av_inv_q(dst_fps);
  
    ret = avcodec_parameters_from_context(strm-&gt;codecpar, avctx);
    if (ret &lt; 0)
    {
      std::cout &lt;&lt; "Could not initialize stream codec parameters!" &lt;&lt; std::endl;
      exit(1);
    }
  
    AVDictionary *opts = nullptr;
    av_dict_set(&amp;opts, "preset", "superfast", 0);
    av_dict_set(&amp;opts, "tune", "zerolatency", 0);
  
    // open video encoder
    ret = avcodec_open2(avctx, codec, &amp;opts);
    if (ret &lt; 0)
    {
      std::cout &lt;&lt; "Could not open video encoder!" &lt;&lt; std::endl;
      exit(1);
    }
  
    // initialize sample scaler
    SwsContext *swsctx = sws_getContext(width, height, AV_PIX_FMT_BGR24, width, height, avctx-&gt;pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);
    if (!swsctx)
    {
      std::cout &lt;&lt; "Could not initialize sample scaler!" &lt;&lt; std::endl;
      exit(1);
    }
  
    // allocate frame buffer for encoding
    AVFrame *frame = av_frame_alloc();
  
    std::vector&lt;uint8_t&gt; framebuf(av_image_get_buffer_size(avctx-&gt;pix_fmt, width, height, 1));
    av_image_fill_arrays(frame-&gt;data, frame-&gt;linesize, framebuf.data(), avctx-&gt;pix_fmt, width, height, 1);
    frame-&gt;width = width;
    frame-&gt;height = height;
    frame-&gt;format = static_cast&lt;int&gt;(avctx-&gt;pix_fmt);
  
    // write header
    ret = avformat_write_header(outctx, nullptr);
    if (ret &lt; 0)
    {
      std::cout &lt;&lt; "Could not write header!" &lt;&lt; std::endl;
      exit(1);
    }
  
    // encoding loop
    int64_t frame_pts = 0;
    unsigned nb_frames = 0;
    bool end_of_stream = false;
  
    do
    {
      nb_frames++;
  
      if (!end_of_stream)
      {
        cam &gt;&gt; image;
        // convert cv::Mat to AVFrame.
        const int stride[] = {static_cast&lt;int&gt;(image.step[0])};
        sws_scale(swsctx, &amp;image.data, stride, 0, image.rows, frame-&gt;data, frame-&gt;linesize);
        frame-&gt;pts = frame_pts++;
      }
      // encode video frame.
      AVPacket pkt = {0};
      av_init_packet(&amp;pkt);
  
      ret = avcodec_send_frame(avctx, frame);
      if (ret &lt; 0)
      {
        std::cout &lt;&lt; "Error sending frame to codec context!" &lt;&lt; std::endl;
        exit(1);
      }
  
      ret = avcodec_receive_packet(avctx, &amp;pkt);
      if (ret &lt; 0)
      {
        std::cout &lt;&lt; "Error receiving packet from codec context!" &lt;&lt; std::endl;
        exit(1);
      }
  
      // rescale packet timestamp.
      av_packet_rescale_ts(&amp;pkt, avctx-&gt;time_base, strm-&gt;time_base);
      // write packet.
      pkt.pts = AV_NOPTS_VALUE;
      pkt.dts = AV_NOPTS_VALUE;
      av_interleaved_write_frame(outctx, &amp;pkt);
  
      std::cout &lt;&lt; " Frames: " &lt;&lt; nb_frames &lt;&lt; '\r' &lt;&lt; std::flush;
  
      av_packet_unref(&amp;pkt);
    } while (!end_of_stream);
  
    av_write_trailer(outctx);
    std::cout &lt;&lt; nb_frames &lt;&lt; " frames encoded" &lt;&lt; std::endl;
  
    av_frame_free(&amp;frame);
    avcodec_close(avctx);
    avio_close(outctx-&gt;pb);
    avformat_free_context(outctx);
  }
  
  int main()
  {
    double width = 1280, height = 720, fps = 30;
    int camID = 1;
  
    stream_video(width, height, fps, camID);
  
    return 0;
  }
  </code></pre>
  
  <p>As I said before I can successfully open the stream with <code>ffplay rtmp://localhost/live/stream</code> or <code>ffplay http://localhost:8000/live/stream.flv</code> but I cannot open the stream with <code>flv.js</code> player inside browser with getting errors:</p>
  
  <pre><code>flv: Invalid AVCDecoderConfigurationRecord, lack of data!
  [FLVDemuxer] &gt; Malformed Nalus near timestamp 0, NaluSize &gt; DataSize!
  [FLVDemuxer] &gt; Malformed Nalus near timestamp 1, NaluSize &gt; DataSize!
  [FLVDemuxer] &gt; Malformed Nalus near timestamp 2, NaluSize &gt; DataSize!
  ....
  </code></pre>
  
  <p>I would really appreciate any help of fixing the stream to work properly with <code>flv.js</code>, if I stream video like <code>ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost/live/stream</code> I can open stream in <code>flv.js</code> without any issues, so "this command" I would like to achieve inside code roughly.
  I also put my code on GitHub repository <a href="https://github.com/jkuri/opencv-ffmpeg-rtmp-stream" rel="noreferrer">here</a> if someone would like to compile the code and check on it.</p>