多媒体直播(代码片段)

author author     2022-10-21     131

关键词:

小程之前介绍了如何录制音视频,以及相关的多媒体的概念。对于已经录制的多媒体进行“就地”播放,也就是回放,也在之前作了介绍。

那么,除了“回放”这个流程,还有一个流程也会经常遇到,那就是“直播”。

本文介绍直播的实现。

“直播”的特点是边录制边播放。如果想完成直播的流程,需要有支持直播功能的服务器(也叫流媒体服务器)。有了直播服务器后,就可以把录制的数据推送到服务器,然后再从服务器拉取数据进行播放。

那么怎么实现这个有直播功能的服务器呢,在这里,小程介绍具备这个功能的服务器程序:nginx。

nginx是一个http服务器,但通过扩展(比如加入rtmp模块等),可以变身为流媒体服务器,并且支持rtmp与hls协议,也就具备了“直播”的功能。如果读者对于rtmp或hls协议不了解,也没有关系,只需要知道它是一个传输的约定就可以了,在特定的场景再作深入了解。

nginx是一个完整的程序,读者只需要做一些安装与配置的工作,就可以弄出一个支持直播(或点播)的原型出来,甚至可以投入使用。

(一)安装nginx

以编译nginx源码的方式来安装nginx。

nginx源码的目录结构是这样的:
技术分享图片

小程列一些具体的操作,读者可以按需参考:

nginx源码下载
到nginx官网下载最新版本的nginx源码,官网的地址:
<http://nginx.org/en/download.html>;

rtmp模块,也就是nginx-rtmp-module的源码下载,让它跟nginx项目在同一个目录下面:
git clone https://github.com/arut/nginx-rtmp-module.git

openssl模块的源码下载(这个被rtmp使用,其实也可以不用,但小程这里还是加上了),同样与nginx项目在一个目录下面:
git clone https://github.com/openssl/openssl.git

编译nginx并安装
下载完源码后,就可以开始编译了:
(注意:如果之前用brew安装过nginx,那要先卸载:sudo brew uninstall nginx,因为要支持rtmp,须通过源码来编译安装。)
cd nginx-1.11.13/
./configure --add-module=../nginx-rtmp-module --with-openssl=../openssl
make
sudo make install

最终的安装目录应该是/usr/local/nginx/sbin/,
在~/.bash_profile中指定搜索路径:
export PATH="$PATH:/usr/local/nginx/sbin/"

安装好nginx后,可以尝试使用它:

查看nginx配置文件路径等信息:
nginx -h

启动nginx:
sudo nginx
如果有提示端口已经被占用,那可能已经启动了,可以重新启动:
sudo nginx -s reload

测试nginx:
curl 127.0.0.1
或者浏览器访问localhost:8080,
能看到welcome信息即表示安装成功而且已经运行,占用8080端口。

最终可以看到nginx的welcome:
技术分享图片

(二)实现点播

因为实现点播的功能会更简单一点,所以小程先介绍使用nginx来实现点播的操作,读者也可以直接跳至“直播”的内容来阅读。

做到点播,只需要改下配置,让nginx关联一个本地的文件即可。

具体操作是这样的:

查看配置文件的路径:
nginx -h

配置文件为/usr/local/nginx/conf/nginx.conf,
也可以通过nginx -c来指定一个新的配置文件。

在配置文件中,增加rtmp项:
rtmp
server
listen 1935; # port
chunk_size 4096; # data chunk size

  application playback 
      play /opt/show;     # target file path
  



1935为端口,chunk_size为块大小。
playback为应用的名称,可以任意改。
play为关键字,不能改。
play对应的值为本地文件所在目录,把要播放的文件放在这个目录内。

配置完毕后,重启nginx:
sudo nginx -s reload

测试,使用ffplay来播放nginx服务器的点播(对于ffplay的使用读者可以参考小程之前的介绍):
ffplay rtmp://localhost/playback/Movie-1.mp4

Movie-1.mp4是放在目录/opt/show/下面的文件。

按测试的示例,可以想到,如果把localhost换成某个ip就可以对不同机子上面的文件进行点播。

(三)实现直播

实现直播,也很简单,同样修改一下配置,就可以做到。

具体的操作是这样的:

在配置文件中,增加rtmp项:
rtmp
server
listen 1935; # port
chunk_size 4096; # data chunk size

  application rtmpdemo 
      live on;
  



同样,1935为端口,chunk_size为块大小。
rtmpdemo是应用名称,可以随意改。

重启nginx:
sudo nginx -s reload

用ffmpeg来模拟推流(小程之前有简单提到ffmpeg程序的使用,如果想了解更多,请关注“广州小程”微信公众号后续的更新):
sudo ffmpeg -re -i Movie-1.mp4 -vcodec copy -f flv rtmp://localhost/rtmpdemo/test1
-re 表示按帧率来推;
-f 为推送时封装的格式,对于rtmp都应该使用flv。

这时,服务器nginx已经有多媒体流了,客户端再拉流播放:
ffplay "rtmp://localhost/rtmpdemo/test1 live=1"

上面的演示,是把一个本地的文件推到了nginx,实际的直播场景中,是边录制边推流,读者可以结合小程以前介绍的录制视频的办法,来做到录制。

至此,小程已经把“使用nginx来实现直播”的主体操作介绍完了,但这毕竟只是一个原型,直播的难点分落在服务器与客户端,比如服务器如何高性能,客户端如何实时(与协议选择、服务器分布也有关)并处理好声画质量的问题,等等,小程后续会在自己的能力范围内介绍这类的知识与技能。

以上介绍了通过nginx实现直播的流程,其中一个环节是通过ffmpeg的命令来推流的,那如果想写代码来实现,可以怎么做呢?

这里涉及到FFmpeg的调用,而它的使用应该有更多的前提,比如FFmpeg的编译、引用、调用等等,如果读者想在了解这些前置环节之后再作深入了解也是可以的,那就不必阅读下面的内容。但是,为了保持内容的完整性,小程还是加上这部分内容。


使用ffmpeg命令来推流,控制度不够高,现在以代码的方式来实现,可灵活控制。

最终的效果是这样的(一边推流到服务器,一边从服务器拉流播放):
技术分享图片

演示推流的代码

#include <stdio.h>
#include "ffmpeg/include/libavformat/avformat.h"
#include "ffmpeg/include/libavcodec/avcodec.h"

void publishstream() 
    const char* srcfile = "t.mp4";
    const char* streamseverurl = "rtmp://localhost/rtmpdemo/test1";
    av_register_all();
    avformat_network_init();
    av_log_set_level(AV_LOG_DEBUG);
    int status = 0;
    AVFormatContext* formatcontext = avformat_alloc_context();
    status = avformat_open_input(&formatcontext, srcfile, NULL, NULL);
    if (status >= 0) 
        status = avformat_find_stream_info(formatcontext, NULL);
        if (status >= 0) 
            int videoindex = -1;
            for (int i = 0; i < formatcontext->nb_streams; i ++) 
                if (formatcontext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
                    videoindex = i;
                    break;
                
            
            if (videoindex >= 0) 
                AVFormatContext* outformatcontext;
                avformat_alloc_output_context2(&outformatcontext, NULL, "flv", streamseverurl);
                if (outformatcontext) 
                    status = -1;
                    for (int i = 0; i < formatcontext->nb_streams; i ++) 
                        AVStream* onestream = formatcontext->streams[i];
                        AVStream* newstream = avformat_new_stream(outformatcontext, onestream->codec->codec);
                        status = newstream ? 0 : -1;
                        if (status == 0) 
                            status = avcodec_copy_context(newstream->codec, onestream->codec);
                            if (status >= 0) 
                                newstream->codec->codec_tag = 0;
                                if (outformatcontext->oformat->flags & AVFMT_GLOBALHEADER) 
                                    newstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
                                
                            
                        
                    
                    if (status >= 0) 
                        AVOutputFormat* outformat = outformatcontext->oformat;
                        av_usleep(5*1000*1000); // 故意等一下再开始推流,让拉流的客户端有时间启动,以拿到视频的pps/sps
                        if (!(outformat->flags & AVFMT_NOFILE)) 
                            av_dump_format(outformatcontext, 0, streamseverurl, 1);
                            status = avio_open(&outformatcontext->pb, streamseverurl, AVIO_FLAG_WRITE);
                            if (status >= 0) 
                                status = avformat_write_header(outformatcontext, NULL);
                                if (status >= 0) 
                                    AVPacket packet;
                                    int videoframeidx = 0;
                                    int64_t starttime = av_gettime();
                                    while (1) 
                                        status = av_read_frame(formatcontext, &packet);
                                        if (status < 0) 
                                            break;
                                        
                                        if (packet.pts == AV_NOPTS_VALUE) 
                                            av_log(NULL, AV_LOG_DEBUG, "set pakcet.pts\n");
                                            AVRational video_time_base = formatcontext->streams[videoindex]->time_base;
                                            int64_t frameduration = (double)AV_TIME_BASE / av_q2d(formatcontext->streams[videoindex]->r_frame_rate);
                                            packet.pts = (double)(videoframeidx * frameduration) / (double)(av_q2d(video_time_base) * AV_TIME_BASE);
                                            packet.dts = packet.pts;
                                            packet.duration = (double)frameduration / (double)(av_q2d(video_time_base) * AV_TIME_BASE);
                                        
                                        if (packet.stream_index == videoindex) 
                                            AVRational video_time_base = formatcontext->streams[videoindex]->time_base;
                                            AVRational time_base_q = 1, AV_TIME_BASE;
                                            int64_t cur_pts = av_rescale_q(packet.dts, video_time_base, time_base_q);
                                            int64_t curtime = av_gettime() - starttime;
                                            av_log(NULL, AV_LOG_DEBUG, "on video frame curpts=%lld curtime=%lld\n", cur_pts, curtime);
                                            if (cur_pts > curtime) 
                                                av_usleep(cur_pts - curtime);
                                            
                                        
                                        AVStream* instream = formatcontext->streams[packet.stream_index];
                                        AVStream* outstream = outformatcontext->streams[packet.stream_index];
                                        packet.pts = av_rescale_q_rnd(packet.pts, instream->time_base, outstream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
                                        packet.dts = av_rescale_q_rnd(packet.dts, instream->time_base, outstream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
                                        packet.duration = av_rescale_q(packet.duration, instream->time_base, outstream->time_base);
                                        packet.pos = -1;
                                        if (packet.stream_index == videoindex) 
                                            videoframeidx ++;
                                        
                                        status = av_interleaved_write_frame(outformatcontext, &packet);
                                        if (status < 0) 
                                            break;
                                        
                                    
                                    av_write_trailer(outformatcontext);
                                
                                avio_close(outformatcontext->pb);
                            
                        
                    
                    avformat_free_context(outformatcontext);
                
            
        
        avformat_close_input(&formatcontext);
    
    avformat_free_context(formatcontext);


int main(int argc, char *argv[])

    publishstream();
    return 0;

小程以本地的视频文件作为内容,模拟了直播推流(推到nginx),功能上相当于直接调用ffmpeg命令:

sudo ffmpeg -re -i Movie-1.mp4 -vcodec copy -f flv rtmp://localhost/rtmpdemo/test1

当然也可以边录制边推送,也可以在不同的电脑或手机上,拉流播放。

直播开始后,这里的流媒体服务器并没有给中途拉流的客户端发送视频解码所必须的参数(pps/sps),所以在测试的时候,要保证拉流端能拿到第一帧数据,比如演示代码中故意sleep几秒后才开始推流,让拉流端有时间开启并拿到推上去的所有数据(包括关键参数)。

对于h264的知识,或者对于FFmpeg使用的知识,请关注“广州小程”微信公众号以获取信息。


总结一下,本文介绍了使用nginx来实现多媒体的直播,对于nginx的操作难度系数为2,如果写代码推流的话,难度系数为3。

流媒体开发10ffmpeg命令直播(代码片段)

...怎么将网络流保存到本地,并且怎么拉流和推流一、直播拉流一般都是先使用ffplay先测试rtmp链接是否可用ffplayrtmp://server/live/streamNameffmpeg-irtmp://server/live/streamName-ccopydump.flv对于不是rtmp的协议-ccopy要谨慎使用,-c等价于-code... 查看详情

搭建自己的直播流媒体服务器srs,以及srs+obs直播推拉流使用及配置(代码片段)

一、前言目前,全球直播带货什么的,成为主流,那如何自己搭建一个直播服务器呢。首先需要一个流媒体服务器,搭建流媒体有很多种方式,如下:流媒体解决方案Live555(C++)流媒体平台... 查看详情

搭建自己的直播流媒体服务器srs,以及srs+obs直播推拉流使用及配置(代码片段)

一、前言目前,全球直播带货什么的,成为主流,那如何自己搭建一个直播服务器呢。首先需要一个流媒体服务器,搭建流媒体有很多种方式,如下:流媒体解决方案Live555(C++)流媒体平台... 查看详情

技术分享|如何搭建直播场景下的推拉流媒体服务器(代码片段)

介绍本文使用的流媒体服务器的搭建是基于rtmp(RealTimeMessageProtocol)协议的,rtmp协议是应用层的协议,要依靠底层的传输层协议,比如tcp协议来保证信息传输的可靠性。相关服务:Nginx、srs、MediaServer等三... 查看详情

开源流媒体解决方案,流媒体服务器,推拉流,直播平台,srs,webrtc,移动端流媒体,网络会议,优秀博客资源等分享(代码片段)

...源流媒体解决方案,流媒体服务器,推拉流,直播平台,SRS,WebRTC,移动端流媒体,网络会议,优秀博客资源等分享一、优秀的流媒体博客资源1.1EasyNVR:专注于安防视频互联网化的技术1.2青柿... 查看详情

livenvr拉转onvif/rtsp/rtmp/flv/hls直播流流媒体服务视频广场页面集成视频播放集成说明(代码片段)

...me示例2.3、分享页面参数说明3、视频流地址获取集成3.1、直播流地址获取3.2、H5直播点播播放器4、Onvif/RTSP流媒体服务1、视频页面集成1.1、关闭接口鉴权关闭接口鉴权 查看详情

网络协议-流媒体协议(代码片段)

视频编码与直播的实现原理介绍为什么要对视频进行编码流媒体协议主要用于流媒体视频的直播和点播,在介绍流媒体协议之前,先给大家介绍下视频的压缩和编码机制。说到视频播放,其实就是快速播放一系列连续的图片而已... 查看详情

直播hls协议(代码片段)

HTTPLiveStreaming(简称HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。​是苹果公司QuickTimeX和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当... 查看详情

直播hls协议(代码片段)

HTTPLiveStreaming(简称HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。​是苹果公司QuickTimeX和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当... 查看详情

直播hls协议(代码片段)

HTTPLiveStreaming(简称HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。​是苹果公司QuickTimeX和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当... 查看详情

国标gb/t28181流媒体服务获取接入的设备通道直播流地址hls/http-flv/ws-flv/rtmp/rtsp(代码片段)

目录国标设备查看通道直播播放分享页面获取直播流地址(1)获取HTTP-FLV直播流地址(2)获取HLS直播流地址(3)获取RTMP直播流地址(4)获取WS-FLV直播流地址(5)RTSP开启RTSP获取RTSP直播流地址搭建GB28181流媒体平台国标设备根据GB28181规范,接... 查看详情

直播代码

...23819 阅读, 81 评论, 收藏,  编辑--iOS多媒体概览随着移动互联网的发展,如今的手机早已不是打电话、发短信那么简单了,播放音乐、视频、录音、拍照等都是很常用的功能。在iOS中对于多媒体的支持是非常 查看详情

android/ios:通过流混合改善实时流媒体体验(代码片段)

...端将多个音频或视频流组合成一个流的技术。广泛应用于直播、在线教育、直播间等场景。开发者可以通过播放混流来查看屏幕并听到房间内所有成员的声音,而不需要管理房间中的每个流。流混合的好处由于对开发人员的... 查看详情

android/ios:通过流混合改善实时流媒体体验(代码片段)

...端将多个音频或视频流组合成一个流的技术。广泛应用于直播、在线教育、直播间等场景。开发者可以通过播放混流来查看屏幕并听到房间内所有成员的声音,而不需要管理房间中的每个流。流混合的好处由于对开发人员的... 查看详情

开源流媒体解决方案,流媒体服务器,推拉流,直播平台,srs,webrtc,移动端流媒体,网络会议,优秀博客资源等分享(代码片段)

...源流媒体解决方案,流媒体服务器,推拉流,直播平台,SRS,WebRTC,移动端流媒体,网络会议,优秀博客资源等分享一、优秀的流媒体博客资源1.1EasyNVR:专注于安防视频互联网化的技术1.2青柿... 查看详情

使用ffmpeg搭建hls直播系统(代码片段)

...2018-04][状态:Open][关键词:流媒体,stream,HLS,ffmpeg,live,直播,点播,nginx,ssegment]0引言本文作为HLS综述的后续文章。主要目的是使用ffmpeg搭建一个简单的HLS点播及直播系统。使用nginx作为HTTP服务器。HLS不管点播还是直播,都是基于... 查看详情

多媒体前端技术入门指南(代码片段)

随着直播视频平台的快速崛起和发展,前端衍生出了多媒体技术方向,各公司的传统前端团队里陆续出现了一支新军:Web多媒体团队。光看团队Title,这应该是一个拥有前端×多媒体交叉领域稀有技能的群体。我们... 查看详情

流媒体服务海康摄像头rtsp视频推流转码拉流直播综合应用:vlc+ffmpeg+nginx实现rtsp到rtmp网页直播(代码片段)

...e4f6f07e3511a.jpg)四、总结前言前几章节已完成推流拉流转码直播相关的基础工作准备,本章节整体演示综合直播应用,延迟5秒钟左右…一、前几章节回顾【流媒体服务】基础协议了解(一):【1】RTP/RTMP/RTSP等... 查看详情