视频和视频帧:ffmpeg的RTMP推流

写在前面

本文将介绍以下内容:

  • 什么是推流?将介绍推流常见的协议RTMPHLS等。
  • 怎么用ffmpeg做推流,包括cmdcode两种方式。
  • 笔者在开发推流时遇到的一些坑点。

I. 推流简介

笔者最初听到“推流”时,内心想:“这是什么高端玩意儿?”,迫于项目压力,不得不顶着压力调研和开发。经过一段时间的学习、开发和总结,笔者终于明白了推流,到底是个什么高端玩意儿?

什么是推流?

 科普 | 直播中常说的推流拉流究竟是什么东东?中说:

推流,指的是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号传到网络的过程。

用大白话讲,推流就是把本地音视频数据通过网络上传到云端/后台服务器,所谓“采集阶段封包好”,笔者认为是未解码的H264的NALU。

推流的整个阶段如下图,本图同样摘自 科普 | 直播中常说的推流拉流究竟是什么东东?(读者请先忽略“拉流”这个名词,姑且替换为客户端用户在视频门户网站,如腾讯视频、B站等看网络在线的视频,自然也包括看直播。):

上图从“推流端”到“源站”(同上文所说到的“服务器”),再到CDN分发节点,最后到“播放端”,整个过程的视音频数据,都是压缩的数据流。也就是说,对视频数据来说,就是H264码流。解码工作是在播放端进行的。

推流的工作可想而知,最多的应用就是直播;而在大多数视频门户网站,笔者目前也很疑惑,是否存在步骤1,从效率上来说,视频数据直接存放在“源站”,通过CDN根据客户端请求下发,应该就可以了。至于具体做法,有待考证,笔者在这里姑且记一笔吧。

了解了什么是推流之后,下一个问题自然就出

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FFmpeg是一个开源的跨平台音视频处理工具,它提供了丰富的功能和接口,可以用于音视频的编解码、转码、推流等操作。下面是使用FFmpeg进行RTMP推流的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> int main(int argc, char *argv[]) { AVFormatContext *fmt_ctx = NULL; AVOutputFormat *out_fmt = NULL; AVStream *video_stream = NULL; AVCodecContext *codec_ctx = NULL; AVCodec *codec = NULL; AVPacket pkt; int ret; // 初始化FFmpeg库 av_register_all(); // 创建输出上下文 ret = avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", "rtmp://your_rtmp_url"); if (ret < 0) { fprintf(stderr, "Failed to allocate output context: %s\n", av_err2str(ret)); return ret; } out_fmt = fmt_ctx->oformat; // 添加视频流 video_stream = avformat_new_stream(fmt_ctx, NULL); if (!video_stream) { fprintf(stderr, "Failed to create video stream\n"); return -1; } codec_ctx = video_stream->codec; // 设置编码器参数 codec_ctx->codec_id = out_fmt->video_codec; codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; codec_ctx->width = 640; codec_ctx->height = 480; codec_ctx->bit_rate = 400000; codec_ctx->gop_size = 10; codec_ctx->time_base = (AVRational){1, 25}; // 查找编码器 codec = avcodec_find_encoder(codec_ctx->codec_id); if (!codec) { fprintf(stderr, "Codec not found\n"); return -1; } // 打开编码器 ret = avcodec_open2(codec_ctx, codec, NULL); if (ret < 0) { fprintf(stderr, "Failed to open codec: %s\n", av_err2str(ret)); return ret; } // 打开输出URL ret = avio_open(&fmt_ctx->pb, fmt_ctx->url, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, "Failed to open output URL: %s\n", av_err2str(ret)); return ret; } // 写入文件头 ret = avformat_write_header(fmt_ctx, NULL); if (ret < 0) { fprintf(stderr, "Failed to write header: %s\n", av_err2str(ret)); return ret; } // 初始化视频 AVFrame *frame = av_frame_alloc(); frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; // 分配视频缓冲区 ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "Failed to allocate frame buffer: %s\n", av_err2str(ret)); return ret; } // 编码并推流 int frame_count = 0; while (frame_count < 100) { // 生成测试图像 for (int y = 0; y < codec_ctx->height; y++) { for (int x = 0; x < codec_ctx->width; x++) { frame->data[0][y * frame->linesize[0] + x] = x + y + frame_count * 3; } } // 设置时间戳 frame->pts = frame_count; // 发送视频 ret = avcodec_send_frame(codec_ctx, frame); if (ret < 0) { fprintf(stderr, "Failed to send frame: %s\n", av_err2str(ret)); return ret; } // 接收编码后的数据包 while (ret >= 0) { ret = avcodec_receive_packet(codec_ctx, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { fprintf(stderr, "Failed to receive packet: %s\n", av_err2str(ret)); return ret; } // 设置流索引和时间戳 pkt.stream_index = video_stream->index; av_packet_rescale_ts(&pkt, codec_ctx->time_base, video_stream->time_base); pkt.pos = -1; // 写入数据包 ret = av_interleaved_write_frame(fmt_ctx, &pkt); if (ret < 0) { fprintf(stderr, "Failed to write packet: %s\n", av_err2str(ret)); return ret; } av_packet_unref(&pkt); } frame_count++; } // 写入文件尾 ret = av_write_trailer(fmt_ctx); if (ret < 0) { fprintf(stderr, "Failed to write trailer: %s\n", av_err2str(ret)); return ret; } // 释放资源 av_frame_free(&frame); avcodec_close(codec_ctx); avio_close(fmt_ctx->pb); avformat_free_context(fmt_ctx); return 0; } ``` 上述代码使用FFmpeg库进行RTMP推流,首先创建输出上下文,然后添加视频流并设置编码器参数。接下来,打开编码器和输出URL,并写入文件头。然后,初始化视频并分配缓冲区。在循环中,生成测试图像并发送视频,然后接收编码后的数据包并写入。最后,写入文件尾并释放资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值