FFmpeg-iOS推流器的简单封装

作者: Bison | 来源:发表于2017-07-12 16:45 被阅读270次
    FFmpeg_allluckly.cn.png
    Mac编译ffmpeg获取FFmpeg-iOS
    ffmpeg的H.264解码
    由上俩篇文章,我们已经对ffmpeg有了一定的了解和应用了,接下来让我们一起学习怎么利用ffmpeg推流。
    在推流之前我们需搭建一个本地的nginx推流服务器用来测试。

    主要参考的这篇文章 iOS直播app(推流篇)在这里不做过多的阐述,有兴趣的朋友可以跟着做一做。

    期间我这边遇到的问题:

    Could not symlink share/man/man8/nginx.8
    /usr/local/share/man/man8 is not writable.
    

    只要原因是文件夹系统没有权限导致的,解决方法是文件夹前往/usr/local/share/man/man8文件,显示简介设置系统可读可写即可。

    我这边配置好的nginx推流服务器的配置文件nginx.conf内容为

    
    #user  nobody;
    worker_processes  1;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    
    #pid        logs/nginx.pid;
    
    
    events {
        worker_connections  1024;
    }
    
    
    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;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        server {
            listen       8080;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;
            }
    
            #error_page  404              /404.html;
    
            # redirect server error pages to the static page /50x.html
            #
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    
            # proxy the PHP scripts to Apache listening on 127.0.0.1:80
            #
            #location ~ \.php$ {
            #    proxy_pass   http://127.0.0.1;
            #}
    
            # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
            #
            #location ~ \.php$ {
            #    root           html;
            #    fastcgi_pass   127.0.0.1:9000;
            #    fastcgi_index  index.php;
            #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            #    include        fastcgi_params;
            #}
    
            # deny access to .htaccess files, if Apache's document root
            # concurs with nginx's one
            #
            #location ~ /\.ht {
            #    deny  all;
            #}
        }
    
    
        # another virtual host using mix of IP-, name-, and port-based configuration
        #
        #server {
        #    listen       8000;
        #    listen       somename:8080;
        #    server_name  somename  alias  another.alias;
    
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
    
    
        # HTTPS server
        #
        #server {
        #    listen       443 ssl;
        #    server_name  localhost;
    
        #    ssl_certificate      cert.pem;
        #    ssl_certificate_key  cert.key;
    
        #    ssl_session_cache    shared:SSL:1m;
        #    ssl_session_timeout  5m;
    
        #    ssl_ciphers  HIGH:!aNULL:!MD5;
        #    ssl_prefer_server_ciphers  on;
    
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
        include servers/*;
    
    }
    
    rtmp {
        server {
            listen 1991;
            application liveApp {
                live on;
                record off;
            }
        }
    }
    

    可整个复制进去。得到的推流服务器地址如下

    rtmp://localhost:1991/liveApp/room
    

    如果先前已经做过Mac编译ffmpeg获取FFmpeg-iOS这一步的话,不需要再继续下载ffmpeg。
    下面进入我们的代码阶段即可。

    - (void)pushFlow:(NSString *)input_str output_str:(NSString *)output_rtmpStr{
        char input_str_full[500]={0};
        char output_str_full[500]={0};
        
    //    NSString *input_str= [NSString stringWithFormat:@"resource.bundle/%@",self.input.text];
        NSString *input_nsstr=[[[NSBundle mainBundle]resourcePath] stringByAppendingPathComponent:input_str];
        
        sprintf(input_str_full,"%s",[input_nsstr UTF8String]);
        sprintf(output_str_full,"%s",[output_rtmpStr UTF8String]);
        
        printf("Input Path:%s\n",input_str_full);
        printf("Output Path:%s\n",output_str_full);
        
        AVOutputFormat *ofmt = NULL;
        //Input AVFormatContext and Output AVFormatContext
        AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
        AVPacket pkt;
        char in_filename[500]={0};
        char out_filename[500]={0};
        int ret, i;
        int videoindex=-1;
        int frame_index=0;
        int64_t start_time=0;
        //in_filename  = "cuc_ieschool.mov";
        //in_filename  = "cuc_ieschool.h264";
        //in_filename  = "cuc_ieschool.flv";//Input file URL
        //out_filename = "rtmp://localhost/publishlive/livestream";//Output URL[RTMP]
        //out_filename = "rtp://233.233.233.233:6666";//Output URL[UDP]
        
        strcpy(in_filename,input_str_full);
        strcpy(out_filename,output_str_full);
        
        av_register_all();
        //Network
        avformat_network_init();
        //Input
        if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
            printf( "Could not open input file.");
            goto end;
        }
        if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
            printf( "Failed to retrieve input stream information");
            goto end;
        }
        
        for(i=0; i<ifmt_ctx->nb_streams; i++)
            if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
                videoindex=i;
                break;
            }
        
        av_dump_format(ifmt_ctx, 0, in_filename, 0);
        
        //Output
        
        avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP
        //avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP
        
        if (!ofmt_ctx) {
            printf( "Could not create output context\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
        ofmt = ofmt_ctx->oformat;
        for (i = 0; i < ifmt_ctx->nb_streams; i++) {
            
            AVStream *in_stream = ifmt_ctx->streams[i];
            AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
            if (!out_stream) {
                printf( "Failed allocating output stream\n");
                ret = AVERROR_UNKNOWN;
                goto end;
            }
            
            ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
            if (ret < 0) {
                printf( "Failed to copy context from input to output stream codec context\n");
                goto end;
            }
            out_stream->codec->codec_tag = 0;
            if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
                out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }
        //Dump Format------------------
        av_dump_format(ofmt_ctx, 0, out_filename, 1);
        //Open output URL
        if (!(ofmt->flags & AVFMT_NOFILE)) {
            ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
            if (ret < 0) {
                printf( "Could not open output URL '%s'", out_filename);
                goto end;
            }
        }
        
        ret = avformat_write_header(ofmt_ctx, NULL);
        if (ret < 0) {
            printf( "Error occurred when opening output URL\n");
            goto end;
        }
        
        start_time=av_gettime();
        while (1) {
            AVStream *in_stream, *out_stream;
            //Get an AVPacket
            ret = av_read_frame(ifmt_ctx, &pkt);
            if (ret < 0)
                break;
            //FIX:No PTS (Example: Raw H.264)
            //Simple Write PTS
            if(pkt.pts==AV_NOPTS_VALUE){
                //Write PTS
                AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
                //Duration between 2 frames (us)
                int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
                //Parameters
                pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
                pkt.dts=pkt.pts;
                pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
            }
            //Important:Delay
            if(pkt.stream_index==videoindex){
                AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
                AVRational time_base_q={1,AV_TIME_BASE};
                int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
                int64_t now_time = av_gettime() - start_time;
                if (pts_time > now_time)
                    av_usleep(pts_time - now_time);
                
            }
            
            in_stream  = ifmt_ctx->streams[pkt.stream_index];
            out_stream = ofmt_ctx->streams[pkt.stream_index];
            /* copy packet */
            //Convert PTS/DTS
            pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
            pkt.pos = -1;
            //Print to Screen
            if(pkt.stream_index==videoindex){
                printf("Send %8d video frames to output URL\n",frame_index);
                frame_index++;
            }
            //ret = av_write_frame(ofmt_ctx, &pkt);
            ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
            
            if (ret < 0) {
                printf( "Error muxing packet\n");
                break;
            }
            
            av_packet_unref(&pkt);
            
        }
        //写文件尾(Write file trailer)
        av_write_trailer(ofmt_ctx);
    end:
        avformat_close_input(&ifmt_ctx);
        /* close output */
        if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
            avio_close(ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);
        if (ret < 0 && ret != AVERROR_EOF) {
            printf( "Error occurred.\n");
            return;
        }
        return;
    }
    

    运行app的时候用VLC播放器打开我们的推流地址即可看到推流效果。

    allluckly.cn
    LBffmpegDemo下载地址

    相关文章

      网友评论

        本文标题:FFmpeg-iOS推流器的简单封装

        本文链接:https://www.haomeiwen.com/subject/iwgvhxtx.html