首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >使用FFMpeg将FLV流式传输到RTMP,使用H264编解码器和C++ API传输到flv.js。

使用FFMpeg将FLV流式传输到RTMP,使用H264编解码器和C++ API传输到flv.js。
EN

Stack Overflow用户
提问于 2018-02-02 07:52:02
回答 1查看 6.1K关注 0票数 14

我想流式直播视频从网络摄像头使用OpenCV使用H264编解码器,并转换为FLV,然后流通过RTMP服务器,并与flv.js在浏览器中捕获流。基本上,除了不能在flv.js中读取流之外,所有的东西我都能正常工作。我可以用ffplay打开流,所以我认为至少大多数设置都是正确的。

我目前的实现:

代码语言:javascript
运行
AI代码解释
复制
#include <iostream>
#include <vector>

#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

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 << "Failed to open video capture device!" << 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<uint8_t> 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(&outctx, nullptr, "flv", output);
  if (ret < 0)
  {
    std::cout << "Could not allocate output format context!" << std::endl;
    exit(1);
  }

  // open output IO context
  if (!(outctx->oformat->flags & AVFMT_NOFILE))
  {
    ret = avio_open2(&outctx->pb, output, AVIO_FLAG_WRITE, nullptr, nullptr);
    if (ret < 0)
    {
      std::cout << "Could not open output IO context!" << 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->codec_id = AV_CODEC_ID_H264;
  avctx->width = width;
  avctx->height = height;
  avctx->pix_fmt = AV_PIX_FMT_YUV420P;
  avctx->framerate = dst_fps;
  avctx->time_base = av_inv_q(dst_fps);

  ret = avcodec_parameters_from_context(strm->codecpar, avctx);
  if (ret < 0)
  {
    std::cout << "Could not initialize stream codec parameters!" << std::endl;
    exit(1);
  }

  AVDictionary *opts = nullptr;
  av_dict_set(&opts, "preset", "superfast", 0);
  av_dict_set(&opts, "tune", "zerolatency", 0);

  // open video encoder
  ret = avcodec_open2(avctx, codec, &opts);
  if (ret < 0)
  {
    std::cout << "Could not open video encoder!" << std::endl;
    exit(1);
  }

  // initialize sample scaler
  SwsContext *swsctx = sws_getContext(width, height, AV_PIX_FMT_BGR24, width, height, avctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);
  if (!swsctx)
  {
    std::cout << "Could not initialize sample scaler!" << std::endl;
    exit(1);
  }

  // allocate frame buffer for encoding
  AVFrame *frame = av_frame_alloc();

  std::vector<uint8_t> framebuf(av_image_get_buffer_size(avctx->pix_fmt, width, height, 1));
  av_image_fill_arrays(frame->data, frame->linesize, framebuf.data(), avctx->pix_fmt, width, height, 1);
  frame->width = width;
  frame->height = height;
  frame->format = static_cast<int>(avctx->pix_fmt);

  // write header
  ret = avformat_write_header(outctx, nullptr);
  if (ret < 0)
  {
    std::cout << "Could not write header!" << 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 >> image;
      // convert cv::Mat to AVFrame.
      const int stride[] = {static_cast<int>(image.step[0])};
      sws_scale(swsctx, &image.data, stride, 0, image.rows, frame->data, frame->linesize);
      frame->pts = frame_pts++;
    }
    // encode video frame.
    AVPacket pkt = {0};
    av_init_packet(&pkt);

    ret = avcodec_send_frame(avctx, frame);
    if (ret < 0)
    {
      std::cout << "Error sending frame to codec context!" << std::endl;
      exit(1);
    }

    ret = avcodec_receive_packet(avctx, &pkt);
    if (ret < 0)
    {
      std::cout << "Error receiving packet from codec context!" << std::endl;
      exit(1);
    }

    // rescale packet timestamp.
    av_packet_rescale_ts(&pkt, avctx->time_base, strm->time_base);
    // write packet.
    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = AV_NOPTS_VALUE;
    av_interleaved_write_frame(outctx, &pkt);

    std::cout << " Frames: " << nb_frames << '\r' << std::flush;

    av_packet_unref(&pkt);
  } while (!end_of_stream);

  av_write_trailer(outctx);
  std::cout << nb_frames << " frames encoded" << std::endl;

  av_frame_free(&frame);
  avcodec_close(avctx);
  avio_close(outctx->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;
}

正如我之前所说的,我可以用ffplay rtmp://localhost/live/streamffplay http://localhost:8000/live/stream.flv成功地打开流,但是我不能用浏览器内部的flv.js播放器打开流,因为出现了错误:

代码语言:javascript
运行
AI代码解释
复制
flv: Invalid AVCDecoderConfigurationRecord, lack of data!
[FLVDemuxer] > Malformed Nalus near timestamp 0, NaluSize > DataSize!
[FLVDemuxer] > Malformed Nalus near timestamp 1, NaluSize > DataSize!
[FLVDemuxer] > Malformed Nalus near timestamp 2, NaluSize > DataSize!
....

我真的很感激任何帮助修复流正常工作与flv.js,如果我像ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost/live/stream流视频我可以打开流在flv.js没有任何问题,所以“这个命令”我想实现内部代码大致。如果有人想编译代码并检查它,我也会将我的代码放在GitHub存储库here上。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-05 09:28:40

我自己解决这个问题。这不能如预期工作的主要原因是AVStream empty数据(sps和pps头)是空的。我需要手动从AVCodecContext复制extradataextradata_size (不确定为什么不能自动完成)。在我这样做之后,我第一次在flv.js中看到了图片。然后我只需要正确地计算frame->pts就可以让视频流正常工作。我附上了下面的整个工作代码,以防任何其他人遇到同样的问题。

代码语言:javascript
运行
AI代码解释
复制
#include <iostream>
#include <vector>

#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

cv::VideoCapture get_device(int camID, double width, double height)
{
  cv::VideoCapture cam(camID);
  if (!cam.isOpened())
  {
    std::cout << "Failed to open video capture device!" << std::endl;
    exit(1);
  }

  cam.set(cv::CAP_PROP_FRAME_WIDTH, width);
  cam.set(cv::CAP_PROP_FRAME_HEIGHT, height);

  return cam;
}

void initialize_avformat_context(AVFormatContext *&fctx, const char *format_name)
{
  int ret = avformat_alloc_output_context2(&fctx, nullptr, format_name, nullptr);
  if (ret < 0)
  {
    std::cout << "Could not allocate output format context!" << std::endl;
    exit(1);
  }
}

void initialize_io_context(AVFormatContext *&fctx, const char *output)
{
  if (!(fctx->oformat->flags & AVFMT_NOFILE))
  {
    int ret = avio_open2(&fctx->pb, output, AVIO_FLAG_WRITE, nullptr, nullptr);
    if (ret < 0)
    {
      std::cout << "Could not open output IO context!" << std::endl;
      exit(1);
    }
  }
}

void set_codec_params(AVFormatContext *&fctx, AVCodecContext *&codec_ctx, double width, double height, int fps)
{
  const AVRational dst_fps = {fps, 1};

  codec_ctx->codec_tag = 0;
  codec_ctx->codec_id = AV_CODEC_ID_H264;
  codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
  codec_ctx->width = width;
  codec_ctx->height = height;
  codec_ctx->gop_size = 12;
  codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
  codec_ctx->framerate = dst_fps;
  codec_ctx->time_base = av_inv_q(dst_fps);
  if (fctx->oformat->flags & AVFMT_GLOBALHEADER)
  {
    codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  }
}

void initialize_codec_stream(AVStream *&stream, AVCodecContext *&codec_ctx, AVCodec *&codec)
{
  int ret = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
  if (ret < 0)
  {
    std::cout << "Could not initialize stream codec parameters!" << std::endl;
    exit(1);
  }

  AVDictionary *codec_options = nullptr;
  av_dict_set(&codec_options, "profile", "high", 0);
  av_dict_set(&codec_options, "preset", "superfast", 0);
  av_dict_set(&codec_options, "tune", "zerolatency", 0);

  // open video encoder
  ret = avcodec_open2(codec_ctx, codec, &codec_options);
  if (ret < 0)
  {
    std::cout << "Could not open video encoder!" << std::endl;
    exit(1);
  }
}

SwsContext *initialize_sample_scaler(AVCodecContext *codec_ctx, double width, double height)
{
  SwsContext *swsctx = sws_getContext(width, height, AV_PIX_FMT_BGR24, width, height, codec_ctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);
  if (!swsctx)
  {
    std::cout << "Could not initialize sample scaler!" << std::endl;
    exit(1);
  }

  return swsctx;
}

AVFrame *allocate_frame_buffer(AVCodecContext *codec_ctx, double width, double height)
{
  AVFrame *frame = av_frame_alloc();

  std::vector<uint8_t> framebuf(av_image_get_buffer_size(codec_ctx->pix_fmt, width, height, 1));
  av_image_fill_arrays(frame->data, frame->linesize, framebuf.data(), codec_ctx->pix_fmt, width, height, 1);
  frame->width = width;
  frame->height = height;
  frame->format = static_cast<int>(codec_ctx->pix_fmt);

  return frame;
}

void write_frame(AVCodecContext *codec_ctx, AVFormatContext *fmt_ctx, AVFrame *frame)
{
  AVPacket pkt = {0};
  av_init_packet(&pkt);

  int ret = avcodec_send_frame(codec_ctx, frame);
  if (ret < 0)
  {
    std::cout << "Error sending frame to codec context!" << std::endl;
    exit(1);
  }

  ret = avcodec_receive_packet(codec_ctx, &pkt);
  if (ret < 0)
  {
    std::cout << "Error receiving packet from codec context!" << std::endl;
    exit(1);
  }

  av_interleaved_write_frame(fmt_ctx, &pkt);
  av_packet_unref(&pkt);
}

void stream_video(double width, double height, int fps, int camID)
{
  av_register_all();
  avformat_network_init();

  const char *output = "rtmp://localhost/live/stream";
  int ret;
  auto cam = get_device(camID, width, height);
  std::vector<uint8_t> imgbuf(height * width * 3 + 16);
  cv::Mat image(height, width, CV_8UC3, imgbuf.data(), width * 3);
  AVFormatContext *ofmt_ctx = nullptr;
  AVCodec *out_codec = nullptr;
  AVStream *out_stream = nullptr;
  AVCodecContext *out_codec_ctx = nullptr;

  initialize_avformat_context(ofmt_ctx, "flv");
  initialize_io_context(ofmt_ctx, output);

  out_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
  out_stream = avformat_new_stream(ofmt_ctx, out_codec);
  out_codec_ctx = avcodec_alloc_context3(out_codec);

  set_codec_params(ofmt_ctx, out_codec_ctx, width, height, fps);
  initialize_codec_stream(out_stream, out_codec_ctx, out_codec);

  out_stream->codecpar->extradata = out_codec_ctx->extradata;
  out_stream->codecpar->extradata_size = out_codec_ctx->extradata_size;

  av_dump_format(ofmt_ctx, 0, output, 1);

  auto *swsctx = initialize_sample_scaler(out_codec_ctx, width, height);
  auto *frame = allocate_frame_buffer(out_codec_ctx, width, height);

  int cur_size;
  uint8_t *cur_ptr;

  ret = avformat_write_header(ofmt_ctx, nullptr);
  if (ret < 0)
  {
    std::cout << "Could not write header!" << std::endl;
    exit(1);
  }

  bool end_of_stream = false;
  do
  {
    cam >> image;
    const int stride[] = {static_cast<int>(image.step[0])};
    sws_scale(swsctx, &image.data, stride, 0, image.rows, frame->data, frame->linesize);
    frame->pts += av_rescale_q(1, out_codec_ctx->time_base, out_stream->time_base);
    write_frame(out_codec_ctx, ofmt_ctx, frame);
  } while (!end_of_stream);

  av_write_trailer(ofmt_ctx);

  av_frame_free(&frame);
  avcodec_close(out_codec_ctx);
  avio_close(ofmt_ctx->pb);
  avformat_free_context(ofmt_ctx);
}

int main()
{
  // av_log_set_level(AV_LOG_DEBUG);
  double width = 1280, height = 720;
  int camID = 1, fps = 25;

  stream_video(width, height, fps, camID);

  return 0;
}

就这样!

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48578088

复制
相关文章
理论 | 使用flv.js做直播
为什么要在这个时候探索flv.js做直播呢?原因在于各大浏览器厂商已经默认禁用Flash,之前常见的Flash直播方案需要用户同意使用Flash后才可以正常使用直播功能,这样的用户体验很致命。 在介绍flv.js之前先介绍下常见的直播协议以及给出我对它们的延迟与性能所做的测试得出的数据。 如果你看的很吃力可以先了解下音视频技术的一些基础概念。 常见直播协议 RTMP: 底层基于TCP,在浏览器端依赖Flash。 HTTP-FLV: 基于HTTP流式IO传输FLV,依赖浏览器支持播放FLV。 WebSocke
用户1097444
2022/06/29
3.7K0
理论 | 使用flv.js做直播
使用flv.js做直播
IMWeb前端团队
2017/12/29
13.4K0
使用flv.js做直播
前端如何实现整套视频直播技术流程
首先说明,本篇文章是概念+实践,对于希望了解和实践一个简单的摄像头直播网页功能的人会有帮助,由于篇幅和实践深入度有限,目前demo效果只支持直播播放电脑端以及常用摄像头的实时视频流,其他复杂的功能(例如视频信息实时处理,高并发,网络分发等)尚未实现,还需要进一步探索。
全栈程序员站长
2022/09/15
3.2K0
前端如何实现整套视频直播技术流程
示例:使用 Flv.js 和 Reflv 播放视频
传统的播放视频是采用 flash 播放器方式,然而各大浏览器厂商都不再支持flash , 我们不得不寻找其他解决方案。
张云飞Vir
2021/04/30
7.3K0
使用FFmpeg API获取flv视频时长
哈喽,这一篇记录一个小小的关于FFmpeg的使用,借助FFmpeg的API接口获取flv格式视频的时长。
视界音你而不同
2020/04/22
3.9K0
使用FFmpeg API获取flv视频时长
直播软件系统源码,视频直播软件系统开发中的流媒体技术
视频直播软件系统开发,常用的流媒体传输协议有RTMP,RTSP,HLS,HTTP-FLV
布谷鸟小刘
2021/02/03
1.8K0
一张图概括淘宝直播背后的前端技术 | 赠送多媒体前端手册
2020年,直播带货火爆全网。想一探淘宝直播背后的前端技术?本文将带你进入淘宝直播前端技术的世界。
音视频开发进阶
2020/11/10
2.9K0
一张图概括淘宝直播背后的前端技术 | 赠送多媒体前端手册
vue+flvjs实现自定义控制条的流媒体播放器
是 HTML5 Flash 视频(FLV)播放器,纯原生 JavaScript 开发,没有用到 Flash。由 bilibili 网站开源(Github)。
codeniu
2022/02/25
5.2K0
vue+flvjs实现自定义控制条的流媒体播放器
在HTML5上开发音视频应用的五种思路
无论是实时视频监控还是直播点播等应用场景,最起码的一个操作就是播放视频。其中最基本的思路就是利用OS的API在PC开发桌面应用、在移动端开发Native App,目前这种技术已经成熟,大厂小厂都是这么做的,但是缺点也很明显:开发比较费时费力,需要IOS开发一遍再去Android开发一遍。特别对于一些非刚性需求比如用户家里有一两个监控摄像头,一个礼拜也不会打开看几次,你却要他下载和安装一个APP进行操作,用户安装意愿其实非常低。
音视频开发进阶
2019/12/05
3.2K0
在HTML5上开发音视频应用的五种思路
前端中的直播
因为公司是做在线抓娃娃的,涉及到直播推流这一部分的工作。之前一直都是在App上面进行游戏,所以关于直播这一部分也是与安卓与IOS有关,与前端是没有关系的。但是现在新的需求就是要求这个在线抓娃娃要能够在网页上面进行游戏。所以,我的事情来了。对于没有涉及到前端音视频的这部分的需求,所以初入这一行,还是有点马马虎虎,花了一周多的时间终于是弄明白了。
踏浪
2019/11/28
5.6K0
前端中的直播
前端中的直播
因为公司是做在线抓娃娃的,涉及到直播推流这一部分的工作。之前一直都是在App上面进行游戏,所以关于直播这一部分也是与安卓与IOS有关,与前端是没有关系的。但是现在新的需求就是要求这个在线抓娃娃要能够在网页上面进行游戏。所以,我的事情来了。对于没有涉及到前端音视频的这部分的需求,所以初入这一行,还是有点马马虎虎,花了一周多的时间终于是弄明白了。
踏浪
2019/11/05
4.9K0
RTSP H264 流 MSE 播放
实现了浏览器 MSE (Media Source Extensions) 播放相机 RTSP (Real Time Streaming Protocol) 流。动手体验一下咯~
GoCoding
2021/07/20
2.4K0
FFmpeg常见的音视频处理方法
FFmpeg可使用众多参数,参数内容会根据ffmpeg版本而有差异 这里不再赘述,使用前建议先参考参数及编解码器的叙述。此外参数明细可用ffmpeg -h显示;编解码器名称等明细可用ffmpeg -formats显示。一些常用的参数也可以通过网上查找相关资料获取。
cohen
2020/12/25
3K0
FFmpeg常见的音视频处理方法
Electron 低延迟视频流播放方案探索
去年最后一篇文章介绍了我们的 Electron 桌面客户端的一些优化措施,这篇文章也跟我们正在开发的 Electron 客户端有一定关系。最近我们正在预研在 Electron 页面中实时播放会议视频流的方案。
_sx_
2020/04/10
6.8K0
Electron 低延迟视频流播放方案探索
几种浏览器播放RTSP视频流解决方案
Streamedian 提供了一种“html5_rtsp_player + websock_rtsp_proxy”的技术方案,可以通过html5的video标签直接播放RTSP的视频流。
音视频牛哥
2021/03/22
19.8K0
几种浏览器播放RTSP视频流解决方案
Web直播,你需要先知道这些
本文由 IMWeb 团队成员 万宽红Terrancewan 首发于社区网站 imweb.io。点击阅读原文查看 IMWeb 社区更多精彩文章。 前段时间接触了一些音视频、web直播相关的东西,加上Flash的即将终结、WebRTCd的兴起、小程序的流行,这里总结了一点个人学习收获和大家分享。 Web直播,你需要先知道这些 直播知识小科普 一个典型的直播流程:录制->编码->网络传输(推流->服务器处理->CDN分发)->解码->播放 IPB:一种常用的视频压缩方案,用I帧表示关键帧,B帧表示前向差别帧,P
用户1097444
2022/06/29
2K0
Web直播,你需要先知道这些
Web直播,你需要先知道这些
实际情况下,当用户数量很大时,对推流设备的性能要求很高,复杂的权限管理也难以实现,采用P2P的架构基本不可行。对于个别用户提供上行流、海量用户只进行拉流的场景,腾讯课堂实现了一种P2S的解决方案。进一步学习可阅读jaychen的系列文章《WebRTC直播技术》。
IMWeb前端团队
2019/12/03
2.2K0
Web直播,你需要先知道这些
主流流媒体服务器软件,十款免费的流媒体服务器软件介绍
互联网时代,服务器是网络的重要支撑,大家租用云服务器除了搭建网站服务器之外,还会用到搭建其他各种WEB应用服务器,而流媒体服务器的搭建就是其中一种,那么应该怎么进行流媒体服务器的搭建呢?你知道有那些免费的流媒体服务器软件吗?(你可能想知道:视频流媒体服务器的选择方式?)
全栈程序员站长
2022/07/01
17.6K1
主流流媒体服务器软件,十款免费的流媒体服务器软件介绍
直播技术协议介绍
目前web前端采用的直播技术一般分为以下几类:rtp/rtcp、rtmp、http-flv、hls。下面介绍不同协议
IMWeb前端团队
2019/12/03
2.5K0
直播技术协议介绍
flv.js怎么用?全面解读flv.js代码
flv.js项目的代码有一定规模,如果要研究的话,我建议从demux入手,理解了demux就掌握了媒体数据处理的关键步骤,前面的媒体数据下载和后面的媒体数据播放就变得容易理解了。
smy
2019/01/07
7.8K0

相似问题

使用FFMPEG将H264流式传输到安卓

10

可能使用javacv/ffmpeg将h264流式传输到javafx

110

使用ffmpeg.autogen将HLS视频源重流式传输到RTMP

19

使用FFMPEG成功地将MP4视频流式传输到RTMP

20

使用SDP将RTP流式传输到FFMPEG

33
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档