精华内容
参与话题
问答
  • rtmp推流

    2020-08-14 10:15:06
    湖南卫视的rtmprtmp://58.200.131.2:1935/livetv/hunantv,可以在浏览器中直接输入地址,选择potplayer打开即可观看直播

    湖南卫视的rtmp是rtmp://58.200.131.2:1935/livetv/hunantv,可以在浏览器中直接输入地址,选择potplayer打开即可观看直播

    展开全文
  • RTMP推流RTSP视频

    2018-10-14 11:55:18
    RTMP推流
  • Real Time Messaging Protocol(RTMP)即...EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP推流功能组件,经过多年客户实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,支...

    Real Time Messaging Protocol(RTMP)即实时消息传输协议,是 Adobe 公司开发的一个基于 TCP 的应用层协议,目前国内的视频云服务都是以 RTMP 为主要推流协议。

    关于RTMP推流组件

    EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP推流功能组件,经过多年客户实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,支持Windows、Linux、ARM、Android、iOS平台,支持市面上绝大部分的RTMP流媒体服务器。

    EasyRTMP

     

    EasyRTMP-Android介绍屏幕推流及其注意点

    解决问题

    1、录屏

    VirtualDisplay类代表一个虚拟显示器,需要调用DisplayManager 类的 createVirtualDisplay()方法,将虚拟显示器的内容渲染在一个Surface控件上,当进程终止时虚拟显示器会被自动的释放,并且所有的Window都会被强制移除。当不再使用时,需要调用release() 方法来释放资源。

    1)获取MediaProjectionManager实例

    	mMpmngr = (MediaProjectionManager) getApplicationContext()
    					.getSystemService(MEDIA_PROJECTION_SERVICE);
    

    2)在Android 6.0后,Android需要动态获取权限,若没有权限,则不启用该service:

    	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    		if (!Settings.canDrawOverlays(this)) {
    			return;
    		}
    	}
    

    3)MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。

    if (mMpj == null) {
    		mMpj = mMpmngr.getMediaProjection(StreamActivity.mResultCode, 
    												StreamActivity.mResultIntent);
    		StreamActivity.mResultCode = 0;
    		StreamActivity.mResultIntent = null;
    	}
    

    4)通过MediaProjection对象的createVirtualDisplay方法,拿到VirtureDisplay对象,拿这个对象的时候,需要把Surface对象传进去,Surface是由MediaCodec获得。

    	mVirtualDisplay = mMpj.createVirtualDisplay(
    	                "record_screen",
    	                windowWidth,
    	                windowHeight,
    	                screenDensity,
    	                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION,
    	                mSurface,
    	                null,
    	                null);
    

    2、编码及推流

    使用Android硬编码MediaCodec,将VirtualDisplay获取到的视频数据编码后通过EasyRTMP推送出去。

    1)初始化MediaCodec,同时获取Surface对象,并启动编码器

    	mMediaCodec = MediaCodec.createByCodecName(ci.mName);
    	MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 
    												windowWidth, windowHeight);
    	mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
    	mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
    	mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, 
    					MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    	mediaFormat.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 
    					20000000);
    	mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
    	mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
    	mMediaCodec.configure(mediaFormat, null, null, 
    							MediaCodec.CONFIGURE_FLAG_ENCODE);
    	// 获取Surface对象
    	mSurface = mMediaCodec.createInputSurface();
    	mMediaCodec.start();
    

    2)音频编码的工作直接使用AudioStream,

    	final AudioStream audioStream = 
    		AudioStream.getInstance(EasyApplication.getEasyApplication(), 
    	SPUtil.getEnableAudio(EasyApplication.getEasyApplication()));
    

    3)启动编码线程,不停的将获取的音频帧和视频帧,编码并推流。详情见代码注释:

    	// 启动线程,
    	mPushThread.start();
    	// 初始化推流器
    	mEasyPusher = new EasyRTMP(mHevc ? EasyRTMP.VIDEO_CODEC_H265 : 
    								EasyRTMP.VIDEO_CODEC_H264, RTMP_KEY);
    	// 视频的硬编码
    	ByteBuffer outputBuffer;
    	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    		outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
    	} else {
    		outputBuffer = outputBuffers[outputBufferIndex];
    	}
    	outputBuffer.position(bufferInfo.offset);
    	outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
    	try {
    		boolean sync = false;
    		if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
    			sync = (bufferInfo.flags&MediaCodec.BUFFER_FLAG_SYNC_FRAME)!=0;
    			if (!sync) {
    				byte[] temp = new byte[bufferInfo.size];
    				outputBuffer.get(temp);
    				mPpsSps = temp;
    				continue;
    			} else {
    				mPpsSps = new byte[0];
    			}
    		}
    		sync |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
    		int len = mPpsSps.length + bufferInfo.size;
    		if (len > h264.length) {
    			h264 = new byte[len];
    		}
    		
    	// 将编码后的视频数据发送出去
    	if (sync) {
    		System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
    		outputBuffer.get(h264, mPpsSps.length, bufferInfo.size);
    		mEasyPusher.push(h264, 0, mPpsSps.length + bufferInfo.size, bufferInfo.presentationTimeUs / 1000, 2);
    	} else {
    		outputBuffer.get(h264, 0, bufferInfo.size);
    		mEasyPusher.push(h264, 0, bufferInfo.size, 		bufferInfo.presentationTimeUs / 1000, 1);
    	}
    展开全文
  • Android摄像头RTMP推流

    2018-12-31 22:11:11
    使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考. 使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考.
  • 本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/74783269需求在做EasyDSS开发时,总是在测试推流...以及其他RTMP推流工具。但是,别忘了,还有ffmpeg这个神器。ffmpeg可以获

    需求

    在做EasyDSS开发时,总是在测试推流效果。

    当然,可以使用EasyRTMP进行推流测试。

    有时候,我们想使用OBS进行推流测试,也可以,这很好。

    以及其他RTMP推流工具。

    但是,别忘了,还有ffmpeg这个神器。ffmpeg可以获取各种视频流,并推送给EasyDSS RTMP流媒体服务器。

    比如,拉取摄像机RTSP流,推送给EasyDSS,命令在此

     ffmpeg -i rtsp://username:password@ip:port/xxxxxxx -vcodec copy -acodec copy -f flv -y rtmp://easydssip:easydssport/live/test
    

    当然,这个命令限于摄像机提供的就为H.264+AAC的码流,若不是

    则将-vcodec copy改为-vcodec libx264-acodec copy改为-acodec aac

    若是,我们想用ffmpeg读取文件进行RTMP推送呢,当然,您可以选择OBS,但是,毕竟,ffmpeg比OBS更轻量,命令在此

     ffmpeg -i test.h264 -vcodec copy -acodec copy -f flv -y rtmp://easydssip:easydssport/live/test
    

    -vcodec和-acodec按需更改同拉取RTSP流。

    这样可以满足我们推送文件到EasyDSS的愿望。

    但是。。。。。

    ffmpeg推流的效果经常不如人意,经常出现播放画面慢、音视频不同步、HLS直播不稳定等各种问题?

    解决

    当然有解决办法

     ffmpeg -re  -stream_loop -1 -i test.h264 -vcodec copy -acodec copy -f flv -y rtmp://easydssip:easydssport/live/test
    

    加了一个-re

    这是干啥的?

    https://ffmpeg.org/ffmpeg-all.html#Main-options

    我们明白了,-re表示重新调整时间戳,这样就能够将各种文件、RTSP源、RTMP源的不均匀时间戳全部进行ffmpeg的重新调整,再进行rtmp推流,保证直播的平滑和hls切片的均匀。

    另外,在拉取RTSP流时,尽量加上 -rtsp_transport tcp 的选项,保证数据源的稳定和不丢包!

    EasyDSS流媒体服务器推荐

    EasyDSS商用流媒体服务器提供一站式的转码、点播、直播、时移回放服务,极大地简化了开发和集成的工作,并且EasyDSS支持多种特性,完全能够满足企业视频信息化建设方面的需求:

    ①多屏播放:支持Flash、HTML5播放,兼容Windows、Android、iOS、Mac等操作系统。

    ②自由组合:EasyDSS软件产品之间无缝对接,也可将EasyDSS流媒体服务器软件与其他第三方平台对接,组合灵活自由。

    ③支持云架构:支持阿里云、腾讯云、华为云、青云、七牛云存储等各大云服务商,支持云架构,部署更灵活。

    ④与CDN无缝对接:EasyDSS软件产品支持与网宿、帝联、蓝汛等CDN无缝对接。

    ⑤二次开发简单:提供编程语言无关化的RESTful API接口,只要了解JS、HTML、JAVA、ASP.NET、PHP、ASP等开发语言中的任意一种,就能运用EasyDSS提供的RESTful API进行二次开发。

    ⑥简单易用:图形化操作,模块清晰,流程简单,极易上手。

    EasyDarwin如何支持点播和RTMP/HLS直播?EasyDSS!

    更多EasyDSS高级功能:www.easydss.com

    更多流媒体音视频资源

    EasyDarwin开源流媒体服务器:www.EasyDarwin.org

    EasyDSS高性能互联网直播服务:www.EasyDSS.com

    EasyNVR安防视频可视化服务:www.EasyNVR.com

    EasyNVS视频综合管理平台:www.EasyNVS.com

    EasyNTS云组网:www.EasyNTS.com

    EasyGBS国标GB/T28181服务器:www.EasyGBS.com

    EasyRTC视频会议解决方案:www.EasyRTC.cn

    Copyright © TSINGSEE.com Team 2012-2019

    青犀TSINGSEE

    展开全文
  • RTMP推流器demo

    2020-03-13 04:40:35
    rtmp推流器 很小的一个推流器 直接加到项目就可以使用 很不错 推荐一下
  • ffmpeg rtmp推流源码

    2019-08-07 09:34:03
    一份可以直接将本地视频推送到服务器的推流源代码, 是学习推流的很好的资料, 代码基于ffmpeg rtmp推流, 采用nginx+rtmp module搭建本地流媒体服务器
  • 安卓rtmp推流工具

    2019-05-10 11:00:41
    安卓app,rtmp推流工具,
  • linux环境rtmp推流

    2018-10-18 14:29:33
    linux环境,基于rtmp推流源代码,源文件可以下载参考学习
  • 在三月份接到了这样一个任务,需要通过USB摄像头采集数据之后,放入6818进行硬件编码后,再通过FFMPEG进行RTMP推流。因为对于ffmpeg并不是非常的了解,加上中间偷了一段时间的懒,直到最近才完成初步的工作。 在这里...

    相关前期准备:

    1.RMTP推流服务器建立
    2.S5P6818平台硬件编码
    3.FFMPEG USB摄像头数据采集

    在三月份接到了这样一个任务,需要通过USB摄像头采集数据之后,放入6818进行硬件编码后,再通过FFMPEG进行RTMP推流。因为对于ffmpeg并不是非常的了解,加上中间偷了一段时间的懒,直到最近才完成初步的工作。

    在这里为了方便直接使用了一些QT的东西,并且通过修改Makefile兼容了一些平台编译的问题。
    我这里提前移植QT4.8.6和FFMPEG4.0.2

    (一)FFMPEG进行RTMP推流相关代码

    ffmpeg.cpp

    #include "ffmpeg.h"
    #include "Vpu.h"
    
    extern "C"
    {
        #include "libavcodec/avcodec.h"
        #include "libavformat/avformat.h"
        #include "libswscale/swscale.h"
        #include "libavdevice/avdevice.h"
    }
    
    //#define FFMPEG_MJPEG
    //#define FFMPEG_H264
    #define FFMPEG_YUV
    
    #define TIMEMS      qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
    
    
    
    static double r2d(AVRational r)
    {
        return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
    }
    
    ffmpeg::ffmpeg(QWidget *parent) :
        QThread(parent)
    {
    	width=640;
    	height=480;
    	Fps=30;
    }
    
    
    ffmpeg::~ffmpeg()
    {
    }
    
    void ffmpeg::YUYV_to_YUV420P(char * image_in, char* image_out, int inwidth, int inheight)
    {
        AVFrame  *frmyuyv = av_frame_alloc();
        AVFrame  *frm420p = av_frame_alloc();
    
        av_image_fill_arrays(frmyuyv->data, frmyuyv->linesize, (uint8_t*)image_in, AV_PIX_FMT_YUYV422, inwidth, inheight, 16);
        av_image_fill_arrays(frm420p->data, frm420p->linesize, (uint8_t*)image_out, AV_PIX_FMT_YUV420P, inwidth, inheight, 16);
    
        struct SwsContext *sws = sws_getContext(inwidth, inheight, AV_PIX_FMT_YUYV422, inwidth,inheight, AV_PIX_FMT_YUV420P,
                                                SWS_BILINEAR, NULL, NULL, NULL);
    
        int ret = sws_scale(sws, frmyuyv->data, frmyuyv->linesize, 0, inheight, frm420p->data, frm420p->linesize);
    
    	image_out=(char*)frm420p->data;
        av_frame_free(&frmyuyv);
        av_frame_free(&frm420p);
        sws_freeContext(sws);
    }
    
    int ffmpeg::GetSpsPpsFromH264(uint8_t* buf, int len)
    {
    	int i = 0;
    	for (i = 0; i < len; i++) {
    		if (buf[i+0] == 0x00 
    			&& buf[i + 1] == 0x00
    			&& buf[i + 2] == 0x00
    			&& buf[i + 3] == 0x01
    			&& buf[i + 4] == 0x06) {
    			break;
    		}
    	}
    	if (i == len) {
    		printf("GetSpsPpsFromH264 error...");
    		return 0;
    	}
    
    	printf("h264(i=%d):", i);
    	for (int j = 0; j < i; j++) {
    		printf("%x ", buf[j]);
    	}
    	return i;
    }
    
    bool ffmpeg::isIdrFrame2(uint8_t* buf, int len)
    {
    	switch (buf[0] & 0x1f) {
    	case 7: // SPS
    		return true;
    	case 8: // PPS
    		return true;
    	case 5:
    		return true;
    	case 1:
    		return false;
    
    	default:
    		return false;
    		break;
    	}
    	return false;
    }
    
    bool ffmpeg::isIdrFrame1(uint8_t* buf, int size)
    {
    	int last = 0;
    	for (int i = 2; i <= size; ++i) {
    		if (i == size) {
    			if (last) {
    				bool ret = isIdrFrame2(buf + last, i - last);
    				if (ret) {
    					return true;
    				}
    			}
    		}
    		else if (buf[i - 2] == 0x00 && buf[i - 1] == 0x00 && buf[i] == 0x01) {
    			if (last) {
    				int size = i - last - 3;
    				if (buf[i - 3]) ++size;
    				bool ret = isIdrFrame2(buf + last, size);
    				if (ret) {
    					return true;
    				}
    			}
    			last = i + 1;
    		}
    	}
    	return false;
    }
    
    int ffmpeg::RtmpInit(void* spspps_date, int spspps_datalen)
    {
        RtmpULR="rtmp://192.168.2.101:1935/live/livestream";
    	int ret = 0;
    	AVStream *out_stream;
    	AVCodecParameters *out_codecpar;
    	avformat_network_init();
    	avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", NULL);// out_filename);
    	if (!ofmt_ctx) {
    		fprintf(stderr, "Could not create output context\n");
    		ret = AVERROR_UNKNOWN;
    	}
    	ofmt = ofmt_ctx->oformat;
    
    	out_stream = avformat_new_stream(ofmt_ctx, NULL);
    	if (!out_stream) {
    		fprintf(stderr, "Failed allocating output stream\n");
    		ret = AVERROR_UNKNOWN;
    	}
    	stream_index = out_stream->index;
    
    	//因为输入是内存读出来的一帧帧的H264数据,所以没有输入的codecpar信息,必须手动添加输出的codecpar
    	out_codecpar = out_stream->codecpar;
    	out_codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    	out_codecpar->codec_id = AV_CODEC_ID_H264;
    	out_codecpar->bit_rate = 400000;
    	out_codecpar->width = width;
    	out_codecpar->height = height;
    	out_codecpar->codec_tag = 0;
    	out_codecpar->format = AV_PIX_FMT_YUV420P;
    
    	//必须添加extradata(H264第一帧的sps和pps数据),否则无法生成带有AVCDecoderConfigurationRecord信息的FLV
    	//unsigned char sps_pps[26] = { 0x00, 0x00, 0x01, 0x67, 0x4d, 0x00, 0x1f, 0x9d, 0xa8, 0x14, 0x01, 0x6e, 0x9b, 0x80, 0x80, 0x80, 0x81, 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x3c, 0x80 };
    	out_codecpar->extradata_size = spspps_datalen;
    	out_codecpar->extradata = (uint8_t*)av_malloc(spspps_datalen + AV_INPUT_BUFFER_PADDING_SIZE);
    	if (out_codecpar->extradata == NULL)
    	{ 
    		printf("could not av_malloc the video params extradata!\n");
    	}
    	memcpy(out_codecpar->extradata, spspps_date, spspps_datalen);	
    	av_dump_format(ofmt_ctx, 0, RtmpULR, 1);
    	if (!(ofmt->flags & AVFMT_NOFILE)) {
    		ret = avio_open(&ofmt_ctx->pb, RtmpULR, AVIO_FLAG_WRITE);
    		if (ret < 0) {
    			fprintf(stderr, "Could not open output file '%s'", RtmpULR);
    		}
    	}
    	AVDictionary *opts = NULL;
    	av_dict_set(&opts, "flvflags", "add_keyframe_index", 0);
    	ret = avformat_write_header(ofmt_ctx, &opts);
    	av_dict_free(&opts);
    	if (ret < 0) {
    		fprintf(stderr, "Error occurred when opening output file\n");
    	}
    
    	waitI = 1;
    	return 0;
    }
    
    // void ffmpeg::VideoWrite(void* data, int datalen)
    // {
    // 	int ret = 0, isI = 0;
    // 	AVRational r = { 10, 1 };
    // 	AVPacket pkt;
    // 	out_stream = ofmt_ctx->streams[videoStreamIndex];
    // 	av_init_packet(&pkt);
    // 	isI = isIdrFrame1((uint8_t*)data, datalen);
    // 	pkt.flags |= isI ? AV_PKT_FLAG_KEY : 0;
    // 	pkt.stream_index = avDePacket->stream_index;
    // 	pkt.data = (uint8_t*)data;
    // 	pkt.size = datalen;
    // 	//AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。
    // 	AVRational time_base1 = in_stream->time_base;
    // 	printf("time_base1:{%d,%d}",in_stream->time_base.num,in_stream->time_base.den);
    // 	//计算两帧之间的时间
    // 	int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(r);
    // 	//配置参数
    // 	pkt.pts = (double)((framIndex+1)*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
    // 	printf("{%d %d %d,%d}\n",framIndex,calc_duration, pkt.pts,av_q2d(time_base1));	
    // 	pkt.dts =pkt.pts ;
    // 	pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
    
    
    // 	AVRational time_base = in_stream->time_base;
    // 	AVRational time_base_q = { 1,AV_TIME_BASE };
    // 	//计算视频播放时间
    // 	int64_t pts_time = av_rescale_q(pkt.pts, time_base, time_base_q); 
    // 	//计算实际视频的播放时间
    // 	int64_t now_time = av_gettime() - start_time;
    // 	printf("pts_time:%d\n", pts_time);	
    // 	printf("now_time:%d\n", now_time);
    // 	AVRational avr = in_stream->time_base;
    // 	// printf("avr.num:%d, avr.den:%d, pkt.dts:%ld, pkt.pts:%ld, pts_time:%ld\n",
    // 	// 		avr.num,    avr.den,    pkt.dts,     pkt.pts,     pts_time);
    // 	if (pts_time > now_time)
    // 	{
    // 		//睡眠一段时间(目的是让当前视频记录的播放时间与实际时间同步)
    // 	printf("pts_time:%d\n", pts_time);	
    // 	printf("now_time:%d\n", now_time);
    // 		av_usleep((unsigned int)(pts_time - now_time));
    // 	}
    
    // 	//计算延时后,重新指定时间戳
    // 	pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    // 	pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    // 	pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    // 	//字节流的位置,-1 表示不知道字节流位置,由程序自行探测
    // 	pkt.pos = -1;
    
    // 	printf("avr.num:%d, avr.den:%d, pkt.dts:%ld, pkt.pts:%ld, pts_time:%ld\n",
    // 			avr.num,    avr.den,    pkt.dts,     pkt.pts,     pts_time);
    
    // 	ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    // 	if (ret < 0)
    // 	{
    // 		printf("发送数据包出错\n");
    // 	}
    // 		av_free_packet(&pkt);
    // }
    
    void ffmpeg::VideoWrite(void* data, int datalen)
    {
    	int ret = 0, isI = 0;
    	AVPacket pkt;
    	out_stream = ofmt_ctx->streams[stream_index];
    	int calc_duration;
    	av_init_packet(&pkt);
    	isI = isIdrFrame1((uint8_t*)data, datalen);
    	pkt.flags |= isI ? AV_PKT_FLAG_KEY : 0;
    	pkt.stream_index = out_stream->index;
    	pkt.data = (uint8_t*)data;
    	pkt.size = datalen;
    	//wait I frame
    	if (waitI) {
    		if (0 == (pkt.flags & AV_PKT_FLAG_KEY))
    			return;
    		else
    			waitI = 0;	
    	}
    	
    	AVRational time_base1=ifmt_ctx->streams[stream_index]->time_base;
    	//Duration between 2 frames (us)
    	calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[stream_index]->r_frame_rate)-2000;
    	//Parameters
    	pkt.pts=((framIndex+1)*calc_duration)/(av_q2d(time_base1)*AV_TIME_BASE);
    	pkt.dts=pkt.pts;
    	pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
    	pkt.pos=-1;
    	// printf("instream{%d,%d}\n",ifmt_ctx->streams[stream_index]->time_base.num,ifmt_ctx->streams[stream_index]->time_base.den);
    	// printf("outstream{%d,%d}\n",ofmt_ctx->streams[stream_index]->time_base.num,ofmt_ctx->streams[stream_index]->time_base.den);
    	 printf("DURATION :%d\n",calc_duration);
    	printf("PTS DTS :%d %d\n",pkt.pts,pkt.dts);
    	
    	//计算延时后,重新指定时间戳
    	pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    	pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    	pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    	//字节流的位置,-1 表示不知道字节流位置,由程序自行探测
    	pkt.pos = -1;
    
    	gettimeofday(&stamp, NULL);
    	int now_time = 1000*1000*(stamp.tv_sec)+stamp.tv_usec-start_time;
    	printf("start_time now_time:%d %d\n",start_time, now_time);
    	if (pkt.dts > now_time)
    	av_usleep(pkt.dts - now_time);
    	
    
    
    	ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    	if (ret < 0) {
    		fprintf(stderr, "Error muxing packet\n");
    	}
    
    	av_packet_unref(&pkt);
    }
    
    void ffmpeg::RtmpUnit(void)
    {
    	if (ofmt_ctx)
    		av_write_trailer(ofmt_ctx);
    	/* close output */
    	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
    		avio_closep(&ofmt_ctx->pb);
    	if (ofmt_ctx) {
    		avformat_free_context(ofmt_ctx);
    		ofmt_ctx = NULL;
    	}
    }
    
    
    // void ffmpeg::YUYV_to_NV12( char * image_in, char* image_out, int inwidth, int inheight)
    // {
    //        /* 计算循环次数,YUYV 一个像素点占2个字节*/
    // 	   int pixNUM = inwidth * inheight;
    // 	   unsigned int cycleNum = 1;
    	   
    // 	  /*单帧图像中 NV12格式的输出图像 Y分量 和 UV 分量的起始地址,并初始化*/
    // 	  char *y = image_out;
    // 	  char *uv = image_out + pixNUM ;
    	 
    // 	  char *start = image_in;
    //           unsigned int i =0; 
    // 	  int j =0,k =0;
    	  
    // 	  /*处理Y分量*/
    // 	  for(i= 0; i<cycleNum ;i++)
    // 	  {
    // 		int index =0;
    // 		for(j =0; j< pixNUM*2; j=j+2) //YUYV单行中每两个字节一个Y分量
    // 		{
    // 			*(y+index) = *(start + j);
    // 			index ++;
    // 		}
    // 		start = image_in + pixNUM*2*i;
    // 		y= y + pixNUM*3/2;
    // 	  }
          
    //       /**处理UV分量**/
    // 	  start = image_in;
    // 	  for(i= 0; i<cycleNum ;i++)
    // 	  {
    // 	    int uv_index = 0;
    // 		for(j=0; j< inheight; j =j+2)  // 隔行, 我选择保留偶数行
    // 		{
    // 			for(k = j*inwidth*2+1; k< inwidth*2*(j+1); k=k+4) //YUYV单行中每四个字节含有一对UV分量
    // 			{
    // 				*(uv+ uv_index) = *(start + k);
    // 				*(uv +uv_index+1) = *(start +k +2);
    // 				uv_index += 2;
    // 			}
    // 		}
    // 	    start = image_in + pixNUM*2*i;
    // 	    uv =uv + pixNUM*3/2;
    // 	  }	
    //  }
    
    
    void ffmpeg::YUV420PtoNV12(unsigned char* Src, unsigned char* Dst,int Width,int Height){
        unsigned char* SrcU = Src + Width * Height;
        unsigned char* SrcV = SrcU + Width * Height / 4 ;
        memcpy(Dst, Src, Width * Height);
        unsigned char* DstU = Dst + Width * Height;
        for(int i = 0 ; i < Width * Height / 4 ; i++ ){
            ( *DstU++) = ( *SrcU++);
            ( *DstU++) = ( *SrcV++);
        }
    }
    
    
    /* 功能:初始化video mjpeg to yuv
     *      1 video
     *      2 解码
     * 参数:无
     * 返回值:成功返回零,失败返回-1
     */
    int ffmpeg::initDecodeVideo()
    {
    	MJPEGPath=fopen("out.mpg","wb");
        H264Path = fopen(outputFilename, "wb"); 
    	YUVPath = fopen("out.yuv","wb");
    	 NV12Path= fopen("out.nv12","wb");
        //为解封装上下文开辟空间
        ifmt_ctx = avformat_alloc_context();   
        framIndex=0;
        /*1、注册*/  
        avcodec_register_all();  
        avdevice_register_all(); 
        qDebug() << TIMEMS << "init ffmpeg lib ok" << " version:" << FFMPEG_VERSION;
         /*2、连接视频源*/   
        AVInputFormat *inputFmt  = av_find_input_format("video4linux2");
        AVDictionary *options = NULL;
    	
        //打开输入视频流,进行解封装
        av_dict_set(&options, "framerate", "30", 0);
    	char videosize[9];
    	sprintf(videosize,"%dx%d",width,height);
        av_dict_set(&options, "video_size", videosize, 0);
    
    #ifdef FFMPEG_MJPEG
        av_dict_set(&options, "input_format", "mjpeg", 0);
    #endif    
    
    #ifdef FFMPEG_YUV
        av_dict_set(&options, "input_format", "yuyv422", 0);
    #endif  	
    	int result = avformat_open_input(&ifmt_ctx, inputFilename, inputFmt, &options);
        if (result < 0) {
            qDebug() << TIMEMS << "open input error" << inputFilename;
            return false;
        }
        //释放设置参数
        if(options != NULL) {
            av_dict_free(&options);
        }
        //获取流信息
        result = avformat_find_stream_info(ifmt_ctx, NULL);
        if (result < 0) {
            qDebug() << TIMEMS << "find stream info error";
            return false;
        }
        avDePacket = av_packet_alloc();
        avDeFrameYuv = av_frame_alloc();
        videoStreamIndex = -1;
        videoStreamIndex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &deCodec, 0);
        if (videoStreamIndex < 0) {
            qDebug() << TIMEMS << "find video stream index error";
            return false;
        }
    	printf("videoindex:%d\n", videoStreamIndex);
    
        //从输入封装上下文获取输入视频流
        in_stream = ifmt_ctx->streams[videoStreamIndex];
        if (!in_stream)
        {
            printf("Failed get input stream\n");
            return false;
        }
            //获取视频流解码器上下文
        deCodecCtx = in_stream->codec;
    
        //获取分辨率大小
        videoWidth = in_stream->codec->width;
        videoHeight = in_stream->codec->height;
    
        //如果没有获取到宽高则返回
        if (videoWidth == 0 || videoHeight == 0) {
            qDebug() << TIMEMS << "find width height error";
            qDebug() <<"WIDTH"<<videoWidth<<":"<<"HEIGHT"<<videoHeight;
            return false;
        }
    
        //获取视频流的帧率 fps,要对0进行过滤,除数不能为0,有些时候获取到的是0
        int num = in_stream->codec->framerate.num;
        int den = in_stream->codec->framerate.den;
        if (num != 0 && den != 0) {
            videoFps = num / den ;
        }
    
    
        QString videoInfo = QString("视频流信息 -> 索引: %1   格式: %2  时长: %3 秒  fps: %4  分辨率: %5*%6 instream->time_base:%7 %8 in_stream->r_frame_rate: %9 %10")
                            .arg(videoStreamIndex).arg(ifmt_ctx->iformat->name)
                            .arg((ifmt_ctx->duration) / 1000000).arg(videoFps).arg(videoWidth).arg(videoHeight).arg(in_stream->time_base.num).arg(in_stream->time_base.den).arg(in_stream->r_frame_rate.num).arg(in_stream->r_frame_rate.den);
        qDebug() << TIMEMS << videoInfo;
        //打开视频解码器
        result = avcodec_open2(deCodecCtx, deCodec, NULL);
        if (result < 0) {
            qDebug() << TIMEMS << "open video codec error";
            return false;
        }
        AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
    
    	#ifdef FFMPEG_YUV
    		srcFormat = AV_PIX_FMT_YUYV422;
    	#endif
    
        return 0;
    }
    
    
    
    int ffmpeg::playVideo()
    {
        int length;
        int got_packet;
        initDecodeVideo();
    
        vpu Vpu(width,height,Fps);
        //WRITE HERDER
        fwrite( Vpu.seqBuffer, 1, Vpu.size, H264Path );
        RtmpInit(Vpu.seqBuffer, Vpu.size);
        nv12=(char*)malloc(width*height*3/2);
        yuv420p=(char*)malloc(width*height*3/2);
    
    	gettimeofday(&stamp, NULL);
    	start_time=(stamp.tv_sec)*1000*1000+stamp.tv_usec;
    
        while(true)
        {
            if (av_read_frame(ifmt_ctx, avDePacket) >= 0) { 
                                   
                YUYV_to_YUV420P(( char*)avDePacket->data,( char*)yuv420p,width,height);
                h264=Vpu.DecodeNV12_To_H264(yuv420p,&length);
                VideoWrite(h264, length);
    			
    			// fwrite(avDePacket->data,avDePacket->size,1,YUVPath);
    			// fwrite(h264,length,1,H264Path);
    			// fwrite(yuv420p,width*height*3/2,1,NV12Path);
                
    			// printf("[H264 PACKET LENGTH]=%d\n",length);  
                // qDebug()<< "解码到第" << framIndex << "帧";
                // qDebug()<<"PCAKET SIZE="<<avDePacket->size;
                // qDebug() << TIMEMS;           
                av_packet_unref(avDePacket);
                av_freep(avDePacket);
            }
            framIndex++;
        }
    	RtmpUnit();
        avformat_free_context(ifmt_ctx);
        qDebug() << TIMEMS << "stop ffmpeg thread";
    }
    
    
    void ffmpeg::run()
    {
        playVideo();
    }
    
    
    

    ffmpeg.h

    #ifndef FFMPEG_H
    #define FFMPEG_H
    
    #include <QMainWindow>
    #include <QMutex>
    #include <QDateTime>
    #include <QFile>
    #include <QThread>
    #include <QDebug>
    #include <stdio.h>
    #include <string>
    #include <iostream>
    #include <chrono>
    #include <vector>
    //引入ffmpeg头文件
    extern "C" {
    #include "libavutil/opt.h"
    #include "libavutil/time.h"
    #include "libavutil/frame.h"
    #include "libavutil/pixdesc.h"
    #include "libavutil/avassert.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/ffversion.h"
    #include "libavcodec/avcodec.h"
    #include "libswscale/swscale.h"
    #include "libavdevice/avdevice.h"
    #include "libavformat/avformat.h"
    #include "libavfilter/avfilter.h"
    #include "libavutil/hwcontext.h"
    #include "libavutil/avutil.h"
    #include "libavutil/opt.h"
    }
    
    
    class ffmpeg : public QThread
    {
        Q_OBJECT
    public:
        explicit ffmpeg(QWidget *parent = NULL);
        ~ffmpeg();
        char *outputFilename;
        char *inputFilename;
        struct timeval stamp;
        int start_time;
        
    protected:
        void run();
    signals:
        //收到图片信号
        void receiveImage(const QImage &image);
    
    private:
    
        AVFormatContext *fmtCtx = NULL;;
        int framIndex;
        uint8_t *buffer;                    //存储解码后图片buffer
        AVFrame *avDeFrameYuv;              //解码帧对象YUV
        AVCodec *deCodec = NULL;            //解码器
    	AVCodec *pCodecH264; //编码器
    
        AVPacket *avDePacket;               //解码包对象
        AVPacket avpkt;
        int frameFinish = 0;
        int stream_index;
        FILE* MJPEGPath;
        FILE* YUVPath;
        FILE* H264Path;
        FILE* NV12Path;
        int WIDTH,HEIGHT,FRAME;
        int videoStreamIndex;               //视频流索引
    	//输入输出视频流
    	AVStream *out_stream;
        AVStream *in_stream;                //输入视频流
        AVCodecContext *c= NULL;
        AVCodecContext *deCodecCtx;         //解码器上下文
        int videoWidth;                     //视频宽度
        int videoHeight;                    //视频高度
        int videoFps;
    	uint8_t * outbuf;
        int outbuf_size;
        int got_packet_ptr;
        char* nv12;
        char *h264;
        char* yuv420p;
        char* RtmpULR;
        AVFormatContext *ifmt_ctx;
        AVOutputFormat *ofmt = NULL;
        AVFormatContext *ofmt_ctx = NULL;
        int waitI,rtmpisinit;
        int ptsInc=0;
        int width;
        int height;
        int Fps;
    
    private:
        void YUV420PtoNV12(unsigned char *Src, unsigned char* Dst,int Width,int Height);
        void YUYV_to_YUV420P( char * image_in, char* image_out, int width, int height); 
        int initDecodeVideo();
        int playVideo();
        int RtmpInit(void* spspps_date, int spspps_datale);
        int GetSpsPpsFromH264(uint8_t* buf, int len);
        bool isIdrFrame2(uint8_t* buf, int len);
        bool isIdrFrame1(uint8_t* buf, int size);
        void VideoWrite(void* data, int datalen);
        void RtmpUnit(void);
    };
    
    #endif // FFMPEG_H
    
    

    (二)运行中遇到的问题

    其他初始化的想关的准备中并没有遇到严重的问题。但是在推流的过程中遇到了三个非常严重的问题。

    (1)时间戳

    在其他的代码中经常会使用av_gettime()获取时间戳,但是非常奇怪的是,我在使用相关函数的时候并不能成功获取时间戳。最后不得已采用
    gettimeofday(&stamp, NULL);
    int now_time = 1000 * 1000 * (stamp.tv_sec)+stamp.tv_usec-start_time;
    获取相关的时间戳。

    (2)推流包参数设置的问题

    打印数据发现,输入和输出具有完全不同的时间基,需要通过一个av_rescale_q_rnd函数重新计算时间PTS DTS参数

    (3)推流和播放无法同步

    这是一个非常麻烦的问题,到目前都没有找到非常好的解决方法。也就是实际时间与播放时间有1到2秒的时间差,无法同步。这个应该不是代码的原因。即使源码推流好像也存在这样的问题。
    我想到了一个这样的办法,通过稍微减小两帧之间的时间可以让这样的问题得到缓解。但是,也有一个问题就是每过一段时间会稍微卡一下。

    
    void ffmpeg::VideoWrite(void* data, int datalen)
    {
    	int ret = 0, isI = 0;
    	AVPacket pkt;
    	out_stream = ofmt_ctx->streams[stream_index];
    	int calc_duration;
    	av_init_packet(&pkt);
    	isI = isIdrFrame1((uint8_t*)data, datalen);
    	pkt.flags |= isI ? AV_PKT_FLAG_KEY : 0;
    	pkt.stream_index = out_stream->index;
    	pkt.data = (uint8_t*)data;
    	pkt.size = datalen;
    	//wait I frame
    	if (waitI) {
    		if (0 == (pkt.flags & AV_PKT_FLAG_KEY))
    			return;
    		else
    			waitI = 0;	
    	}
    	
    	AVRational time_base1=ifmt_ctx->streams[stream_index]->time_base;
    	
    //Duration between 2 frames (us)
    //通过减少两帧之间的时间缓解时间差的问题	
    calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[stream_index]->r_frame_rate)-2000;
    
    
    	//Parameters
    	pkt.pts=((framIndex+1)*calc_duration)/(av_q2d(time_base1)*AV_TIME_BASE);
    	pkt.dts=pkt.pts;
    	pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
    	pkt.pos=-1;
    	// printf("instream{%d,%d}\n",ifmt_ctx->streams[stream_index]->time_base.num,ifmt_ctx->streams[stream_index]->time_base.den);
    	// printf("outstream{%d,%d}\n",ofmt_ctx->streams[stream_index]->time_base.num,ofmt_ctx->streams[stream_index]->time_base.den);
    	 printf("DURATION :%d\n",calc_duration);
    	printf("PTS DTS :%d %d\n",pkt.pts,pkt.dts);
    	
    	//计算延时后,重新指定时间戳
    	pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    	pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    	pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    	//字节流的位置,-1 表示不知道字节流位置,由程序自行探测
    	pkt.pos = -1;
    
    	gettimeofday(&stamp, NULL);
    	int now_time = 1000*1000*(stamp.tv_sec)+stamp.tv_usec-start_time;
    	printf("start_time now_time:%d %d\n",start_time, now_time);
    	if (pkt.dts > now_time)
    	av_usleep(pkt.dts - now_time);
    	
    
    
    	ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    	if (ret < 0) {
    		fprintf(stderr, "Error muxing packet\n");
    	}
    
    	av_packet_unref(&pkt);
    }
    
    

    在这里插入图片描述

    源码:FFMPEG RTMP推流

    展开全文
  • 背景分析 RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写,该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMP/RTMPS/RTMPE等多种变种。...RTMP推流,就是将直播内容推送到服...
  • 如果无人机自身支持RTMP推流,可以直接采用RTMP直接推流的方式;也可以采用LivePush,与定制GBR24到H.264结合的方式;可以使用稳定的RTMP推流服务器LiveQing 或是 其他运营商提供的CDN.
  • rtmp推流抓包,Flash Media Live Encoder 推流给Adobe服务器的抓包。
  • windows rtmp推流

    2017-05-23 19:11:28
    本事例主要采用ffmpeg、libaac、libx264、librtmp实现的windows系统下的音视频设备检测、数据采集、编码(AAC/X264)、RTMP推流直播。
  • TSINGSEE青犀视频和海康联合研发的RTMP推流摄像头上线已经有一段时间了,RTMP推流摄像头无需布置电源线、交换机,即插即用,支持萤石云等平台,可以随时随地查看监控中的店铺、公司动态,且支持全新Smart H265编码,...
  • 第一步:准备工具 OBS推流工具下载及配置可以参见:OBS推流工具 ##第二步:安装流媒体服务 免费获得试用安装包,加入QQ群 615081503 群文件里有试用安装包,极速安装,下载...点击复制获得推流地址: rtmp://192.16...
  • python利用ffmpeg进行rtmp推流直播

    万次阅读 多人点赞 2019-07-04 23:44:06
    思路: opencv读取视频 —> 将视频分割为帧 —> 将每一帧进行需求加工后 —> 将此帧写入pipe管道 —> 利用ffmpeg进行推流...利用这个特点 让ffmpeg读取处理后的图像帧并进行rtmp推流即可 代码 ...
  • RTMP 推流器,RTMP(HLS)秒开播放器,跨平台(Win,IOS,Android)开源代码
  • ffmpeg rtmp推流代码示例

    千次阅读 2020-01-31 21:22:11
    rtmp推流,得有rtmp服务器,可以参考这篇博客搭建rtmp服务器 ===》》》ubuntu搭建rtmp服务器,如果自己有rtmp服务器,可以不用看。 rtmp推流需要用flv格式, 本篇博客demo是本地flv文件rtmp推流. 主要流程如下: ...
  • 每天都有很多新老用户咨询我关于RTMP推流摄像头的问题,推流摄像头的配置手册我之前已经写过(RTMP推流摄像头如何接入EasyDSS视频平台),已经持有RTMP推流摄像头的可以按照此手册进行配置。 由于部分用户使用的...
  • 每天都有很多新老用户咨询我关于RTMP推流摄像头的问题,推流摄像头的配置手册我之前已经写过(RTMP推流摄像头如何接入EasyDSS视频平台),已经持有RTMP推流摄像头的可以按照此手册进行配置。 由于部分用户使用的...
  • TSINGSEE青犀团队定制的海康RTMP推流摄像头是新上线定制产品,能够直接将摄像头视频流推到流媒体平台,本资源讲述了摄像头配置及推流方法。
  • TSINGSEE青犀视频和海康联合研发的RTMP推流摄像头上线已经有一段时间了,RTMP推流摄像头无需布置电源线、交换机,即插即用,支持萤石云等平台,可以随时随地查看监控中的店铺、公司动态,且支持全新Smart H265编码,...
  • LiveQing流媒体服务器软件,提供一站式的转码、点播、直播、时移回放服务,极大地简化了开发和集成的工作。 其中,点播功能主要包含:上传、转码、分发。...提供播放鉴权、推流鉴权等安全保证。提供用户及相关权限...
  • Window环境下 海康视频RTMP推流方法,使用到的工具。希望能够帮到大家。

空空如也

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

rtmp推流