直播技术(从服务端到客户端)一

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下载地址:

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目录。
这个脚本有两个事情需要说明:

  1. 如果不修改configure文件,android平台编译出来的so文件名有问题,会在.so后面带上版本号,这个在android中识别不了的。
  2. 如果修改了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基本配置以及客户端采集推流相关的东西。

以上是关于直播技术(从服务端到客户端)一的主要内容,如果未能解决你的问题,请参考以下文章

直播技术(从服务端到客户端)二

直播技术(从服务端到客户端)一

直播技术(从服务端到客户端)三

直播技术(从服务端到客户端)三

淘宝直播端到端音视频评测方案首次公开

从服务端到客户端怎么传输Sql的dataset,怎么把字节数组转化为字符串,在线等