iOS平台基于ffmpeg的视频直播技术揭秘
现在非常流行直播,相信很多人都跟我一样十分好奇这个技术是如何实现的,正好最近在做一个ffmpeg的项目,发现这个工具很容易就可以做直播,下面来给大家分享下技术要点:
首先你得编译出ffmpeg运行所需的静态库,这个百度一下有很多内容,这里我就不多说了,建议可以用Github上的一个开源脚本来编译,简单粗暴有效率。
地址:GitHub - kewlbear/FFmpeg-iOS-build-script: Shell scripts to build FFmpeg for iOS and tvOS
下载后直接用终端运行build-ffmpeg.sh脚本就行了,大概半个小时就全部编译好了…反正我觉得速度还行吧(PS:当初编译Android源码那叫一个慢啊…),若是报错就再来一遍,直到提示成功。
视频直播怎么直播呢?大概流程图如下:
1.直播人设备端:从摄像头获取视频流,然后使用rtmp服务提交到服务器
2.服务器端:接收直播人提交的rtmp视频流,并为观看者提供rtmp源
3.观看者:用播放器播放rtmp源的视频.
PS:RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。
前期准备:
新建一个项目,将所有需要引入的ffmpeg的静态库及其他相关库引入到工程中,配置头文件搜索路径,这一步网上有很多教程就不重复叙述了。
我是用上面脚本编译的最新版,为了后期使用,需要将这些C文件添加到项目:
cmdutils_common_opts.h cmdutils.h及cmdutils.c config.h在scratch目录下取个对应平台的 ffmpeg_filter.c ffmpeg_opt.c ffmpeg_videotoolbox.c ffmpeg.h及ffmpeg.c
除了config.h文件外,别的文件均在ffmpeg-3.0源码目录中
注意问题:
1.编译会报错,因为ffmpeg.c文件中包含main函数,请将该函数重命名为ffmpeg_main并在ffmpeg.h中添加ffmpeg_main函数的声明.
2.ffmpeg任务完成后会结束进程,而iOS设备都是单进程多线程任务,所以需要将cmdutils.c文件中的exit_program方法中的
exit(ret);
改为结束线程,需要引入#include
pthread_exit(NULL);
直播端:用ffmpeg库抓取直播人设备的摄像头信息,生成裸数据流stream,注意!!!这里是裸流,裸流意味着什么呢?就是不包含PTS(Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来)、DTS(Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码)等信息的数据流,播放器拿到这种流是无法进行播放的.将这个客户端只需要将这个数据流以RTMP协议传到服务器即可。
如何获取摄像头信息:
使用libavdevice库可以打开获取摄像头的输入流,在ffmpeg中获取摄像头的输入流跟打开文件输入流很类似,示例代码:
//打开一个文件: AVFormatContext*pFormatCtx =avformat_alloc_context(); avformat_open_input(&pFormatCtx,"test.h264",NULL,NULL); //获取摄像头输入: AVFormatContext*pFormatCtx =avformat_alloc_context(); //多了查找输入设备的这一步 AVInputFormat*ifmt=av_find_input_format("vfwcap"); //选取vfwcap类型的第一个输入设别作为输入流 avformat_open_input(&pFormatCtx,, ifmt,NULL);
如何使用RTMP上传视频流:
使用RTMP上传文件的指令是:
使用ffmpeg.c中的ffmpeg_main方法直接运行该指令即可,示例代码:
NSString*command =@"ffmpeg -re -i temp.h264 -vcodec copy -f flv rtmp://xxx/xxx/livestream"; //根据空格将指令分割为指令数组 NSArray*argv_array=[command_strcomponentsSeparatedByString:(@" ")]; //将OC对象转换为对应的C对象 intargc=(int)argv_array.count; char** argv=(char**)malloc(sizeof(char*)*argc); for(inti=;i { argv[i]=(char*)malloc(sizeof(char)*); strcpy(argv[i],[[argv_arrayobjectAtIndex:i]UTF8String]); } //传入指令数及指令数组 ffmpeg_main(argc,argv); //线程已杀死,下方的代码不会执行 ffmpeg -re -itemp.h264 -vcodec copy -f flvrtmp://xxx/xxx/livestream
这行代码就是
-re参数是按照帧率发送,否则ffmpeg会按最高速率发送,那么视频会忽快忽慢,
-itemp.h264是需要上传的裸h264流
-vcoder copy 这段是复制一份不改变源
-f flvrtmp://xxx/xxx/livestream是指定格式为flv发送到这个url
这里看到输入是裸流或者是文件,但是我们从摄像头获取到的是直接内存流,这怎么解决呢?
当然是有办法的啦
1.将这串参数中temp.h264参数变为null
2.初始化自定义的AVIOContext,指定自定义的回调函数。示例代码如下:
NSString*command =@"ffmpeg -re -i temp.h264 -vcodec copy -f flv rtmp://xxx/xxx/livestream"; //根据空格将指令分割为指令数组 NSArray*argv_array=[command_strcomponentsSeparatedByString:(@" ")]; //将OC对象转换为对应的C对象 intargc=(int)argv_array.count; char** argv=(char**)malloc(sizeof(char*)*argc); for(inti=;i { argv[i]=(char*)malloc(sizeof(char)*); strcpy(argv[i],[[argv_arrayobjectAtIndex:i]UTF8String]); } //传入指令数及指令数组 ffmpeg_main(argc,argv); //线程已杀死,下方的代码不会执行 ffmpeg -re -itemp.h264 -vcodec copy -f flvrtmp://xxx/xxx/livestream
3. 自己写回调函数,从输入源中取数据。示例代码如下:
//Callback intread_buffer(void*opaque, uint8_t *buf,intbuf_size){ //休眠,否则会一次性全部发送完 if(pkt.stream_index==videoindex){ AVRational time_base=ifmt_ctx->streams[videoindex]->time_base; AVRational time_base_q={,AV_TIME_BASE}; int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q); int64_t now_time = av_gettime() - start_time; if(pts_time > now_time) av_usleep(pts_time - now_time); } //fp_open替换为摄像头输入流 if(!feof(fp_open)){ inttrue_size=fread(buf,,buf_size,fp_open); returntrue_size; }else{ return-; } }
服务端:原谅我一个移动开发不懂服务器端,大概应该是获取直播端上传的视频流再进行广播.所以就略过吧.
播放端:播放端实际上就是一个播放器,可以有很多解决方案,这里提供一种最简单的,因为很多直播软件播放端和客户端都是同一个软件,所以这里直接使用项目中已经有的ffmpeg进行播放简单粗暴又省事.
在Github上有个基于ffmpeg的第三方播放器kxmovie,直接用这个就好.
地址:GitHub - kolyvan/kxmovie: movie player for iOS using ffmpeg
当你把kxmovie的播放器部分添加到之前做好的上传部分,你会发现报错了......
查找的结果是kxmovie所使用的avpicture_deinterlace方法不存在,我第一个想法就是想办法屏蔽到这个方法,让程序能正常使用,结果......当然不能正常播放视频了,一百度才发现这个方法居然是去交错,虽然我视频只是不够丰富,但是也知道这个方法肯定是不能少的.
没事,只有改源码了.从ffmpeg官方源码库中可以找到这个方法.
地址:ffmpeg.org/doxygen/1.0/imgconvert_8c-source.html#l00940
发现这个方法在之前的实现中是在avcodec.h中声明是AVPicture的方法,然后在avpicture.c中再调用libavcodec/imgconvert.c这个文件中,也就是说这个方法本身就是属于imgconvert.c的,avpicture.c只是间接调用,查找ffmpeg3.0的imgconvert.c文件,居然没这个方法,但是官方代码库中是有这个方法的,难道是已经移除了?移除不移除关我毛事,我只想能用,所以简单点直接改avpicture.c
首先添加这几个宏定义
#define deinterlace_line_inplace deinterlace_line_inplace_c #define deinterlace_line deinterlace_line_c #define ff_cropTbl ((uint8_t *)NULL)
然后从网页上复制这几个方法到avpicture.c文件中
static void deinterlace_line_c static void deinterlace_line_inplace_c static void deinterlace_bottom_field static void deinterlace_bottom_field_inplace int avpicture_deinterlace
再在avcodec.h头文件中,avpicture_alloc方法下面添加声明:
attribute_deprecated intavpicture_deinterlace(AVPicture*dst,constAVPicture*src, enumAVPixelFormatpix_fmt,intwidth,intheight);
保存后再用终端执行build-ffmpeg.sh脚本编译一次就行了…再次导入项目中kxmovie就不会报错了,播放视频的代码如下:
KxMovieViewController*vc = [KxMovieViewControllermovieViewControllerWithContentPath:pathparameters:nil]; [selfpresentViewController:vcanimated:YEScompletion:nil];
注:其中path可以是以http/rtmp/trsp开始的url
iOS平台基于ffmpeg的视频直播技术揭秘的更多相关文章
- 实战FFmpeg--iOS平台使用FFmpeg将视频文件转换为YUV文件
做播放器的开发这里面涉及的东西太多,我只能一步步往前走,慢慢深入.播放器播放视频采用的是渲染yuv文件.首先,要知道yuv文件是怎么转换得来的,其次,要知道怎么把视频文件保存为yuv文件.雷神的文章1 ...
- 视频直播技术之iOS端推流
随着网络基础建设的发展和资费的下降,在这个内容消费升级的时代,文字.图片无法满足人们对视觉的需求,因此视频直播应运而生.承载了实时性Real-Time和交互性的直播云服务是直播覆盖各行各业的新动力.网 ...
- 视频直播技术-视频-编码-传输-秒开等<转>
转载地址:http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653547042&idx=1&sn=26d8728548 ...
- 「视频直播技术详解」系列之七:直播云 SDK 性能测试模型
关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...
- 多媒体开发(7):编译Android与iOS平台的FFmpeg
编译FFmpeg,一个古老的话题,但小程还是介绍一遍,就当记录.之前介绍怎么给视频添加水印时,就已经提到FFmpeg的编译,并且在编译时指定了滤镜的功能. 但是,在手机盛行的时代,读者可能更需要的是能 ...
- 在iOS平台使用ffmpeg解码h264视频流(转)
在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,ffmpeg提供avformat_open_input接口,直接将文件路径或UR ...
- 在iOS平台使用ffmpeg解码h264视频流
来源:http://www.aichengxu.com/view/37145 在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,f ...
- 基于live555的视频直播 DM368IPNC RTSP分析
因需要,从个人的理解顺序和需求角度对live555的分析与开发整理,包含RTSP Server与RTSP Client.如何直播H.264流与JPEG流等,均进行了探讨,对live555的初学者有一定 ...
- iOS:基于RTMP的视频推流
iOS基于RTMP的视频推流 一.基本介绍 iOS直播一出世,立马火热的不行,各种直播平台如雨后春笋,正因为如此,也同样带动了直播的技术快速发展,在IT界精通直播技术的猴子可是很值钱的.直播技术涉及的 ...
随机推荐
- Node 之 Express 学习笔记 第一篇 安装
最近由于工作不忙,正好闲暇时间学学基于 node 的 web开发框架. 现在关于web开发框架除了Express 还有新出的 KOA以及其它一些. 但是想想还是先从 Express 入手吧.因为比较成 ...
- [jQuery编程挑战]007 切换数据表格的行列
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8&quo ...
- asp.net mvc 强类型视图中传入List 数据到控制器
问题来源: 在和一位技术老师聊天时,老师问我一个mvc 表单提交的问题,问道:怎样在表单提交的时候,将 带有 List 属性的对象传入控制器? 这时,我有点呆了,以前一直都好像是 单一属性的表单提交, ...
- MAC——laravel环境
apache: 目录:etc/apache2 重启:sudo apachectl restart PHP: 把/etc/php.ini.default复制并重命名/ect/php.ini 打开Load ...
- 如何重载ComboBox 使其下拉按钮(带下箭头的)和下拉列表的垂直滚动条的宽度改变?(自绘ComboBox) [转]
原文地址:http://bbs.csdn.net/topics/390135022 http://blog.csdn.net/scsdn/article/details/4363299 想使用winf ...
- Webpack 傻瓜式指南(一)
modules with dependencies webpack module bundler static assetss .js .js .png Webpack傻瓜式指南 n ...
- html里的table如何在表格内部保留表格横线的同时去掉表格里的竖线
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ♫【JS模式】偏函数
<深入浅出Node.js> var toString = Object.prototype.toString var isType = function(type) { return fu ...
- 总结linux无线命令wpa
关于wpa_supplicant: 用到wpa_cli命令 wpa_cli -iwlan0 add_network // wlan0 是无线网络设备的名字,增加一个网络,会返回 ...
- (转载)php curl_init函数用法
(转载)http://blog.sina.com.cn/s/blog_640738130100tsig.html 使用PHP的cURL库可以简单和有效地去抓网页.你只需要运行一个脚本,然后分析一下你所 ...