当前位置:   article > 正文

FFmpeg 代码实现流媒体推流(RTSP)_ffmpeg rtsp推流

ffmpeg rtsp推流

实时录屏并把视频推流到RTSP服务器,具体流程是抓取屏幕内容(bitmap),并把bitmap转化为YUV,接着把YUV编码成H264,再把H264码流推到RTSP服务器;把采集到的PCM编码为AAC,再把AAC推流至RTSP服务器。

RTSP服务器使用的是HappyTime的免费试用版本。

1. bitmap转YUV

我抓到的bitmap是BGRA格式的,所以使用的图像格式是AV_PIX_FMT_BGRA,cropImage是含有rgba图像的数组

  1. bool init_RGB_to_YUV(){
  2. //BGRA 转 YUV
  3. swrCtxBGRA2YUV = sws_getContext(
  4. cap_w, cap_h, AV_PIX_FMT_BGRA,
  5. cap_w, cap_h, AV_PIX_FMT_YUV420P,
  6. SWS_BICUBIC,
  7. NULL, NULL, NULL
  8. );
  9. //创建BGRA帧
  10. frame_bgra = av_frame_alloc();
  11. frame_bgra->format = AV_PIX_FMT_BGRA;
  12. frame_bgra->width = cap_w;
  13. frame_bgra->height = cap_h;
  14. if (av_frame_get_buffer(frame_bgra, 32) < 0) {
  15. printf("Failed: av_frame_get_buffer\n");
  16. return false;
  17. }
  18. frame_bgra->data[0] = cropImage;
  19. //YUV帧
  20. frame_yuv = av_frame_alloc();
  21. frame_yuv->width = cap_w;
  22. frame_yuv->height = cap_h;
  23. frame_yuv->format = AV_PIX_FMT_YUV420P;
  24. //
  25. uint8_t *picture_buf = (uint8_t *)av_malloc(cap_w * cap_h * 1.5);
  26. if (av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, picture_buf, AV_PIX_FMT_YUV420P, cap_w, cap_h, 1) < 0){
  27. printf("Failed: av_image_fill_arrays\n");
  28. return false;
  29. }
  30. return true;
  31. }

调用:

  1. //BGRA 转 YUV
  2. if (sws_scale(swrCtxBGRA2YUV,
  3. frame_bgra->data, frame_bgra->linesize,
  4. 0, cap_h,
  5. frame_yuv->data, frame_yuv->linesize) < 0)
  6. {
  7. printf("失败:BGRA 转 YUV\n");
  8. return;
  9. }
  10. frame_yuv->pts = av_gettime();

由于我是实时抓取的屏幕,frame_yuv->pts设为当前的时间戳,以保证能正常播放。

2. H264编码

  1. bool init_YUV_to_H264(){
  2. //寻找编码器
  3. codec_h264 = avcodec_find_encoder(AV_CODEC_ID_H264);
  4. if (!codec_h264){
  5. printf("Fail: avcodec_find_encoder\n");
  6. return false;
  7. }
  8. //编码器上下文
  9. codec_ctx_h264 = avcodec_alloc_context3(codec_h264);
  10. if (!codec_ctx_h264){
  11. printf("Fail: avcodec_alloc_context3\n");
  12. return false;
  13. }
  14. codec_ctx_h264->pix_fmt = AV_PIX_FMT_YUV420P;
  15. codec_ctx_h264->codec_type = AVMEDIA_TYPE_VIDEO;
  16. codec_ctx_h264->width = cap_w;
  17. codec_ctx_h264->height = cap_h;
  18. codec_ctx_h264->channels = 3;
  19. codec_ctx_h264->time_base = { 1, 25 };
  20. codec_ctx_h264->gop_size = 5; //图像组两个关键帧(I帧)的距离
  21. codec_ctx_h264->max_b_frames = 0;
  22. //codec_ctx_h264->qcompress = 0.6;
  23. //codec_ctx_h264->bit_rate = 90000;
  24. codec_ctx_h264->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //添加PPS、SPS
  25. av_opt_set(codec_ctx_h264->priv_data, "preset", "ultrafast", 0); //快速编码,但会损失质量
  26. //av_opt_set(codec_ctx_h264->priv_data, "tune", "zerolatency", 0); //适用于快速编码和低延迟流式传输,但是会出现绿屏
  27. //av_opt_set(codec_ctx_h264->priv_data, "x264opts", "crf=26:vbv-maxrate=728:vbv-bufsize=3640:keyint=25", 0);
  28. //打开编码器
  29. if (avcodec_open2(codec_ctx_h264, codec_h264, NULL) < 0){
  30. printf("Fail: avcodec_open2\n");
  31. return false;
  32. }
  33. //用于接收编码好的H264
  34. pkt_h264 = av_packet_alloc();
  35. return true;
  36. }

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

调用:

  1. ret = avcodec_send_frame(codec_ctx_h264, frame_yuv);
  2. if (ret < 0){
  3. printf("send frame fail\n");
  4. return;
  5. }
  6. while (ret >= 0) {
  7. ret = avcodec_receive_packet(codec_ctx_h264, pkt_h264);
  8. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
  9. break;
  10. }
  11. if (ret < 0){
  12. printf("Error during encoding\n");
  13. break;
  14. }
  15. pkt_h264->stream_index = videoindex;
  16. //printf("pkt_h264 timestamp = %d\n", pkt_h264->pts);
  17. if (av_interleaved_write_frame(fmt_ctx, pkt_h264) < 0) {
  18. printf("Error muxing packet\n");
  19. }
  20. av_packet_unref(pkt_h264);
  21. }

3. AAC编码 

  1. bool init_PCM_to_AAC(){
  2. codec_aac = avcodec_find_encoder(AV_CODEC_ID_AAC);
  3. if (!codec_aac) {
  4. printf("avcodec_find_encoder fail\n");
  5. return false;
  6. }
  7. codec_ctx_aac = avcodec_alloc_context3(codec_aac);
  8. if (!codec_ctx_aac) {
  9. printf("avcodec_find_encoder fail\n");
  10. return false;
  11. }
  12. codec_ctx_aac->sample_fmt = AV_SAMPLE_FMT_FLT;
  13. codec_ctx_aac->codec_type = AVMEDIA_TYPE_AUDIO;
  14. codec_ctx_aac->channels = channels;
  15. codec_ctx_aac->channel_layout = av_get_default_channel_layout(channels);
  16. codec_ctx_aac->sample_rate = sample_rete;
  17. if (avcodec_open2(codec_ctx_aac, codec_aac, NULL) < 0) {
  18. printf("open codec fail\n");
  19. return false;
  20. }
  21. swrCtxS162FLT = swr_alloc_set_opts(NULL,
  22. codec_ctx_aac->channel_layout, codec_ctx_aac->sample_fmt, codec_ctx_aac->sample_rate,
  23. codec_ctx_aac->channel_layout, AV_SAMPLE_FMT_S16, codec_ctx_aac->sample_rate,
  24. 0, 0);
  25. if (!swrCtxS162FLT)
  26. {
  27. printf("swr_alloc_set_opts error\n");
  28. return false;
  29. }
  30. if (swr_init(swrCtxS162FLT) < 0) {
  31. printf("open resample fail\n");
  32. return false;
  33. }
  34. frame_pcm = av_frame_alloc();
  35. frame_pcm->nb_samples = nbSamples_; //一帧音频存放的样本数量
  36. frame_pcm->format = codec_ctx_aac->sample_fmt;
  37. frame_pcm->channels = codec_ctx_aac->channels;
  38. frame_pcm->channel_layout = codec_ctx_aac->channel_layout;
  39. if (av_frame_get_buffer(frame_pcm, 0) < 0) {
  40. printf("av_frame_get_buffer error\n");
  41. return false;
  42. }
  43. pkt_aac = av_packet_alloc();
  44. return true;
  45. }

调用:
其中pcm_buff是包含pcm数据的数组

  1. const uint8_t *pcm[1];
  2. pcm[0] = pcm_buff;
  3. int len = swr_convert(swrCtxS162FLT,
  4. frame_pcm->data, frame_pcm->nb_samples,
  5. pcm, nbSamples_);
  6. if (len <= 0) {
  7. printf("---Encodec:PCM->AAC--- swr_convert fail \n");
  8. return;
  9. }
  10. frame_pcm->pts = av_gettime();
  11. //printf("channels = %d\n", frame_pcm->channels);
  12. //printf("framePCM->linesize = %6d %6d\n", frame_pcm->linesize[0], frame_pcm->linesize[1]);
  13. //AAC编码
  14. int ret = avcodec_send_frame(codec_ctx_aac, frame_pcm);
  15. if (ret < 0){
  16. printf("send frame fail\n");
  17. return;
  18. }
  19. ret = avcodec_receive_packet(codec_ctx_aac, pkt_aac);
  20. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
  21. return;
  22. }
  23. if (ret < 0){
  24. printf("Error during encoding\n");
  25. return;
  26. }
  27. pkt_aac->stream_index = audioindex;
  28. //printf("pkt_aac timestamp = %d\n", pkt_aac->pts);
  29. if (av_interleaved_write_frame(fmt_ctx, pkt_aac) < 0) {
  30. printf("Error muxing packet\n");
  31. }
  32. av_packet_unref(pkt_aac);

4. 推流器

使用udp传输时传到1400多帧就断开链接了,原因不明,所以改用使用tcp协议传输

  1. bool init_rtsp_pusher(){
  2. //RTSP
  3. if (avformat_alloc_output_context2(&fmt_ctx, NULL, "RTSP", RTSP_URL.c_str()) < 0){
  4. printf("Fail: avformat_alloc_output_context2\n");
  5. return false;
  6. }
  7. //使用tcp协议传输
  8. av_opt_set(fmt_ctx->priv_data, "rtsp_transport", "tcp", 0);
  9. //检查所有流是否都有数据,如果没有数据会等待max_interleave_delta微秒
  10. fmt_ctx->max_interleave_delta = 1000000;
  11. //输出视频流
  12. AVStream *video_s = avformat_new_stream(fmt_ctx, codec_h264);
  13. if (!video_s){
  14. printf("Fail: avformat_new_stream\n");
  15. return false;
  16. }
  17. video_s->time_base = { 1, 25 };
  18. videoindex = video_s->id = fmt_ctx->nb_streams - 1; //加入到fmt_ctx流
  19. //复制AVCodecContext的设置
  20. if (avcodec_copy_context(video_s->codec, codec_ctx_h264) < 0) {
  21. printf("Fail: avcodec_copy_context\n");
  22. return false;
  23. }
  24. video_s->codec->codec_tag = 0;
  25. if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  26. video_s->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  27. avcodec_parameters_from_context(video_s->codecpar, codec_ctx_h264);
  28. //输出音频流
  29. AVStream *audio_s = avformat_new_stream(fmt_ctx, codec_ctx_aac->codec);
  30. if (!audio_s){
  31. printf("Fail: avformat_new_stream\n");
  32. return false;
  33. }
  34. audio_s->time_base = { 1, 25 };
  35. audioindex = audio_s->id = fmt_ctx->nb_streams - 1;
  36. //复制AVCodecContext的设置
  37. if (avcodec_copy_context(audio_s->codec, codec_ctx_aac) < 0) {
  38. printf("Fail: avcodec_copy_context\n");
  39. return false;
  40. }
  41. audio_s->codec->codec_tag = 0;
  42. if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  43. audio_s->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  44. avcodec_parameters_from_context(audio_s->codecpar, codec_ctx_aac);
  45. //printf("fmt_ctx nb_streams = %d\n", fmt_ctx->nb_streams);
  46. av_dump_format(fmt_ctx, 0, fmt_ctx->filename, 1);
  47. if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) { //???
  48. //打开输出URL(Open output URL)
  49. if (avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE) < 0) {
  50. printf("Fail: avio_open('%s')\n", fmt_ctx->filename);
  51. return false;
  52. }
  53. }
  54. return true;
  55. }

结果 

 

 原文链接:FFmpeg 代码实现流媒体推流(RTSP) - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓ 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/174891
推荐阅读
相关标签
  

闽ICP备14008679号