精华内容
下载资源
问答
  • 一分钟部署HLS录像和点播服务

    转载请注明出处:https://blog.csdn.net/impingo
    项目地址:https://github.com/im-pingo/pingos

    HLS直播和录像

    众所周知不管是在直播或者点播方式的选择中,HLS都是兼容性最好的协议。

    以往常见的录像和点播

    以往为了快速实现HLS点播服务,需要服务器将实时数据保存成mp4或者flv格式数据,然后直播结束后再使用ffmpeg将录像文件切成小片。

    推流
    推流
    读取内容
    直播
    读取
    点播
    主播
    直播服务器
    录像服务
    切片服务
    观众
    点播服务器

    从这张图中可以看到为了实现hls点播,我们需要将原有录像进行独立切片,然后将其切片放在http目录以供hls点播服务使用。
    为了完成这样的操作,需要频繁对硬盘进行读写,同时又必须等到录像文件生成后才能切成ts和m3u8文件。

    使用pingos快速实现HLS录像和点播

    pingos项目中的nginx-rtmp-module模块可以极大地简化上述流程,用户甚至可以直接对当前直播时间一分钟以内的内容进行点播,不尽在效率和点播内容的时效性上有很大改善,在机器资源配置上也节省了切片服务。

    推流
    推流
    直播
    点播
    主播
    直播服务器
    录像服务直接录成hls文件
    观众

    需要注意的是,我们应该尽量将录像服务和直播服务器独立,因为直播服务器对磁盘并无太高要求,仅对转发效率和带宽要求较高。
    而录像服务队磁盘空间和磁盘读写性能要求都比较高,所以面对不同应用场景需要配备不同类型的硬件。
    下面给出直播服务器和录像服务器配置方案:
    假设 录像服务器IP为 192.168.1.3,下面是直播服务器和录像服务器的配置模板(注意:直播服务器和录像服务器是分开部署的)

    直播配置模板

    user  root;
    daemon on;
    master_process on;
    worker_processes  1;
    #worker_rlimit 4g;
    #working_directory /usr/local/openresty/nginx/logs;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    error_log  logs/error.log  info;
    
    worker_rlimit_nofile 102400;
    worker_rlimit_core   2G;
    working_directory    /tmp;
    
    #pid        logs/nginx.pid;
    
    events {
        use epoll;
        worker_connections  1024;
        multi_listen unix:/tmp/http 80;
    }
    
    stream_zone buckets=1024 streams=4096;
    
    rtmp {
        log_format log_json '{$remote_addr, [$time_local]}';
        access_log logs/rtmp.log trunc=2s;
        server {
            listen 1935;
            serverid 000;
            out_queue 2048;
    
            application live {
                rtmp_auto_pull on;
                rtmp_auto_pull_port unix:/tmp/rtmp;
                live_record on;
                live_record_path /tmp/record;
                live_record_interval  1m;
        
                push rtmp://192.168.1.3/live;
    
                live on;
                wait_key on;
                wait_video on;
                cache_time 3s;
                low_latency off;
                fix_timestamp 0s;
    # h265 codecid, default 12
                hevc_codecid  12;
            }
        }
    }
    
    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" "$http_X-Real-IP" "$host"';
    
    
        access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
        server {
             listen 80;
            location /rtmp_stat {
                rtmp_stat all;
                rtmp_stat_stylesheet /stat.xsl;
            }
    
            location /xstat {
                rtmp_stat all;
            }
    
            location /sys_stat {
                sys_stat;
            }
             location / {
                 chunked_transfer_encoding on;
                 root html/;
             }
    }
    

    录像配置模板

    user  root;
    daemon on;
    master_process on;
    worker_processes  1;
    #worker_rlimit 4g;
    #working_directory /usr/local/openresty/nginx/logs;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    error_log  logs/error.log  info;
    
    worker_rlimit_nofile 102400;
    worker_rlimit_core   2G;
    working_directory    /tmp;
    
    #pid        logs/nginx.pid;
    
    events {
        use epoll;
        worker_connections  1024;
    }
    
    stream_zone buckets=1024 streams=4096;
    
    rtmp {
        log_format log_json '{$remote_addr, [$time_local]}';
        access_log logs/rtmp.log trunc=2s;
        server {
            listen 1935;
            serverid 000;
            out_queue 2048;
    
            application live {
                rtmp_auto_pull on;
                rtmp_auto_pull_port unix:/tmp/rtmp;
                live_record on;
                live_record_path /tmp/record;
                live_record_interval  1m;
      
                live on;
    # h265 codecid, default 12
                hevc_codecid  12;
            }
        }
    }
    
    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" "$http_X-Real-IP" "$host"';
    
    
        access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
        server {
             listen 80;
            location /rtmp_stat {
                rtmp_stat all;
                rtmp_stat_stylesheet /stat.xsl;
            }
    
            location /xstat {
                rtmp_stat all;
            }
    
            location /sys_stat {
                sys_stat;
            }
             location / {
                 chunked_transfer_encoding on;
                 root html/;
             }
    }
    

    QQ交流群:697773082(流媒体人的后院)

    展开全文
  • 项目地址:...目录控制台接口配置配置项配置模板控制接口说明JS控制台示例录像文件查看接口获取流信息QQ交流群:697773082 控制台接口 一个成熟的媒体服务器需要有跟业系统对接的能力,例如...

    转载请注明出处:https://blog.csdn.net/impingo
    项目地址:https://github.com/im-pingo/
    项目官网:http://pingos.io

    控制台接口

    一个成熟的媒体服务器需要有跟业系统对接的能力,例如允许开发者开发自己的控制台系统,通过http接口控制当前直播流的暂停与播放,录像功能的暂停与继续等操作。
    本项目支持多种控制台控制接口和流信息获取接口。

    配置

    配置项

    配置项参数类型默认值描述
    rtmp_control选项allall: 开启所有控制接口。record: 只开启录像控制接口。drop:只开启关闭连接控制接口。redirect:只开启重定向控制接口。pause:只开启暂停接口。 resume:只开启恢复接口

    配置模板

    user  root;
    daemon on;
    master_process on;
    worker_processes  1;
    #worker_rlimit 4g;
    #working_directory /usr/local/openresty/nginx/logs;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    error_log  logs/error.log  info;
    
    worker_rlimit_nofile 102400;
    worker_rlimit_core   2G;
    working_directory    /tmp;
    
    #pid        logs/nginx.pid;
    
    events {
        use epoll;
        worker_connections  1024;
        multi_listen unix:/tmp/http 80;
    }
    
    stream_zone buckets=1024 streams=4096;
    
    rtmp {
        log_format log_json '{$remote_addr, [$time_local]}';
        access_log logs/rtmp.log trunc=2s;
        server {
            listen 1935;
            serverid 000;
            out_queue 2048;
    
            application live {
                rtmp_auto_pull on;
                rtmp_auto_pull_port unix:/tmp/rtmp;
    #            live_record on;
    #            live_record_path /tmp/record;
    #            live_record_interval  1m;
                record all;
                record_path /tmp/record;
    
    #            exec_publish_done bash -c  "ffmpeg -i /tmp/record/$name.flv -c copy /tmp/record/$basename.mp4";
    #            exec_publish bash -c "ffmpeg -i rtmp://127.0.0.1/live/$name -c copy -movflags faststart /tmp/record/$name-$starttime.mp4";
                idle_streams on;
    #
    #            oclp_pull http://127.0.0.1/p1;
    
    #            push rtmp://120.132.13.108/live;
    
                live on;
                hls on;
                hls_type live;
                hls_path /tmp/hls;
                hls_fragment 5000ms;
                hls_max_fragment 8000ms;
                hls_playlist_length 15000ms;
    
    #            live_record on;
    #            live_record_path /tmp/record;
    #            live_record_interval 2m;
    #            live_record_min_fragment 10s;
    #            live_record_max_fragment 30s;
    
                hls2memory on;
                mpegts_cache_time 20s;
    
                hls2_fragment 1000ms;
                hls2_max_fragment 1300ms;
                hls2_playlist_length 3000ms;
    
                wait_key on;
                wait_video on;
                cache_time 3s;
                low_latency off;
                fix_timestamp 0s;
    # h265 codecid, default 12
                hevc_codecid  12;
            }
        }
    }
    
    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" "$http_X-Real-IP" "$host"';
    
    
        access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #reset_server_name www.test1.com www.test2.com;
        #gzip  on;
        server {
             listen 80;
             location /ts {
                 ts_live 1935 app=live;
             }
            location /control {
                rtmp_control all;
            }
            location /rtmp_stat {
                rtmp_stat all;
                rtmp_stat_stylesheet /stat.xsl;
            }
    
            location /xstat {
                rtmp_stat all;
            }
    
            location /sys_stat {
                sys_stat;
            }
    
         location /live {
                flv_live 1935;
             }
             
             location /files {
    	         alias /tmp/record;
    	        #Nginx日志目录
    	         autoindex on;
    	         #打开目录浏览功能
    	         autoindex_exact_size off;
    	         #默认为on,显示出文件的确切大小,单位是bytes
    	         #显示出文件的大概大小,单位是kB或者MB或者GB
    	         autoindex_localtime on;
    	         #默认为off,显示的文件时间为GMT时间。
    	         #改为on后,显示的文件时间为文件的服务器时间
    	         add_header Cache-Control no-store;
             }
             location /hls {
                # Serve HLS fragments
                 types {
                     application/vnd.apple.mpegurl m3u8;
                     video/mp2t ts;
                 }
                 root /tmp;
                 expires -1;
                 add_header Cache-Control no-cache;
             }
    
            location /hls2 {
                 hls2_live 1935 app=live;
             }
             location / {
                 chunked_transfer_encoding on;
                 root html/;
             }
    
             location /hls0/ {
                 types{
                     application/vnd.apple.mpegurl m3u8;
                     video/mp2t ts;
                 }
                 expires -1;
             }
        }
    }
    

    控制接口说明

    接口名请求回复描述
    暂停推流/${location}/pause/publisher?srv=${serverid}&app=${app}&name=${name}参考 http ack服务器停止转发收到的直播流
    恢复推流/${location}/resume/publisher?srv=${serverid}&app=${app}&name=${name}参考 http ack服务器恢复转发收到的直播流
    暂停录像/${location}/record/stop?srv=${serverid}&app=${app}&name=${name}&rec=${recorder}参考 http ack暂停某条流的录像
    恢复录像/${location}/record/start?srv=${serverid}&app=${app}&name=${name}&rec=${recorder}参考 http ack恢复某条流的录像

    JS控制台示例

    为了更好地帮助大家理解上述接口的使用方法,我专门写了一个简单的js控制台,仅仅实现了暂停、播放、录像和停止录像的基本控制。
    在这里插入图片描述
    源码如下:

    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
    <script>
        function pause() {
    		var name=document.getElementById('value').value;
    		var ip=document.getElementById('ip').value;
            var url = 'http://'+ ip + '/control/pause/publisher?srv=000&app=live&name='+name;
            var request = new XMLHttpRequest();
            request.open("Get", url, true);
            request.send()
        }
    
        function resume() {
    		var name=document.getElementById('value').value;
    		var ip=document.getElementById('ip').value;
            var url = 'http://' + ip + '/control/resume/publisher?srv=000&app=live&name='+name;
            var request = new XMLHttpRequest();
            request.open("Get", url, true);
            request.send()
        }
    
        function pauseRecord(){
    		var name=document.getElementById('value').value;
    		var ip=document.getElementById('ip').value;
            var url = 'http://' + ip + '/control/record/stop?srv=000&app=live&name='+name+'&rec=r1';
            var request = new XMLHttpRequest();
            request.open("Get", url, true);
            request.send()
        }
    
        function resumeRecord(){
    		var name=document.getElementById('value').value;
    		var ip=document.getElementById('ip').value;
            var url = 'http://'+ip+'/control/record/start?srv=000&app=live&name='+name+'&rec=r1';
            var request = new XMLHttpRequest();
            request.open("Get", url, true);
            request.send()
        }
    
        function finalize(){
    		var name=document.getElementById('value').value;
    		var ip=document.getElementById('ip').value;
            var url = 'http://'+ip+'/control/drop/publisher?srv=000&app=live&name='+name;
            var request = new XMLHttpRequest();
            request.open("Get", url, true);
            request.send()
        }
    </script>
    
        <span id="car_control" class="car">
    		server ip:
            <input type="text" id="ip" placeholder=""/><br/>
    		stream name:
            <input type="text" id="value" placeholder=""/><br/>
            <button class="top" onclick="pause()">暂停直播</button>
            <button class="left" onclick="resume()">继续直播</button>
            <button class="right" onclick="pauseRecord()">暂停录像</button>
            <button class="bottom" onclick="resumeRecord()">继续录像</button>
            <button class="bottom" onclick="finalize()">结束直播</button>
        </span>
    </body>
    </html>
    

    录像文件查看接口

    生成的录像文件会存在固定目录下,通过http接口(http://ip/files/)可以在浏览器中看到其文件列表。
    在这里插入图片描述

    获取流信息

    通过接口(http://ip/xstat)可请求到所有在线流的信息,内容如下

    <?xml version="1.0" encoding="utf-8" ?>
    <rtmp>
        <nginx_version>1.17.1</nginx_version>
        <nginx_rtmp_version>1.1.4</nginx_rtmp_version>
        <compiler>gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) </compiler>
        <built>Oct 24 2019 16:26:06</built>
        <pid>20853</pid>
        <uptime>176258</uptime>
        <naccepted>3</naccepted>
        <bw_in>0</bw_in>
        <bytes_in>171403</bytes_in>
        <bw_out>0</bw_out>
        <bytes_out>172549</bytes_out>
        <server>
            <serverid>000</serverid>
            <live>
                <stream>
                    <name>0</name>
                    <time>3225</time>
                    <bw_in>0</bw_in>
                    <bytes_in>168524</bytes_in>
                    <bw_out>0</bw_out>
                    <bytes_out>165725</bytes_out>
                    <bw_audio>0</bw_audio>
                    <bw_video>0</bw_video>
                    <client>
                        <id>455</id>
                        <address>0.0.0.0:1935</address>
                        <remote_address>127.0.0.1</remote_address>
                        <time>3056</time>
                        <flashver>LNX 9,0,124,2</flashver>
                        <dropped>0</dropped>
                        <avsync>0</avsync>
                        <timestamp>0</timestamp>
                        <active/>
                    </client>
                    <client>
                        <id>454</id>
                        <address>0.0.0.0:1935</address>
                        <remote_address>127.0.0.1</remote_address>
                        <time>3413</time>
                        <flashver>FMLE/3.0 (compatible; Lavf58.26.101)</flashver>
                        <dropped>0</dropped>
                        <avsync>11</avsync>
                        <timestamp>3219</timestamp>
                        <publishing/>
                        <active/>
                    </client>
                    <meta>
                        <video>
                            <width>1920</width>
                            <height>1080</height>
                            <frame_rate>24</frame_rate>
                            <codec>H264</codec>
                            <profile>High</profile>
                            <compat>0</compat>
                            <level>4.0</level>
                        </video>
                        <audio>
                            <codec>AAC</codec>
                            <profile>LC</profile>
                            <channels>6</channels>
                            <sample_rate>48000</sample_rate>
                        </audio>
                    </meta>
                    <nclients>2</nclients>
                    <publishing/>
                    <active/>
                </stream>
                <nclients>2</nclients>
            </live>
        </server>
    </rtmp>
    

    本项目支持的接口远不止此,更多控制接口请继续关注后续的文章或者进群交流。

    QQ交流群:697773082

    展开全文
  • 本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/105302384,欢迎关注...音视频系列博客: HectoorZ:音视频系列1:ffmpeg+rtmp拉流​zhuanlan.zhihu.comHectoorZ:音视频系列2:ffmpeg将H.26...

    c3f719c5c843625f4931aa2f49b2399a.png

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/105302384,欢迎关注,点赞,评论。

    前言

    本篇博客是音视频系列的续集与改进,有兴趣的可以看我之前的博客专栏。

    音视频系列博客:

    HectoorZ:音视频系列1:ffmpeg+rtmp拉流​zhuanlan.zhihu.com
    c80d15066e3cf68fd7a9ca8dee1967f2.png
    HectoorZ:音视频系列2:ffmpeg将H.264解码为RGB​zhuanlan.zhihu.com
    c80d15066e3cf68fd7a9ca8dee1967f2.png
    HectoorZ:音视频系列3:使用ffmpeg + nginx搭建本地转发服务器​zhuanlan.zhihu.com
    c80d15066e3cf68fd7a9ca8dee1967f2.png
    HectoorZ:音视频系列4:新手如何入门ffmpeg(以FLV解码H.264为例)​zhuanlan.zhihu.com
    c80d15066e3cf68fd7a9ca8dee1967f2.png
    HectoorZ:音视频系列5:ffmpeg拉流并引入ROS库​zhuanlan.zhihu.com
    97f20f3da8500426838046a33f4f8bf8.png

    有兴趣的小伙伴们可以看看。本节主要是对以下两篇博客进行改造升级,使得当前可以同时拉多个流,并且修改了一些之前的bug比如说段错误Segmentation fault。 音视频系列1:ffmpeg+rtmp拉流 音视频系列2:ffmpeg将H.264解码为RGB

    用一张图表示接下来我要做的事情(橙色框框):

    5b70e6afff846ecde3b404cdf89b21bd.png

    环境是

    Ubuntu18.04,ffmpeg4.1.5

    主要有这么几个文件:

    main.cpp

    transdata.cpp

    transdata.h

    源码

    主程序main.cpp分为两个部分,一是main()函数里申请pthread线程ID,开启线程,释放线程;二是athread线程函数的编写,每一个用户拉的流根据用户ID而定,比如用户ID为1的,拉流地址后面加1。 main.cpp:

    #include <iostream>
    #include "transdata.h"
    using namespace std;
    vector<Transdata> user_tran;
    void *athread(void *ptr)
    {
        int count = 0;
        int num = *(int *)ptr;
        //初始化
        while((user_tran[num].Transdata_init(num))<0)
        {
            cout << "init error "<< endl;
        }
        cout <<"My UserId is :"<< num << endl;
        //do something you want
        user_tran[num].Transdata_Recdata();
        user_tran[num].Transdata_free();
        return 0;
    }
    int main(int argc, char** argv)
    {
        int ret;
        //申请内存  相当于注册
        for(int i = 0; i < 5 ; i++)
        {
            Transdata *p = new Transdata();
            user_tran.push_back(*p);
            user_tran[i].User_ID = i;
            cout << &user_tran[i] << endl;
            delete p;
        }
        //开启五个线程
        for(int i = 0; i < 5; i ++)
        {
            int *num_tran;
            num_tran = &user_tran[i].User_ID;
            ret = pthread_create(&user_tran[i].thread_id,NULL,athread,(void *)num_tran);
            if(ret < 0) return -1;
        }
        for(int i = 0; i < 5; i++)
        {
            pthread_join(user_tran[i].thread_id, NULL);/*等待进程t_a结束*/
        }
        return 0;
    }
    

    transdata.cpp transdata.h是拉流的功能函数。

    transdata.cpp

    #include "transdata.h"
    
    Transdata::Transdata(){}
    Transdata::~Transdata(){}
    
    
    int Transdata::Transdata_free()
    {
        av_bsf_free(&bsf_ctx);
        avformat_close_input(&ifmt_ctx);
        av_frame_free(&pframe);
        if (ret < 0 && ret != AVERROR_EOF)
        {
            printf( "Error occurred.n");
            return -1;
        }
        return 0;
    }
    
    
    int Transdata::Transdata_Recdata()
    {
        //可以自己增加LOG函数
        //LOGD("Transdata_Recdata entry %d",User_ID);
        int count = 0;
        while(av_read_frame(ifmt_ctx, &pkt)>=0)
        {
            //LOGD("av_read_frame test %d",User_ID);
            if (pkt.stream_index == videoindex) {
                // H.264 Filter
                if (av_bsf_send_packet(bsf_ctx, &pkt) < 0){
                    cout << " bsg_send_packet is error! " << endl;
                    av_packet_unref(&pkt);
                    continue;
                    //return -1;
                }
               // LOGD("av_bsf_send_packet test %d",User_ID);
                if (av_bsf_receive_packet(bsf_ctx, &pkt) < 0) {
                    cout << " bsg_receive_packet is error! " << endl;
                    av_packet_unref(&pkt);
                    continue;
                    //return -1;
                }
               // LOGD("av_bsf_receive_packet test %d",User_ID);
                count ++;
                if(count == 10) {
                    printf("My id is %d,Write Video Packet. size:%dtpts:%ldn",User_ID, pkt.size, pkt.pts);
                    count =0;
                }
    
                // Decode AVPacket
               // LOGD("Decode AVPacket ,ID is  %d",User_ID);
                if (pkt.size) {
                    ret = avcodec_send_packet(pCodecCtx, &pkt);
                    if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                        std::cout << "avcodec_send_packet: " << ret << std::endl;
                        av_packet_unref(&pkt);
    
                        continue;
                        //return -1;
                    }
                //    LOGD("avcodec_send_packet test %d",User_ID);
                    //Get AVframe
                    ret = avcodec_receive_frame(pCodecCtx, pframe);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                        std::cout << "avcodec_receive_frame: " << ret << std::endl;
                        av_packet_unref(&pkt);
                        av_frame_unref(pframe);
                        continue;
                        // return -1;
                    }
                    //转成rgb
                    avframeToCvmat(pframe);
                }
            }
            //Free AvPacket
            av_packet_unref(&pkt);
         //   av_free(pframe->data[0]);
            av_frame_unref(pframe); //后来才增加的 !! 每次重用之前应调用将frame复位到原始干净可用状态
            //https://www.cnblogs.com/leisure_chn/p/10404502.html
    
        }
            return 0;
    
    }
    //AVFrame 转 cv::mat
    void Transdata::avframeToCvmat(const AVFrame * frame)
    {
       // LOGD("avframeToCvmat  imshow1 , ID is  %d",User_ID);
        int width = frame->width;
        int height = frame->height;
        cv::Mat image(height, width, CV_8UC3);
      //  LOGD("avframeToCvmat  imshow2 , ID is  %d",User_ID);
        int cvLinesizes[1];
        cvLinesizes[0] = image.step1();
        SwsContext* conversion = sws_getContext(width, height, (AVPixelFormat) frame->format, width, height, AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
        sws_scale(conversion, frame->data, frame->linesize, 0, height, &image.data, cvLinesizes);
      //  LOGD("avframeToCvmat  imshow3 , ID is  %d",User_ID);
        sws_freeContext(conversion);
      //  LOGD("avframeToCvmat  imshow4 , ID is  %d",User_ID);
        imshow(Simg_index,image);
        startWindowThread();//开启显示线程,专门用于显示
        waitKey(1);
        image.release();
      //  LOGD("avframeToCvmat  imshow5 , ID is  %d",User_ID);
    }
    
    int Transdata::Transdata_init(int num) {
    
        User_ID = num;  //用户ID
        Simg_index = to_string(num);
        cout << "Simg_index is : "<< Simg_index << endl;
        string str3 = to_string(num);
        cout << str3.size()<< endl;
        std::string video_name=str2+str3;
        const char *video_filename = video_name.c_str();//string转const char*
    
        cout << video_filename << endl;
    
        //新增
        ifmt_ctx = avformat_alloc_context();
        //pkt = (AVPacket *)av_malloc(sizeof(AVPacket));
    
        //Register
        av_register_all();
        //Network
        avformat_network_init();
        //Input
        if ((ret = avformat_open_input(&ifmt_ctx, video_filename, 0, 0)) < 0) {
            printf("Could not open input file.");
            return -1;
        }
        if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
            printf("Failed to retrieve input stream information");
            return -1;
        }
    
        videoindex = -1;
        for (i = 0; i < ifmt_ctx->nb_streams; i++) {
            if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoindex = i;
                codecpar = ifmt_ctx->streams[i]->codecpar;
            }
        }
    
        //Find H.264 Decoder
        pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
        if (pCodec == NULL) {
            printf("Couldn't find Codec.n");
            return -1;
        }
        pCodecCtx = avcodec_alloc_context3(pCodec);
        if (!pCodecCtx) {
            fprintf(stderr, "Could not allocate video codec contextn");
            return -1;
        }
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Couldn't open codec.n");
            return -1;
        }
        pframe = av_frame_alloc();
        if (!pframe) {
            printf("Could not allocate video framen");
            return -1;
        }
        //find filter
        buffersrc = av_bsf_get_by_name("h264_mp4toannexb");
        //
        if(av_bsf_alloc(buffersrc, &bsf_ctx) < 0) {
            printf("av_bsf_alloc is error");
            return -1;
        }
        if(codecpar != NULL) {
            if (avcodec_parameters_copy(bsf_ctx->par_in, codecpar) < 0) {
                printf("avcodec_parameters_copy is error");
                return -1;
            }
            if (av_bsf_init(bsf_ctx) < 0) {
                printf("av_bsf_init is error");
                return -1;
            }
        }
        else {
            printf("codecpar is NULLn");
            return -1;
        }
            return 0;
    }
    

    transdata.h

    #ifndef VERSION1_0_TRANSDATA_H
    #define VERSION1_0_TRANSDATA_H
    
    #include <iostream>
    extern "C"
    {
    #include "libavformat/avformat.h"
    #include <libavutil/mathematics.h>
    #include <libavutil/time.h>
    #include <libavutil/samplefmt.h>
    #include <libavcodec/avcodec.h>
    #include <libavfilter/buffersink.h>
    #include <libavfilter/buffersrc.h>
    #include "libavutil/avconfig.h"
    #include <libavutil/imgutils.h>
    #include "libswscale/swscale.h"
    };
    #include "opencv2/core.hpp"
    #include<opencv2/opencv.hpp>
    //#include "LogUtils.h"
    using namespace std;
    using namespace cv;
    
    class Transdata
    {
    public:
        Transdata();
        ~Transdata();
        AVFormatContext *ifmt_ctx = NULL;
        AVPacket pkt;
        AVFrame *pframe = NULL;
        int ret, i;
        int videoindex=-1;
        AVCodecContext  *pCodecCtx;
        AVCodec         *pCodec;
        const AVBitStreamFilter *buffersrc  =  NULL;
        AVBSFContext *bsf_ctx;
        AVCodecParameters *codecpar = NULL;
        std::string str2= "rtmp://localhost:1935/rtmplive/test";
        //std::string str2= "rtmp://47.100.110.164:1935/live/test";
        //const char *in_filename  = "rtmp://localhost:1935/rtmplive";   //rtmp地址
        //const char *in_filename  = "rtmp://58.200.131.2:1935/livetv/hunantv";   //芒果台rtmp地址
        cv::Mat image_test;
        int Transdata_init(int num);
        int Transdata_Recdata();
        int Transdata_free();
        void avframeToCvmat(const AVFrame * frame);
        int User_ID;
        string Simg_index;
        pthread_t thread_id;
    };
    
    
    #endif //VERSION1_0_TRANSDATA_H
    #ifndef VERSION1_0_TRANSDATA_H
    #define VERSION1_0_TRANSDATA_H
    
    #include <iostream>
    extern "C"
    {
    #include "libavformat/avformat.h"
    #include <libavutil/mathematics.h>
    #include <libavutil/time.h>
    #include <libavutil/samplefmt.h>
    #include <libavcodec/avcodec.h>
    #include <libavfilter/buffersink.h>
    #include <libavfilter/buffersrc.h>
    #include "libavutil/avconfig.h"
    #include <libavutil/imgutils.h>
    #include "libswscale/swscale.h"
    };
    #include "opencv2/core.hpp"
    #include<opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    
    class Transdata
    {
    public:
        Transdata();
        ~Transdata();
        AVFormatContext *ifmt_ctx = NULL;
        AVPacket pkt;
        AVFrame *pframe = NULL;
        int ret, i;
        int videoindex=-1;
        AVCodecContext  *pCodecCtx;
        AVCodec         *pCodec;
        const AVBitStreamFilter *buffersrc  =  NULL;
        AVBSFContext *bsf_ctx;
        AVCodecParameters *codecpar = NULL;
        std::string str2= "rtmp://localhost:1935/rtmplive/test";
        int Transdata_init(int num);
        int Transdata_Recdata();
        int Transdata_free();
        void avframeToCvmat(const AVFrame * frame);
        int User_ID;
        string Simg_index;
        pthread_t thread_id;
    };
    
    #endif //VERSION1_0_TRANSDATA_H
    

    遇到的问题

    ①由于要把增加用户ID增加进拉流地址,拉流地址的格式是const char * ,由于ID是整型,这可以把int类型的ID转成string类型,拼接到服务器的string类型IP去,然后再转成const char *,这样比直接在const char *拼接简单,至少我还不知道有什么其他方法。

    例子:

    int num = 5;
    string str2 = {"test"};
    string str3 = to_string(num);
    std::string video_name=str2+str3;
    const char *video_filename = video_name.c_str();//string转const char*
    

    输出为test5

    ②imshow处显示久了会冻结,不更新 这个问题谷歌后,发现有很多这样的问题,opencv里给的官方拉流demo也确实没有考虑这样的问题,出现这个问题是因为如果接收速度太快而显示速度太慢就会矛盾,从而产生问题。实际上处理图片和接收图片最后是放在两个线程里,接收图片一个线程,显示图片一个线程,接收图片后把图片放进一个队列里,显示图片线程就去取,如果接收得太快,显示图片线程发现有队列里有两张图片,那么就丢掉之前的一张,只拿后面一张,这样就不会发生冲突了。 那么opencv里也有这么一个函数,应该是能够实现上面所说,startWindowThread(),官方给的api说明也不够,但是我用上去之后发现确实没问题了。

    原来是这样会出问题:

    while(1)
    {
        RecImage();
        imshow(“test0”,img);
        waitKey(1);
    }
    

    修改后:

    while(1)
    {
        RecImage();
        imshow(“test0”,img);
        startWindowThread();//开启一个线程专门显示图片
        waitKey(1);
    }
    

    ③各种段错误Segmentation Fault问题,千万要记得申请内存,释放内存,出现Segmentation Fault也不要慌张,把你所定义的变量从头到尾检查一遍,基本就能够解决问题了,另外可以使用gdb调试、查看程序开启前和开启后的内存情况,或是增加LOG库,保存日志,从而发现问题。

    注意事项

    CMakelists.txt可以参照我之前的博客,这里就不放了。

    音视频系列2:ffmpeg将H.264解码为RGB 拉远程流需要修改transdata.h里的str字符串。

    需要多少个流可以在for里面修改个数,注意申请、开启、释放的值都要修改。

    可以使用本地推流测试,可参考音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    由于电脑太旧,CPU不行,我这里同时拉取两个流是稳定运行的,但拉取五个流以上就会很卡,但还是能够运行的,所以说,想要拉取多个流,首先要保证电脑性能ok。

    做到这一步,还远远不够,下一步是增加socket,与中转服务器进行通信,拉流之前中转服务器会告诉我谁需要注册,谁需要推流,然后我就做相应的操作啦。

    >如果我的文章对你有帮助,欢迎关注,点赞,评论

    参考文献:

    https://zhuanlan.zhihu.com/p/80350418 http://www.man7.org/linux/man-pages/man3/pthread_create.3.html pthread 文档 https://zhuanlan.zhihu.com/p/38136322

    展开全文
  • nginx+rtmp 推流录制

    千次阅读 2019-07-04 15:59:46
    nginx+rtmp 推流录制 1、环境搭建: https://blog.csdn.net/caowenjing123/article/details/94621301 2、修改nginx.conf 配置文件 在你对应的myapp推流APP下添加record recorder all { record all manual; rec...

    nginx+rtmp 推流录制

    1、环境搭建:
    https://blog.csdn.net/caowenjing123/article/details/94621301
    2、修改nginx.conf 配置文件
    在这里插入图片描述在你对应的myapp推流APP下添加record

     recorder all {
                    record all manual;
                    record_suffix -%Y-%m-%d-%H_%M_%S.flv;
    		on_record_done   http://192.168.42.189:5002/devframe-server/common/on_record_done;
            #        record_unique on;
                    record_path /usr/local/nginx/html/record;
                }
    

    record :all 表示推流的时候就开始录制
    添加上manual 组合标识,下发录制指令start后才开始录制
    on_record_done :录制完成事件,即停止录制的时候触发

    录制指令:
    11 开始录制

    curl "http://192.168.42.24:8002/control/record/start?app=myapp&name=cwj001&rec=all"
    

    12 停止录制

       curl "http://192.168.42.24:8002/control/record/stop?app=myapp&name=cwj001&rec=all"
    

    13、如果录制的文件能够访问必须吧保存录制文件的地址在nginx 中映射出去,所以需要再http中添加一个映射

    	location /record{
    	 alias /usr/local/nginx/html/record;
    	autoindex on;
    	expires -1;
    	add_header Cache-Control no-cache;
    	add_header 'Access-Control-Allow-Origin' '*';
    	add_header 'Access-Control-Allow-Credentials' 'true';
    	add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
     	add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    	}
    

    注:
    app = myapp ,如果application 后面的名字不叫myapp,这里的app 也要相应改变;
    name= 流串秘钥 ,推流的流串名称
    rec =recorder 后面的名称

    如果保存的.flv文件没有流串秘钥名称,检查一下你的nginx worker 的个数,进程多了会存在这种问题

    展开全文
  • 怎么开启 smart_rtmpd 的录像功能

    千次阅读 2019-12-03 09:47:18
    软件下载地址:http://www.qiyicc.com/download/rtmpd.rar ... 其实 smart_rtmpd 不仅支持强大的直播功能,而且还支持录像功能。开启录像功能其实很简单,就是把 url 中的 app 部分变成关键字 ...
  • 用Nginx的RTMP插件建立流媒体服务器。 本文中从服务器的建立到运行的机制,把整在运行的服务上的核心代码贴出,并针对代码介绍服务器的建立方法。
  • ffmpeg命令:ffmpeg rtmp拉流截图、录制

    千次阅读 2020-10-23 12:09:19
    authedkey=ABC23 timeout=1 " -ss 0 -f image2 -vframes 5 imge_%d.jpg # 关键帧截图 ffmpeg -i "rtmp://192.168.1.115:1935/live/camera_152?authedkey=ABC23 timeout=1 " -vf "select='eq(pict_type,PICT_TYPE_I)'...
  • 无损直播录制工具

    2018-03-09 01:26:03
    支持录制所有RTMP方式推流的直播网站,如哔哩哔哩等 Step1. 复制直播流地址到窗口 (直播流地址可以通过Chrome浏览器F12“检查”功能获得,切换到Network选项卡,刷新页面,最长的一条就是) Step2. 输入保存的...
  • EasyDSS推流摄像机
  • 视频文件是多媒体数据中比较常见的一种,也是入门门槛比较高的一个领域。视频数据相关的领域任务包括视频物体检测、视频物体追踪、视频分类、视频检索和视频摘要抽取等。视频数据与图像数据非常类似,都是由像素点...
  • Note: 此文章首发于我的个人博客zhi-hua.wang ,开放转载但需附上出处。使用FFMPEG进行视频转码 - 日落孤城​www.zhi-hua.wangFFMPEG 是一款...以下所有的命令的输入都是《The.Mandalorian.S01E01.INTERNAL.HDR.2160...
  • RTMP流生成MP4文件方法总结

    千次阅读 2019-08-23 22:16:56
    RTMP流生成MP4文件方法总结 Rtmp流生成MP4文件的流程基本上与rtsp流类似,可以参考前一篇RTSP流封装成MP4方法总结 : 这里主要针对rtmp与rtsp不同的地方进行讲解: 1.rtmp流的写法与rtsp流不太一样 如果利用...
  • ZLMediaKit中rtmp部分分析

    千次阅读 2018-11-10 16:16:19
    # rtmp分析 RtmpPlayer播放器类 继承自 PlayerBase(播放器基类,主要处理播放的开始,暂停,停止,相关信息的获取,以及视频显示以及音频播放回调函数的设置) TcpClient(网络连接基类,负责网络连接断开与数据的...
  • RTSP, RTMP协议推流, 以及保存到本地MP4,MKV录像文件。 如果要成功编译, 需要下载和编译ffmpeg库,libfdk-aac库,x264库。 编译这些库,非常耗时。如果懒得去编译, 可以直接使用已经编译好的stream_push.dll...
  • 每天都有很多新老用户咨询我关于RTMP推流摄像头的问题,推流摄像头的配置手册我之前已经写过(RTMP推流摄像头如何接入EasyDSS视频平台),已经持有RTMP推流摄像头的可以按照此手册进行配置。 由于部分用户使用的...
  • ijkplayer源码编译so库,支持rtsp、rtmp视频流播放
  • ffmpeg处理rtmp直播流(截图、收录)

    万次阅读 2014-02-28 11:39:43
    也不知道他是抄过来的还是自己写的,他给了我启发,当然不是抄的启发,而是里面内容确实让我完成了ffmpeg对rtmp直播流截图的处理,之前都有试过用opencv写个程序截图,但是写完发现,在使用载入视频流函数的时间比较...
  • 海康威视视频,视频流 RTSP 转RTMP 。海
  • Window环境下 海康视频RTMP推流方法,使用到的工具。希望能够帮到大家。
  • 公司最近要做溯源,想要手机和电脑都能在线预览视频(B/S),现在有海康威视的硬盘录像机和摄像头共40套左右,全为公网Ip,海康威视的硬盘录像机支持RSTP协议,请问有什么办法把rstp 转成rtmp协议?网上有用 [FFmpeg...
  • 背景 好多开发者在做Android平台RTMP推送对接的同时,除了编码前的数据外,还有些外部编码数据推送诉求,他们希望外部的编码音视频数据不止可以实现RTMP推送,还可以同时在推送端实时录制下来,本文以我们(官方)...
  • ijkplayer 录像 截图功能,支持rtsp rtmp http 流媒体 直接下载Demo看 项目使用 备注:您不需要编译任何东西,只需要下载Demo,拷贝main目录下的 jniLibs包下的所有so依赖库 和app/libs目录下 ijkplayer-java...
  • 在《海康大华等安防摄像机采用通用RTSP协议流转RTMP推送至Web无插件播放展示的流程方法》中,我们分析了整个将安防设备互联网化的主要思路,同时...EasyRTSPLive就是RTSP to RTMP,拉流IPC摄像机或者NVR硬盘录像机R...
  • EasyPlayer-RTMP-Win EasyPlayer RTMP Windows播放器是由开发和维护的一...支持实时录像,立即抓图; 详尽的日志记录输出最小; 最新版本下载 EasyPlayer-RTMP Windows播放器: : 演示效果 EasyPlayer项目体现说明 √
  • 我们在使用流媒体的时候,很多时候需要录像存储。那么涉及到的存储方式,存储控制等方面的问题。如何解决呢 录像存储(天) 可配置永久存储 录像此时不会被删除,永久的被记录在存储中; 可配置存储保留的天数 录像...
  • 之前有博客专门介绍了EasyPlayer的本地录像的功能,简单来说,EasyPlayer是一款RTSP播放器,它将RTSP流里的音视频媒体帧解析出来,并用安卓系统提供的MediaMuxer类进行录像.那EasyPlayerPro可以这样实现吗?答案是不太...
  • 四:支持编码器rtmp/rtsp等协议立即录像或者任务录像录像为标准mp4格式 五:支持本地mp4文件转发成网络流,推送给KMS/FMS/Wowza等流媒体服务器 六:不限转发频道数量,不限推送服务器数量 七:配合KMS流媒体...
  • 好多开发者认为,无论是RTSP/RTMP推送端还是RTSP/RTMP播放端,涉及到录像,只要2个接口足矣:开始录像、停止录像。 实际场景下,一个好的录像模块,2个接口远远不够, 本文以大牛直播SDK(Github)RTSP/RTMP推送和...
  • 很多项目团队都因为推流的需求,咨询了解TSINGSEE青犀视频和海康合作研发的RTMP摄像头。该RTMP摄像头支持语音对讲、主动推流,虽然摄像头没有自行存储的功能,但是可以通过插入...3、类型设置为定时,再设置需要录像
  • 好多开发者认为,无论是RTSP/RTMP推送端还是RTSP/RTMP播放端,涉及到录像,只要2个接口足矣:开始录像、停止录像。 实际场景下,一个好的录像模块,2个接口远远不够, 本文以大牛直播SDK(Github)RTSP/RTMP推送和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,864
精华内容 1,145
关键字:

rtmp录像