写代码实现直播推流

花满楼原创


小白:你之前介绍过使用nginx来实现直播,使用摄像头来录制,这些知识已经可以做到推流了。

花满楼:之前是使用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;
}

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

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

当然也可以边录制,边推送。

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

这里有一个前提,就是把nginx架设好并启动,可以参考“流媒体服务器,给你好看”这篇文章,它介绍了如何用nginx实现点播与直播。

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

对于h264的知识,或者对于FFmpeg使用的知识,可以参考之前的文章,也可以留意后续的更新。

小白:你说的话好长啊,而且没什么用,我还是看代码吧!

花满楼:如果你有掌握细节的必要,那最好自己写一遍代码。

小白:我只是看看!



写代码实现直播推流

原文地址:http://blog.51cto.com/13136504/2059555

时间: 2024-10-10 04:44:21

写代码实现直播推流的相关文章

不用任何第三方,写一个RTMP直播推流器

2016年是移动直播爆发年,不到半年的时间内无数移动直播App掀起了全民直播的热潮.然而个人觉得直播的门槛相对较高,从推流端到服务端器到播放端,无不需要专业的技术来支撑,仅仅推流端就有不少需要学习的知识.目前大部分直播采用的都是RTMP协议,我这里写一个简单的Demo,帮助大家更好的理解直播推流的过程,主要包括:音视频采集, 音视频编码, 数据打包, RTMP协议等相关的知识等.项目结构分的很清楚,各个模块也用协议进行了分离,方便大家学习不同的模块. 先阐述下推流的整体流程: 建立tcp连接 建

看外国女程序员如何直播写代码

我第一次直播写代码是在去年七月份.想要直播一下我在业余时间内为开源项目领域所做的工作,尽管在youtube上的大部分直播都是关于游戏的.我比较擅长于NodeJS的硬件库方面的工作(尽管大部分项目都是自己的).并且我在youtube上曾经开启过一个房间了,那为什么我不继续做下去?我的栏目可以叫做:基于JavaScript的硬件开发. 当然,我并不是第一个在直播平台直播代码的,Handmade Hero是我见到的第一个直播代码的人.接下来Vlambeer的工程师们也开始了直播代码,他们在youtub

Android流媒体开发之路二:NDK开发Android端RTMP直播推流程序

NDK开发Android端RTMP直播推流程序 经过一番折腾,成功把RTMP直播推流代码,通过NDK交叉编译的方式,移植到了Android下,从而实现了Android端采集摄像头和麦克缝数据,然后进行h264视频编码和aac音频编码,并发送到RTMP服务器,从而实现Android摄像头直播.程序名为NdkRtmpEncoder,在这里把整个过程,和大体框架介绍一下,算是给需要的人引路. 开发思路 首先,为什么要用NDK来做,因为自己之前就已经实现过RTMP推流.RTMP播放.RTSP转码等等各种

基于GPUImage的多滤镜rtmp直播推流

之前做过开源videocore的推流改进:1)加入了美颜滤镜; 2) 加入了librtmp替换原来过于简单的rtmpclient: 后来听朋友说,在videocore上面进行opengl修改,加入新的滤镜比较麻烦,IOS的滤镜最好的要算GPUImage了. 自己就决定将多滤镜开源GPUImage来实现rtmp的直播推流: 实现的方案: 1,GPUImageMovieWriter进行rgba视频数据的输出. 通过重写GPUImageMovieWriter,实现一个代理协议pixelBufferde

Android直播推流学习

Android直播推流学习 Android直播推流学习 第一部 第二部 第三部 第四部 第一部 本文也主要是一步步分析spydroid源码. 首先spydroid的采用的协议是RTSP,目前我知道支持RTSP协议的服务器是Darwin,但是Darwin比较复杂,所以大家可以选择EasyDarwin,大家可以去搜搜看看.还是继续说spydroid吧,spydroid这个项目大家可以在github上搜到的,不过作者也是很久没有更新了,如果大家只做推流的话可以看看原作者的另外一个项目Spydroid.

安卓直播推流SDK

最近整理了Android直播推流SDK,在github上开源出来. 1,支持市面上绝大部分的rtmp服务器 nginx-rtmp,SRS,RED5等 2,视频用软编,兼容性好 市面上的一些android?rtmp推流sdk用的是android中mediacodec来进行,但是有两个缺点: 1,mediacodec这个类在android4.1以后才支持,之前的版本就没法用: 2,mediacodec这个类是硬件编码,需要手机厂家支持,很多厂家支持的情况都不一样,手机的失败是个大的问题. 这里采用软

写代码有这些想法,同事才不会认为你是复制粘贴程序员

前言 最近做完12月份版本需求,有一些思考不够深入的代码,因此写一下总结,希望大家日常写代码多点思考,多点总结,加油!同时哪里有不对的,也望指出. 一.复杂的逻辑条件,是否可以调整顺序,让程序更高效呢. 假设业务需求是这样:会员,第一次登陆时,需要发一条感谢短信.如果没有经过思考,代码直接这样写了 if(isUserVip && isFirstLogin){ sendMsg(); } 假设总共有5个请求,isUserVip通过的有3个请求,isFirstLogin通过的有1个请求. 那么以

会写代码和写好代码的区别

功能: 对一个列表中的字符串小写 代码一: l = ['Hello','World',20,'IBM','Apple'] J = [] for s in l: if isinstance(s,str): s.lower() J.append(s.lower()) else: s J.append(s) print J 代码二: M = ['Hello','World',20,'IBM','Apple'] N = [h.lower() if isinstance(h,str) else h for

Opencv实时眼球追踪,解脱的你双手,让你的眼睛写代码!

Opencv实时眼球追踪,让你的眼睛写代码!这个还是有点对于我现在的追踪效果,还有点距离,但是我想完成这个还是没有问题的,用眼睛去控制电脑打字.我认为只要用手可以做,用眼睛都可以做到,包括游戏.画画.写字等等! 废话不多说,目前追踪率82.5%-98%(戴眼镜和不戴眼镜),目前这个是第一个版本,只是基本上实现了眼球追踪,后面会加上GPU处理提高效率,将目前的face分类器,增加face特征算法以提高face查准率!使实时眼球追踪更加稳定,效率更高,追踪率更高稳定些,适应更多场景!给两个视频连接和