音视频流媒体技术:FFmpeg入门 - 视频播放上一篇博客先容了怎样用ffmpeg去播放视频. 里面用于翻开视频流的avformat_open_input函数除了翻开当地视频之外,现实上也能翻开rtmp协议的远程视频,实现拉流: ./demo -p 当地视频途径./demo -p rtmp://办事器ip/视频流途径 这篇文章我们来说下怎样实现推流,然后和之前的demo代码配合就能完成推流、拉流的全部进程,实现直播。 rtmp办事器全部直播的功用分红下面三个模块: 从上图我们可以看到rtmp是需要办事器做转发的,我们选用开源的srs.间接从github上把它的源码拉下来编译,然后间接启动即可: git clone git@github.com:ossrs/srs.gitcd srs/trunk ./configure make ./etc/init.d/srs start 假如是当地的电脑,这个时辰就能在局域网内间接用它的内网ip去拜候了.但假如是腾讯云、阿里云之类的云办事器还需要设置平安组开放下面几个端口的拜候权限: listen 1935;max_connections 1000; #srs_log_tank file; #srs_log_file ./objs/srs.log; daemon on; http_api { enabled on; listen 1985; } http_server { enabled on; listen 8080; dir ./objs/nginx/html; } rtc_server { enabled on; listen 8000; # UDP port # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate candidate $CANDIDATE; } ... 固然假如这几个端口已经被占用的话可以点窜设置文件conf/srs.conf去点窜 预备输出流我们挑选推送当地的视频到rtmp办事器,所以第一步照旧是翻开当地视频流: bool VideoSender::Send(const string& srcUrl, const string& destUrl) {... // 翻开文件流读取文件头剖析出视频信息如轨道信息、时长等 // mFormatContext初始化为NULL,假如翻开成功,它会被设备成非NULL的值 // 这个方式现实可以翻开多种来历的数据,url可所以当地途径、rtmp地址等 // 在不需要的时辰经过avformat_close_input封闭文件流 if(avformat_open_input(&inputFormatContext, srcUrl.c_str(), NULL, NULL) < 0) { cout << "open " << srcUrl << " failed" << endl; break; } // 对于没有文件头的格式如MPEG大概H264裸流等,可以经过这个函数剖析前几帧获得视频的信息 if(avformat_find_stream_info(inputFormatContext, NULL) < 0) { cout << "can't find stream info in " << srcUrl << endl; break; } // 打印输入视频信息 av_dump_format(inputFormatContext, 0, srcUrl.c_str(), 0); ... } 【相关进修材料保举,点击下方链接免费报名,先码住不迷路~】 音视频免费进修地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开辟 【纯干货免费分享】C++音视频进修材料包、大厂口试题、技术视频和进修线路图,材料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费支付哦~ 当地视频翻开以后,我们建立输出视频流高低文,然后为输出流建立轨道,最初翻开输出视频流: // 建立输出流高低文,outputFormatContext初始化为NULL,假如翻开成功,它会被设备成非NULL的值,在不需要的时辰利用avformat_free_context开释// 输出流利用flv格式 if(avformat_alloc_output_context2(&outputFormatContext, NULL, "flv", destUrl.c_str()) < 0) { cout << "can't alloc output context for " << destUrl << endl; break; } // 拷贝编解码参数 if(!createOutputStreams(inputFormatContext, outputFormatContext)) { break; } // 打印输出视频信息 av_dump_format(outputFormatContext, 0, destUrl.c_str(), 1); // 翻开输出流,竣事的时辰利用avio_close封闭 if(avio_open(&outputFormatContext->pb, destUrl.c_str(), AVIO_FLAG_WRITE) < 0) { cout << "can't open avio " << destUrl << endl; break; } 这里有个createOutputStreams用于按照当地视频文件的轨道信息,为输出流建立一样的轨道: static bool createOutputStreams(AVFormatContext* inputFormatContext, AVFormatContext* outputFormatContext) {// 遍历输入流的一切轨道,拷贝编解码参数到输出流 for(int i = 0 ; i < inputFormatContext->nb_streams ; i++) { // 为输出流建立轨道 AVStream* stream = avformat_new_stream(outputFormatContext, NULL); if(NULL == stream) { cout << "can't create stream, index " << i << endl; return false; } // 编解码参数在AVCodecParameters中保存,从输入流拷贝到输出流 if(avcodec_parameters_copy(stream->codecpar, inputFormatContext->streams[i]->codecpar) < 0) { cout << "can't copy codec paramters, stream index " << i << endl; return false; } // codec_tag代表了音视频数据采用的码流格式,分歧的封装格式如flv、mp4等的支持情况是纷歧样的 // 上面的avcodec_parameters_copy将输出流的codec_tag从输入拷贝过来酿成了一样的 // 由于我们输出流在avformat_alloc_output_context2的时辰写死了flv格式 // 假如输入流不是flv而是mp4等格式的话便能够会出现mp4里某种codec_tag在flv不支持致使推流失利的情况 // 这里我们可以用av_codec_get_id从输出流的oformat的支持的codec_tag列内里面查找codec_id // 假如和codecpar的codec_id纷歧致的话代表不支持 if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != stream->codecpar->codec_id) { // 这里将codec_tag设备为0,FFmpeg会按照编码codec_id从封装格式的codec_tag列表中找到一个codec_tag stream->codecpar->codec_tag = 0; } } return true; } codec_id和codec_tag { AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* uncompressed RGB */ { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* uncompressed YUV422 */ { AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* uncompressed 8-bit 4:2:2 */ { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2VUY but byte-swAPPed */ 又例如codec_id都是AV_CODEC_ID_H264,但现实上也有很多细分范例: // libavformat/isom.c{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '2') }, { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') }, { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '4') }, { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'p') }, /* AVC-Intra 50M 720p24/30/60 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'q') }, /* AVC-Intra 50M 720p25/50 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '2') }, /* AVC-Intra 50M 1080p25/50 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '3') }, /* AVC-Intra 50M 1080p24/30/60 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '5') }, /* AVC-Intra 50M 1080i50 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '6') }, /* AVC-Intra 50M 1080i60 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'p') }, /* AVC-Intra 100M 720p24/30/60 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'q') }, /* AVC-Intra 100M 720p25/50 */ { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '2') }, /* AVC-Intra 100M 1080p25/50 */ 可以看出来codec_tag是经过4个字母去暗示的,我们来看看MKTAG的界说: #define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))终极它获得的是一个整数,例如MKTAG('a', 'v', 'c', '1')获得的值是0x31637661
我们可以用av_fourcc2str这个函数将终极的整数转换回字符串 大部分情况下假如codec_tag在输出流不支持的情况下av_codec_get_id拿到的是AV_CODEC_ID_NONE,所以大部分情况可以等价于: if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != AV_CODEC_ID_NONE)不外也存在都是MKTAG('l', 'p', 'c', 'm'),但codec_id能够是AV_CODEC_ID_PCM_S16BE大概AV_CODEC_ID_PCM_S16LE的情况: { AV_CODEC_ID_PCM_S16BE, MKTAG('l', 'p', 'c', 'm') },{ AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, { AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, // [flv @ 0x14f808e00] Failed to update header with correct duration. // [flv @ 0x14f808e00] Failed to update header with correct filesize AVDictionary * opts = NULL; av_dict_set(&opts, "flvflags", "no_duration_filesize", 0); if(avformat_write_header(outputFormatContext, opts ? &opts : NULL) < 0) { cout << "write header to " << destUrl << " failed" << endl; break; } // 建立建立AVPacket接收数据包 // 不管是紧缩的音频流还是紧缩的视频流,都是由一个个数据包组成的 // 解码的进程现实就是从文件流中读取一个个数据包传给解码器去解码 // 对于视频,它凡是应包括一个紧缩帧 // 对于音频,它能够是一段紧缩音频、包括多个紧缩帧 // 在不需要的时辰可以经过av_packet_free开释 packet = av_packet_alloc(); if(NULL == packet) { cout << "can't alloc packet" << endl; break; } ... // 从文件流里面读取出数据包,这里的数据包是编解码层的紧缩数据 while(av_read_frame(inputFormatContext, packet) >= 0) { // 我们以视频轨道为基准去同步时候 // 假如时候还没有到就增加提早,避免向办事器推流速度过快 ... // 往输出流写入数据 av_interleaved_write_frame(outputFormatContext, packet); // 写入成以后紧缩数据包的数据就不需要了,将它开释 av_packet_unref(packet); } // 写入视频尾部信息 av_write_trailer(outputFormatContext); 帧同步由于av_read_frame这里读取出来的是未解码的紧缩数据速度很快,假如不做控制一会儿就发送完成了,会形成数据聚积在办事器上。这里我们疏忽收集传输耗时,仍然经过视频包的pts做一定的同步: while(av_read_frame(inputFormatContext, packet) >= 0) {// 我们以视频轨道为基准去同步时候 // 假如时候还没有到就增加提早,避免向办事器推流速度过快 if(videoStreamIndex == packet->stream_index) { if(AV_NOPTS_VALUE == packet->pts) { // 有些视频流不带pts数据,按30fps将间隔同一成32ms av_usleep(32000); } else { // 带pts数据的视频流,我们计较出每一帧应当在什么时辰播放 int64_t nowTime = av_gettime() - startTime; int64_t pts = packet->pts * 1000 * 1000 * timeBaseFloat; if(pts > nowTime) { av_usleep(pts - nowTime); } } } // 往输出流写入数据 av_interleaved_write_frame(outputFormatContext, packet); // 写入成以后紧缩数据包的数据就不需要了,将它开释 av_packet_unref(packet); } 资本开释等视频流读写完成以后就是最初的资本开释扫尾工作了: if(NULL != packet) {av_packet_free(&packet); } if(NULL != outputFormatContext) { if(NULL != outputFormatContext->pb) { avio_close(outputFormatContext->pb); } avformat_free_context(outputFormatContext); } if(NULL != inputFormatContext) { avformat_close_input(&inputFormatContext); } 其他 |
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!