环境部署2015年开始直播变得越来越流行,很多的直播平台也应运而生,直播是一个很有技术的项目,从服务端到客户端到web等等。我们将写一序列的博客来阐述直播中的技术,这包括服务端技术和客户端技术。包括最简单的服务端环境部署、客户端编译、采集、推流、拉流、美化特效、水印、延时优化、音视频同步、p2p等等。当然还可能包括一些信号处理的知识,比如滤波,傅里叶变换(FFT)。从本文开始我们将从环境部署开始,这包括两方面的环境部署即服务端和客户端。 1、服务端在部署服务端环境其实包含很多东西的,最常用的web服务nginx,数据库Mysql、Nosql,api开发最多的三种选择:
考虑使用缓存技术,则主要包含redis和memcached。如果还要其他的日志统计(kafka什么的)需求则还需要更多的环境,我们这里不讨论,只是简单叙述 对于直播而言,我们需要部署两个东西,nginx(含nginx-rtmp-module)、ffmpeg,这两个是直播服务端的关键,下面我们简单讲述如何安装nginx(含nginx-rtmp-module)和ffmpeg以及如何配置nginx.conf。 首选我们来安装nginx和ffmpeg。 nginx下载地址:
ffmpeg下载地址:
nginx-rtmp-module下载地址: https://github.com/arut/nginx-rtmp-module 其中nginx-rtmp-module是Google工程师开发的,在gitHub上有很多分支,根据自己的需求选择分支,我们这里选择master分支。 首选安装nginx和nginx-rtmp-module,在安装nginx的时候,会需要openssl、pcre、zlib这几个库。cd 进入nginx解压目录 ./configure --prefix=/usr/local/nginx --with-pcre=/path/to/your/pcre/ --with-zlib=/path/to/your/zlib/ --with-openssl=/path/to/your/openssl/ --add-module=/path/to/your/nginx-rtmp-module 其中–prefix是指安装后nginx的目录,–with-pcre需要pcre库,/path/to/your/pcre/是指的pcre源代码路径,其他的同理。–add-module=/path/to/your/nginx-rtmp-module 这个是添加nginx-rtmp-module,将nginx-rtmp-module嵌入到nginx中,这样是nginx的强大之处-插件功能。 安装完成之后,在浏览器中输入localhost:8080则会出现如下画面: ![]() 那么接下安装ffmpeg,解压ffmpeg并cd进去目录,执以下语句 ./configure --enable-shared --prefix=/usr/local/ffmpeg 网上有很多安装ffmpeg的教程,这里不详细介绍。 如果安装成功后会,在终端中输入 ffmpeg -version会显示相关的信息。如果没有则可能没有安装成功。 安装完成之后我们来看看nginx.conf的配置信息。 worker_processes 2; error_log logs/error.log; error_log logs/error.log notice; error_log logs/error.log info; pid logs/nginx.pid; events { worker_connections 128; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; keepalive_timeout 65; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 300m; tcp_nopush on; tcp_nodelay on; server_tokens off; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; include vhosts/*.conf; } #切换自动推送(多 worker 直播流)模式。默认为 off #rtmp_auto_push on; #当 worker 被干掉时设置自动推送连接超时时间。默认为 100 毫秒 #rtmp_auto_push_reconnect 1s; #设置用于流推送的 UNIX 域套接字目录。默认为 /tmp #rtmp_socket_dir /var/sock; rtmp { server { listen 1935; #点播配置 #application vod { # play /opt/media/nginxrtmp/flv; #} #直播流配置 application live { live on; #allow publish 127.0.0.1; #deny publish all; #allow play all; } #access_log logs/rtmp_access.log new; access_log logs/rtmp_access.log; access_log on; #HLS协议支持 #application hls { #live on; #hls on; #hls_path /tmp/app; #hls_fragment 5s; #} #application hls{ # live on; # hls on; # hls_path /usr/local/Cellar/nginx-full/1.10.1/html/app; # hls_fragment 1s; #} } } 其中http相关的标签我这边不做详细的介绍,这个等一会用到api的时候详细介绍。下面我们看看rtmp标签,rtmp标签的意思是声明一个 RTMP 实例。在rtmp标签下面有server,它的意思是给 NGINX 添加一个监听端口以接收 RTMP 连接 application标签是创建一个 RTMP 应用。application 名的模式并不类似于 http location。这个以后再详细阐述。在这个配置中,我们只是配置了一个live,同时打开了log,其他的相关的参数我们现在不说明,等以后会专门介绍rtmp的配置文章介绍。 配置好了这个之后就是通过ffmpeg向server推流。ffmpeg的指令为: ffmpeg -re -i /Users/jarlene/Downloads/test.flv -c copy -f flv rtmp://localhost:1935/live/steam 这里不详细介绍ffmpeg指令,因为接下来我们会有一篇专门的文章接受ffmpeg。 这个时候就可以用vlc播放器拉流看直播了。 ![]() 到这里我们就简单讲述了nginx和ffmpeg安装和nginx.conf配置信息以及推流和观看 2、客户端相对于服务端环境部署来说客户端环境部署复杂很多,尤其是在android平台,编译导入android studio等等过程都很复杂,ios平台还好,但是由于我个人喜爱,在此也会加上Windows客户端相配置,只不过windows会一段时间后更新。对于客户端主要就是编译ffmpeg。下面这段脚本是编译ffmpeg到android和ios平台的。 #/bin/bash AndroidDest=`pwd`/FFmpeg-Android && rm -rf $AndroidDest IOSDest=`pwd`/FFmpeg-iOS && rm -rf $IOSDest SOURCE="ffmpeg-3.1.1" THIN="$IOSDest/thin" SCRATCH="$IOSDest/scratch" echo $THIN echo $SCRATCH # if [ -d ffmpeg ]; then # cd ffmpeg # else # git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg # cd ffmpeg # fi if [ ! -r $SOURCE ] then echo 'FFmpeg source not found. Trying to download...' curl http://www.ffmpeg.org/releases/$SOURCE.tar.bz2 | tar xj \ || exit 1 fi function android() { cd $SOURCE # sed -i -e 's|SLIBNAME_WITH_MAJOR='\$(SLIBNAME).\$(LIBMAJOR)'|SLIBNAME_WITH_MAJOR='\$(SLIBPREF)\$(FULLNAME)-\$(LIBMAJOR)\$(SLIBSUF)'|' configure # sed -i -e 's|LIB_INSTALL_EXTRA_CMD='\$\$(RANLIB)"\$(LIBDIR)/\$(LIBNAME)"'|LIB_INSTALL_EXTRA_CMD='\$\$(RANLIB)"\$(LIBDIR)/\$(LIBNAME)"'|' configure # sed -i -e 's|SLIB_INSTALL_LINKS='\$(SLIBNAME_WITH_MAJOR)\$(SLIBNAME)'|SLIB_INSTALL_LINKS='\$(SLIBNAME)'|' configure NDK=/Users/jarlene/Library/Android/sdk/ndk-bundle SYSROOT=$NDK/platforms/android-19/arch-arm WORKING_DIR=`pwd` # Expand the prebuilt/* path into the correct one TOOLCHAIN=`echo $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64` export PATH=$TOOLCHAIN/bin:$PATH # Don't build any neon version for now for version in armv5te armv7a; do DEST=$AndroidDest FLAGS="--target-os=linux --cross-prefix=arm-linux-androideabi- --arch=arm" FLAGS="$FLAGS --sysroot=$SYSROOT" #FLAGS="$FLAGS --soname-prefix=/data/data/net.sourceforge.servestream/lib/" FLAGS="$FLAGS --enable-shared --disable-symver" # FLAGS="$FLAGS --enable-small --optimization-flags=-O2" #FLAGS="$FLAGS --extra-cflags=-I$NDK/platforms/android-23/arch-arm/usr/include/ffmpeg --extra-ldflags=-L$NDK/platforms/android-23/arch-arm/usr/lib" FLAGS="$FLAGS --disable-doc" # FLAGS="$FLAGS --enable-gpl" FLAGS="$FLAGS --disable-ffmpeg" FLAGS="$FLAGS --disable-ffplay" FLAGS="$FLAGS --disable-ffprobe" FLAGS="$FLAGS --disable-ffserver" # FLAGS="$FLAGS --enable-avdevice" # FLAGS="$FLAGS --enable-swresample" # FLAGS="$FLAGS --enable-swscale" FLAGS="$FLAGS --enable-postproc" # FLAGS="$FLAGS --enable-avfilter" # FLAGS="$FLAGS --disable-everything" # FLAGS="$FLAGS --enable-muxer=mov --enable-muxer=ipod --enable-muxer=psp --enable-muxer=mp4 --enable-muxer=avi " # FLAGS="$FLAGS --enable-demuxer=acc,flac,h263,h264,m4v,matroska,mp3,mpegvideo,ogg,pcm_alaw,pcm_f32be,pcm_f32le,pcm_f64be,pcm_f64le,pcm_mulaw,pcm_s16be,pcm_s16le,pcm_s24be" # FLAGS="$FLAGS --enable-demuxer=pcm_s24le,pcm_s32be,pcm_s32le,pcm_s8,pcm_u16be,pcm_u16le,pcm_u24be,pcm_u24le,pcm_u32be,pcm_u32le,pcm_u8,rtp,rtsp,sdp,wav" # FLAGS="$FLAGS --enable-parser=aac,aac_latm,flac,h263,h264,mpeg4video,mpegaudio,mpegvideo,vorbis,vp8" # FLAGS="$FLAGS --enable-decoder=aac,aac_latm,mp3,wmalossless,wmapro,wmav1,wmav2,wmavoice,mpeg4,h264" # FLAGS="$FLAGS --enable-protocol=http,https,mmsh,mmst,rtmp,hls,file,rtsp" FLAGS="$FLAGS --disable-debug" case "$version" in neon) EXTRA_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon" EXTRA_LDFLAGS="-Wl,--fix-cortex-a8" # Runtime choosing neon vs non-neon requires # renamed files ABI="armeabi-v7a" ;; armv7a) EXTRA_CFLAGS="-march=armv7-a -mfloat-abi=softfp" EXTRA_LDFLAGS="" ABI="armeabi-v7a" ;; *) EXTRA_CFLAGS="" EXTRA_LDFLAGS="" ABI="armeabi" ;; esac DEST="$DEST/$ABI" FLAGS="$FLAGS --prefix=$DEST" mkdir -p $DEST echo $FLAGS --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" > $DEST/info.txt ./configure $FLAGS --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" | tee $DEST/configuration.txt [ $PIPESTATUS == 0 ] || exit 1 make clean make -j4 || exit 1 make install || exit 1 done cd .. } function ios() { mkdir -p $IOSDest CONFIGURE_FLAGS="--enable-cross-compile --disable-debug --disable-programs \ --disable-doc --enable-pic" if [ "$X264" ] then CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-gpl --enable-libx264" fi if [ "$FDK_AAC" ] then CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-libfdk-aac" fi # avresample #CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-avresample" # arm64 armv7 x86_64 i386 ARCHS="arm64 armv7 x86_64" COMPILE="y" LIPO="y" DEPLOYMENT_TARGET="6.0" if [ "$*" ] then if [ "$*" = "lipo" ] then # skip compile COMPILE= else ARCHS="$*" if [ $# -eq 1 ] then # skip lipo LIPO= fi fi fi if [ "$COMPILE" ] then if [ ! `which yasm` ] then echo 'Yasm not found' if [ ! `which brew` ] then echo 'Homebrew not found. Trying to install...' ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" \ || exit 1 fi echo 'Trying to install Yasm...' brew install yasm || exit 1 fi if [ ! `which gas-preprocessor.pl` ] then echo 'gas-preprocessor.pl not found. Trying to install...' (curl -L https://github.com/libav/gas-preprocessor/raw/master/gas-preprocessor.pl \ -o /usr/local/bin/gas-preprocessor.pl \ && chmod +x /usr/local/bin/gas-preprocessor.pl) \ || exit 1 fi # if [ ! -r $SOURCE ] # then # echo 'FFmpeg source not found. Trying to download...' # curl http://www.ffmpeg.org/releases/$SOURCE.tar.bz2 | tar xj \ # || exit 1 # fi cd $SOURCE configHeaderFile="$SOURCE/config.h" make clean if [ ! -f "$configHeaderFile" ]; then # echo $configHeaderFile rm config.h fi cd .. CWD=`pwd` echo $CWD for ARCH in $ARCHS do echo "building $ARCH..." mkdir -p "$SCRATCH/$ARCH" cd "$SCRATCH/$ARCH" CFLAGS="-arch $ARCH" if [ "$ARCH" = "i386" -o "$ARCH" = "x86_64" ] then PLATFORM="iPhoneSimulator" CFLAGS="$CFLAGS -mios-simulator-version-min=$DEPLOYMENT_TARGET" else PLATFORM="iPhoneOS" CFLAGS="$CFLAGS -mios-version-min=$DEPLOYMENT_TARGET -fembed-bitcode" if [ "$ARCH" = "arm64" ] then EXPORT="GASPP_FIX_XCODE5=1" fi fi XCRUN_SDK=`echo $PLATFORM | tr '[:upper:]' '[:lower:]'` CC="xcrun -sdk $XCRUN_SDK clang" CXXFLAGS="$CFLAGS" LDFLAGS="$CFLAGS" if [ "$X264" ] then CFLAGS="$CFLAGS -I$X264/include" LDFLAGS="$LDFLAGS -L$X264/lib" fi if [ "$FDK_AAC" ] then CFLAGS="$CFLAGS -I$FDK_AAC/include" LDFLAGS="$LDFLAGS -L$FDK_AAC/lib" fi TMPDIR=${TMPDIR/%\/} $CWD/$SOURCE/configure \ --target-os=darwin \ --arch=$ARCH \ --cc="$CC" \ $CONFIGURE_FLAGS \ --extra-cflags="$CFLAGS" \ --extra-ldflags="$LDFLAGS" \ --prefix="$THIN/$ARCH" \ || exit 1 make -j3 install $EXPORT || exit 1 cd $CWD done fi if [ "$LIPO" ] then echo "building fat binaries..." mkdir -p $IOSDest/lib set - $ARCHS CWD=`pwd` cd $THIN/$1/lib for LIB in *.a do cd $CWD echo lipo -create `find $THIN -name $LIB` -output $IOSDest/lib/$LIB 1>&2 lipo -create `find $THIN -name $LIB` -output $IOSDest/lib/$LIB || exit 1 done cd $CWD cp -rf $THIN/$1/include $IOSDest fi echo Done } ios android 这段脚本可以编译出android和ios的两个平台的配置,当然根据你的需求在configure中配置需要的东西,否则ffmpeg编译出来之后还是很大。 顺便解释一下:SOURCE=”ffmpeg-3.1.1”这个是release ffmpeg的版本,通过curl去下载,然后会在脚本的目录出下载并解压生成一个ffmpeg-3.1.1目录。 这个脚本有两个事情需要说明:
将下面的内容 SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)' SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)' 修改为: SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' SLIB_INSTALL_LINKS='$(SLIBNAME)' 当然如果你是shell脚本高手,在编译android的时候通过sed命令将上面提到内容进行修改也是可以的。 编译完成之后将编译出来的lib(so文件或者.a文件)和include(头文件)拷贝到你android studio中和xcode中。android具体如图: ![]() gradle脚本配置: ndk {
moduleName "FFPlayer"
stl "stlport_static"
cFlags "-std=gnu++11 -DGL_GLEXT_PROTOTYPES"
abiFilters "armeabi", "armeabi-v7a"
String basePath = new File("./").canonicalPath + "/ffplaylib/src/main/jniLibs/armeabi/"
String libavcodec = basePath + "libavcodec-54.so"
String libavdevice = basePath + "libavdevice-54.so"
String libavfilter = basePath + "libavfilter-3.so"
String libavformat = basePath + "libavformat-54.so"
String libavutil = basePath + "libavutil-51.so"
String libswresample = basePath + "libswresample-0.so"
String libswscale = basePath + "libswscale-2.so"
ldLibs "log","GLESv2","dl", "GLESv1_CM","GLESv2", "android",libavcodec, libavdevice, libavfilter, libavformat, libavutil,libswresample,libswscale
}
sourceSets {
main {
jni.srcDirs "src/main/jni", "src/main/jni/include","src/main/jni/include/libavcodec","src/main/jni/include/libavdevice","src/main/jni/include/libavfilter",
"src/main/jni/include/libavformat","src/main/jni/include/libavutil","src/main/jni/include/libswresample",
"src/main/jni/include/libswscale", "src/main/jni/SDL", "src/main/jni/SDL/include"
jniLibs.srcDirs "src/main/jinLibs"
}
}第一个是ndk配置,配置moduleName等,同时指定本地依赖libavcodec等so库。 sourceSets主要是制定include头文件路径。 这样就能移植进入android studio中,而不是在eclipse中。 而对ios想到简单一些。具体如图: ![]() 因为这边是用swift写的应用,所以还需要指定Swift compiler和Search path Search path: ![]() Swift compiler ![]() Swift compiler的头文件如下: |