1. 简介流媒体是利用了流式传输的多媒体利用技术。以下是维基百科关于流媒体概念的界说: 流媒体 (streaming media) 是指将连续串的媒体数据紧缩后,经过收集分段发送数据,在收集上立即传输影音以供观赏的一种技术与进程,此技术使得数据包得以像流水一样发送;假如不利用此技术,就必须在利用前下载全部媒体文件。 1.1 FFmpeg 影音处置的条理FFmpeg 中对影音数据的处置,可以分别为协议层、容器层、编码层与原始数据层四个条理: 协议层:供给收集协议收发功用,可以接收或推送含封装格式的媒体流。协议层由 libavformat 库及第三方库(如 librtmp)供给支持。 容器层:处置各类封装格式。容器层由 libavformat 库供给支持。 编码层:处置音视频编码及解码。编码层由各类丰富的编解码器(libavcodec 库及第三方编解码库(如 libx264))供给支持。 原始数据层:处置未编码的原始音视频帧。原始数据层由各类丰富的音视频滤镜(libavfilter 库)供给支持。 本文说起的收流与推流的功用,属于协议层的处置。 FFmpeg 中 libavformat 库供给了丰富的协议处置及封装格式处置功用,在翻开输入/输出时,FFmpeg 会按照 输入 URL / 输出 URL 探测输入/输出格式,挑选合适的协议和封装格式。例如,假如输出 URL 是 "rtmp://192.168.0.104/live",那末 FFmpeg 翻开输出时,会肯定利用 rtmp 协议,封装格式为 flv。 FFmpeg 中翻开输入/输出的内部处置细节用户不必关注,是以本文流处置的例程和前面转封装的例程很是类似,分歧之处首要在于输入/输出 URL 形式分歧,若 URL 照顾 "rtmp://"、"rpt://"、"udp://"等前缀,则暗示触及流处置;否则,处置的是当地文件。 1.2 流媒系统统中的脚色流媒系统统是一个比力复杂的系统,简单来说触及三个脚色:流媒体办事器、推流客户端和收流客户端。推流客户端是内容生产者,收流客户端是内容消耗者。表示图以下: 1.3 收流与推流假如输入是收集流,输出是当地文件,则实现的是收流功用,将收集流存储为当地文件,以下: 假如输入是当地文件,输出是收集流,则实现的是推流功用,将当地文件推送到收集,以下: 假如输入是收集流,输出也是收集流,则实现的是转流功用,将一个流媒体办事器上的流推送到另一个流媒体办事器,以下: 相关视频保举: LinuxC++音视频开辟视频:免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开辟 【文章福利】:小编整理了一些相关的音视频开辟进修材料(材料包括C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等),qun994289133免费分享,有需要的可以加群支付哦!~点击裙994289133加入支付材料 企鹅群994289133支付材料 企鹅群994289133支付材料 2. 源码源码和转封装例程大部分不异,可以以为是转封装例程的增强版: #include <stdbool.h>#include <libavutil/timestamp.h> #include <libavformat/avformat.h> // ffmpeg -re -i tnhaoxc.flv -c copy -f flv rtmp://192.168.0.104/live // ffmpeg -i rtmp://192.168.0.104/live -c copy tnlinyrx.flv // ./streamer tnhaoxc.flv rtmp://192.168.0.104/live // ./streamer rtmp://192.168.0.104/live tnhaoxc.flv int main(int argc, char **argv) { AVOutputFormat *ofmt = NULL; AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; AVPacket pkt; const char *in_filename, *out_filename; int ret, i; int stream_index = 0; int *stream_mAPPing = NULL; int stream_mapping_size = 0; if (argc < 3) { printf("usage: %s input output\n" "API example program to remux a media file with libavformat and libavcodec.\n" "The output format is guessed according to the file extension.\n" "\n", argv[0]); return 1; } in_filename = argv[1]; out_filename = argv[2]; // 1. 翻开输入 // 1.1 读取文件头,获得封装格式相关信息 if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) { printf("Could not open input file '%s'", in_filename); goto end; } // 1.2 解码一段数据,获得流相关信息 if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { printf("Failed to retrieve input stream information"); goto end; } av_dump_format(ifmt_ctx, 0, in_filename, 0); // 2. 翻开输出 // 2.1 分派输出ctx bool push_stream = false; char *ofmt_name = NULL; if (strstr(out_filename, "rtmp://") != NULL) { push_stream = true; ofmt_name = "flv"; } else if (strstr(out_filename, "udp://") != NULL) { push_stream = true; ofmt_name = "mpegts"; } else { push_stream = false; ofmt_name = NULL; } avformat_alloc_output_context2(&ofmt_ctx, NULL, ofmt_name, out_filename); if (!ofmt_ctx) { printf("Could not create output context\n"); ret = AVERROR_UNKNOWN; goto end; } stream_mapping_size = ifmt_ctx->nb_streams; stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping)); if (!stream_mapping) { ret = AVERROR(ENOMEM); goto end; } ofmt = ofmt_ctx->oformat; AVRational frame_rate; double duration; for (i = 0; i < ifmt_ctx->nb_streams; i++) { AVStream *out_stream; AVStream *in_stream = ifmt_ctx->streams[i]; AVCodecParameters *in_codecpar = in_stream->codecpar; if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO && in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { stream_mapping[i] = -1; continue; } if (push_stream && (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) { frame_rate = av_guess_frame_rate(ifmt_ctx, in_stream, NULL); duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); } stream_mapping[i] = stream_index++; // 2.2 将一个新流(out_stream)增加到输出文件(ofmt_ctx) out_stream = avformat_new_stream(ofmt_ctx, NULL); if (!out_stream) { printf("Failed allocating output stream\n"); ret = AVERROR_UNKNOWN; goto end; } // 2.3 将当前输入流中的参数拷贝到输出流中 ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar); if (ret < 0) { printf("Failed to copy codec parameters\n"); goto end; } out_stream->codecpar->codec_tag = 0; } av_dump_format(ofmt_ctx, 0, out_filename, 1); if (!(ofmt->flags & AVFMT_NOFILE)) { // TODO: 研讨AVFMT_NOFILE标志 // 2.4 建立并初始化一个AVIOContext,用以拜候URL(out_filename)指定的资本 ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); if (ret < 0) { printf("Could not open output file '%s'", out_filename); goto end; } } // 3. 数据处置 // 3.1 写输出文件头 ret = avformat_write_header(ofmt_ctx, NULL); if (ret < 0) { printf("Error occurred when opening output file\n"); goto end; } while (1) { AVStream *in_stream, *out_stream; // 3.2 从输出流读取一个packet ret = av_read_frame(ifmt_ctx, &pkt); if (ret < 0) { break; } in_stream = ifmt_ctx->streams[pkt.stream_index]; if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0) { av_packet_unref(&pkt); continue; } int codec_type = in_stream->codecpar->codec_type; if (push_stream && (codec_type == AVMEDIA_TYPE_VIDEO)) { av_usleep((int64_t)(duration*AV_TIME_BASE)); } pkt.stream_index = stream_mapping[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; /* copy packet */ // 3.3 更新packet中的pts和dts // 关于AVStream.time_base(容器中的time_base)的说明: // 输入:输入流中含有time_base,在avformat_find_stream_info()中可取到每个流中的time_base // 输出:avformat_write_header()会按照输出的封装格式肯定每个流的time_base并写入文件中 // AVPacket.pts和AVPacket.dts的单元是AVStream.time_base,分歧的封装格式AVStream.time_base分歧 // 所以输出文件中,每个packet需要按照输出封装格式重新计较pts和dts av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base); pkt.pos = -1; // 3.4 将packet写入输出 ret = av_interleaved_write_frame(ofmt_ctx, &pkt); if (ret < 0) { printf("Error muxing packet\n"); break; } av_packet_unref(&pkt); } // 3.5 写输出文件尾 av_write_trailer(ofmt_ctx); end: avformat_close_input(&ifmt_ctx); /* close output */ if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) { avio_closep(&ofmt_ctx->pb); } avformat_free_context(ofmt_ctx); av_freep(&stream_mapping); if (ret < 0 && ret != AVERROR_EOF) { printf("Error occurred: %s\n", av_err2str(ret)); return 1; } return 0; } 2.1 收流收流的代码与翻开普通文件的代码没有区分,翻开输入时,FFmpeg 能识别流协议及封装格式,按照响应的协议层代码来接收流,收到流数据去掉协议层后获得的数据和普通文件内容是一样的,后续的处置流程也就一样了。 2.2 推流推流有两个需要留意的地方。 一是需要按照输出流协议显式指定输出 URL 的封装格式: bool push_stream = false;char *ofmt_name = NULL; if (strstr(out_filename, "rtmp://") != NULL) { push_stream = true; ofmt_name = "flv"; } else if (strstr(out_filename, "udp://") != NULL) { push_stream = true; ofmt_name = "mpegts"; } else { push_stream = false; ofmt_name = NULL; } avformat_alloc_output_context2(&ofmt_ctx, NULL, ofmt_name, out_filename); 这里只写了两种。rtmp 推流必须推送 flv 封装格式,udp 推流必须推送 mpegts 封装格式,其他情况就看成是输出普通文件。这里利用 push_stream 变量来标志能否利用推流功用,这个标志前面会用到。 二是要留意推流的速度,不能一股脑将收到的数据全推进来,这样流媒体办事器承受不住。可以按视频播放速度(帧率)来推流。是以每推送一个视频帧,要延时一个视频帧的时长。音频流的数据量很小,可以不必关心此题目。 在翻开输入 URL 时,获得视频帧的延续时长: AVRational frame_rate;double duration; if (push_stream && (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) { frame_rate = av_guess_frame_rate(ifmt_ctx, in_stream, NULL); duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); } 在 av_read_frame() 以后,av_interleaved_write_frame() 之前增加延时,延不时长就是一个视频帧的延续时长: int codec_type = in_stream->codecpar->codec_type;if (push_stream && (codec_type == AVMEDIA_TYPE_VIDEO)) { av_usleep((int64_t)(duration*AV_TIME_BASE)); } 3. 考证3.1 编译第三方库 librtmpFFmpeg 默许并不支持 rtmp 协议。需要先编译安装第三方库 librtmp,然后开启 --enable-librtmp 选项重新编译安装 FFmpeg。 3.2 搭建流媒体办事器测试收流与推流功用需要搭建流媒体办事器。我们选用 nginx-rtmp 作为流媒体办事器用于测试。nginx-rtmp 办事器运转于虚拟机上,推流客户端与收流客户端和 nginx-rtmp 办事器处于同一局域网即可。我的虚拟机是 OPENSUSE LEAP 42.3,IP 是 192.168.0.104(就是 nginx-rtmp 办事器的地址)。 为避免搭建办事器的烦琐进程,我们间接利用 docker 拉取一个 nginx-rtmp 镜像。步调以下: [1] 安装与设置docker办事 安装 docker: sudo zypper install docker将当前用户增加到 docker 组(若 docker 组不存在则先建立),从而可免得 sudo 利用 docker 号令: sudo gpasswd -a ${USER} docker[2] 设置镜像加速 docker 镜像源位于美国,摘取镜像很是缓慢。可设置国内镜像源,加速镜像拉取速度。 点窜 /etc/docker/daemon.json 文件并增加上 registry-mirrors 键值: {"registry-mirrors": [ "https://registry.docker-cn.com", "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com", "https://mirror.ccs.tencentyun.com" ] } 上述设置文件增加了四个国内镜像源:docker 中国、清华、163 和腾讯。 点窜设置文件后重启 docker 办事: systemctl restart docker[3] 拉取 nginx-rtmp 镜像 docker pull tiangolo/nginx-rtmp[4] 翻开容器 docker run -d -p 1935:1935 --name nginx-rtmp tiangolo/nginx-rtmp[5] 防火墙增加破例端口 假如没法推流,应在防火墙中将 1935 端口增加破例 openSUSE 系统:点窜 /etc/sysconfig/SuSEfirewall2 文件,在 FW_SERVICES_EXT_TCP 项中增加 1935 端口,以下: FW_SERVICES_EXT_TCP="ssh 1935"然后重启防火墙: systemctl restart SuSEfirewall2CentOS 8 系统:运转以下号令将 1935 端口增加到防火墙破例端口中: firewall-cmd --permanent --zone=public --add-port=1935/tcp[6] 考证办事器 测试文件下载(右键另存为):tnhaoxc.flv ffmpeg 推流测试: ffmpeg -re -i tnhaoxc.flv -c copy -f flv rtmp://192.168.0.104/live"-re":按视频帧率的速度读取输入 ffplay 收流播放测试: ffplay rtmp://192.168.0.104/liveffplay 播放一般,说明 nginx-rtmp 流媒体办事器搭建成功,可一般利用。 3.3 编译在 shell 中运转以下号令下载例程源码: svn checkout https://github.com/leichn/exercises/trunk/source/ffmpeg/ffmpeg_stream在源码目录履行 ./compile.sh 号令,天生 streamer 可履行文件。 3.4 考证测试文件下载(右键另存为):shifu.mkv,将测试文件保存在和源码同一目录。 推流测试: ./streamer shifu.mkv rtmp://192.168.0.104/live利用 vlc 播放器翻开收集串流,输入流地址 "rtmp://192.168.0.104/live",播放一般。上述测试号令等价于: ffmpeg -re -i shifu.mkv -c copy -f flv rtmp://192.168.0.104/live收流测试:先依照上一步号令启动推流,然后运转以下号令收流 ./streamer rtmp://192.168.0.104/live shifu.ts以上测试号令等价于: ffmpeg -i rtmp://192.168.0.104/live -c copy shifu.ts接收竣事后检查一下天生的当地文件 shifu.ts 能否一般播放。 4. 遗留题目推流的题目:非论是用 ffmpeg 号令,还是用本测试法式,推流竣事时会打印以下信息 [flv @ 0x22ab9c0] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properlyLarger timestamp than 24-bit: 0xffffffc2 [flv @ 0x22ab9c0] Failed to update header with correct duration. [flv @ 0x22ab9c0] Failed to update header with correct filesize. 收流的题目:推流竣事后,收流超时未收以数据,会打印以下信息后法式退出运转 RTMP_ReadPacket, failed to read RTMP packet header |
0.引言本篇文章主要讲解RTSP推流实战,整体推流流程与RTMP推流流程类似。如果对于RTSP
随着视频号直播的不断完善,美颜、抽奖、推流直播等功能纷纷上线,【附近的直播和人】
很多人直播间没有人,就是因为搞不懂抖音直播间推流机制。不明白推流机制就找不到正确
思路:opencv读取视频 — 将视频分割为帧 — 将每一帧进行需求加工后 — 将此帧写入pi
500强直播策划,策划过多起直播。经常会遇到需要全平台推流的情况,但是OBS的原生软件
#头条文章养成计划#根据我400多天创作的亲身体验,文章推荐的时间比较长,而微头条的
#头条创作挑战赛#头条推流的机制是怎样的?在头条写作了31天,小编一直被这个问题困扰
Open Broadcaster Software(简称 OBS)是一款好用的第三方开源程序直播流媒体内容制
写在前面本文将介绍以下内容:什么是推流?将介绍推流常见的协议RTMP,HLS等。怎么用f
头条直播换到西瓜后台啦! 推流还是一样方便快捷! 功能更多更
最近有很多同学问我视频号推流直播怎么做,这种三两句话回答不清楚,今天特意写了详
推流:将直播内容推送至服务器的过程拉流:为服务器已有直播内容,用指定地址进行拉取
我是依伊,一个全职写作的创作人,点击右上角关注,为你分享【新媒体写作变现】和【个
用OBS作为电脑直播推流,是很多人使用的一款开源软件。我们使用OBS时,它默认的是只能
你是否也遇到过辛辛苦苦拍了视频,但结果却还不如跳舞的小姐姐?为什么别人随便拍的视
专业直播操盘手必须掌握的OBS推流直播技能私域直播母東東,业绩增长分分钟Hello,各位
想在直播间中直播游戏,那就需要用到2个东西,分别是推流码和obs软件。那抖音直播推流
缘起: 最近工作比较忙、文章也没怎么更新,不过最近 一段时间过来问我视频号问题的朋
随着近几年互联网技术高速发展,人们对社交形式多样化的需求不断增加,从一开始的文字
直播目前处于一个风口期,很多直播开始跨平台跨地域直播,如何实现异地直播,跨平台直
声明:本站内容由网友分享或转载自互联网公开发布的内容,如有侵权请反馈到邮箱 1415941@qq.com,我们会在3个工作日内删除,加急删除请添加站长微信:15314649589
Copyright @ 2022-2044 杭州共生网络 www.gongshengyun.cn Powered by Discuz!