直播技术(从服务端到客户端)一
Posted Jarlene
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直播技术(从服务端到客户端)一相关的知识,希望对你有一定的参考价值。
环境部署
2015年开始直播变得越来越流行,很多的直播平台也应运而生,直播是一个很有技术的项目,从服务端到客户端到web等等。我们将写一序列的博客来阐述直播中的技术,这包括服务端技术和客户端技术。包括最简单的服务端环境部署、客户端编译、采集、推流、拉流、美化特效、水印、延时优化、音视频同步、p2p等等。当然还可能包括一些信号处理的知识,比如滤波,傅里叶变换(FFT)。从本文开始我们将从环境部署开始,这包括两方面的环境部署即服务端和客户端。
1、服务端
在部署服务端环境其实包含很多东西的,最常用的web服务nginx,数据库mysql、Nosql,api开发最多的三种选择:
- java环境,需要jdk,tomcat/jboss
- php环境,需要安装php,odp
- lua环境,需要安装lua、luajit
考虑使用缓存技术,则主要包含redis和memcached。如果还要其他的日志统计(kafka什么的)需求则还需要更多的环境,我们这里不讨论,只是简单叙述
对于直播而言,我们需要部署两个东西,nginx(含nginx-rtmp-module)、ffmpeg,这两个是直播服务端的关键,下面我们简单讲述如何安装nginx(含nginx-rtmp-module)和ffmpeg以及如何配置nginx.conf。
首选我们来安装nginx和ffmpeg。
nginx下载地址:
- 官方release:http://nginx.org/en/download.html
- gitHub地址:https://github.com/nginx/nginx
ffmpeg下载地址:
- 官方release:https://ffmpeg.org/download.html
- gitHub地址:https://github.com/FFmpeg/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目录。
这个脚本有两个事情需要说明:
- 如果不修改configure文件,android平台编译出来的so文件名有问题,会在.so后面带上版本号,这个在android中识别不了的。
- 如果修改了configure文件,在编译ios平台的时候会遇到一个no file or directory的问题。导致完成后install的时候失败。
建议:
先编译ios平台,然后在修改configure,具体修改如下:
将下面的内容
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的头文件如下:
做完这些之后就可以在代码中直接引用了。
在android中是通过jni调用Native代码的,这里我就详细介绍了。
3、总结
本文讲述从服务端到客户基本环境部署,其中包括一些关键的东西点。下一篇直播技术的文章将阐述android和ios如何播放视频代码。再之后将会详细介绍ffmpeg以及其基本架构。之后会叙述服务端nginx.conf基本配置以及客户端采集推流相关的东西。
以上是关于直播技术(从服务端到客户端)一的主要内容,如果未能解决你的问题,请参考以下文章