ffmpeg处理rtmp/文件/rtsp的推流和拉流

ffmpeg处理rtmp/文件/rtsp的推流和拉流


本demo演示了利用ffmpeg从服务器拉流或本地文件读取流,更改流url或文件类型名称发送回服务器或存到本地的作用。
由于本程序只写了3个小时,还要忙别的,所以会有一些bug和优化的地方。不过demo的意义已经达到了。

//info.h
#ifndef __INFO_H__
#define __INFO_H__

#include <string.h> 
#include <stdio.h>

#endif

//ffmpeg.h
#ifndef __FFMPEG_H__
#define __FFMPEG_H__

#include "info.h"

extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libavdevice/avdevice.h"  //摄像头所用
#include "libavfilter/avfilter.h"
#include "libavutil/error.h"
#include "libavutil/mathematics.h"  
#include "libavutil/time.h"  
#include "inttypes.h"
#include "stdint.h"
};

#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avdevice.lib")
#pragma comment(lib,"avfilter.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"postproc.lib")
#pragma comment(lib,"swresample.lib")
#pragma comment(lib,"swscale.lib")


//#define INPUTURL   "test.flv" 
#define INPUTURL     "rtmp://test1.com:1935/myapp/teststream1" 
//#define OUTPUTURL  "testnew.flv"; 
#define OUTPUTURL    "rtmp://test1.com:1935/myapp/teststream1new"

//video param
extern int m_dwWidth;
extern int m_dwHeight;
extern double m_dbFrameRate;  //帧率                                                  
extern AVCodecID video_codecID;
extern AVPixelFormat video_pixelfromat;
extern char spspps[100];
extern int spspps_size;

//audio param
extern int m_dwChannelCount; //声道
extern int m_dwBitsPerSample; //样本
extern int m_dwFrequency;     //采样率
extern AVCodecID audio_codecID;
extern int audio_frame_size;

#define AUDIO_ID            0                                                 //packet 中的ID ,如果先加入音频 pocket 则音频是 0  视频是1,否则相反
#define VEDIO_ID            1

extern int nRet;                                                              //状态标志
extern AVFormatContext* icodec; 
extern AVInputFormat* ifmt;
extern char szError[256];                                                     //错误字符串
extern AVFormatContext* oc ;                                                  //输出流context
extern AVOutputFormat* ofmt;
extern AVStream* video_st;
extern AVStream* audio_st;
extern AVCodec *audio_codec;
extern AVCodec *video_codec;
extern int video_stream_idx;
extern int audio_stream_idx;
extern AVPacket pkt;   
extern AVBitStreamFilterContext * vbsf_aac_adtstoasc;                         //aac->adts to asc过滤器
static int  m_nVideoTimeStamp = 0;
static int  m_nAudioTimeStamp = 0;

int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe);
int InitOutput();
AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec); 
int OpenCodec(AVStream * istream, AVCodec * icodec); //打开编解码器
void read_frame(AVFormatContext *oc);    //从文件读取一帧数据根据ID读取不同的帧
void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t); //这个是根据传过来的buf 和size 写入文件
int Es2Mux_2(); //通过时间戳控制写入音视频帧顺序
int UintInput();
int UintOutput();

#endif

//ffmpeg.cpp
#include "ffmpeg.h"

int nRet = 0;
AVFormatContext* icodec = NULL; 
AVInputFormat* ifmt = NULL;
char szError[256]; 
AVFormatContext* oc = NULL;
AVOutputFormat* ofmt = NULL;
AVStream * video_st = NULL;
AVStream * audio_st = NULL;
AVCodec *audio_codec;
AVCodec *video_codec;
double audio_pts = 0.0;
double video_pts = 0.0;
int video_stream_idx = -1;
int audio_stream_idx = -1;
AVPacket pkt;
AVBitStreamFilterContext * vbsf_aac_adtstoasc = NULL;

//video param
int m_dwWidth = 0;
int m_dwHeight = 0;
double m_dbFrameRate = 25.0;  //帧率                                                  
AVCodecID video_codecID = AV_CODEC_ID_H264;
AVPixelFormat video_pixelfromat = AV_PIX_FMT_YUV420P;
char spspps[100];
int spspps_size = 0;

//audio param
int m_dwChannelCount = 2;      //声道
int m_dwBitsPerSample = 16;    //样本
int m_dwFrequency = 44100;     //采样率
AVCodecID audio_codecID = AV_CODEC_ID_AAC;
int audio_frame_size  = 1024;


int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe)
{
	int i = 0;
	nRet = avformat_open_input(iframe_c, Filename,(*iinputframe), NULL);
	if (nRet != 0)
	{
		av_strerror(nRet, szError, 256);
		printf(szError);
		printf("\n");
		printf("Call avformat_open_input function failed!\n");
		return 0;
	}
	if (av_find_stream_info(*iframe_c) < 0)
	{
		printf("Call av_find_stream_info function failed!\n");
		return 0;
	}
	//输出视频信息
	av_dump_format(*iframe_c, -1, Filename, 0);

	//添加音频信息到输出context
	for (i = 0; i < (*iframe_c)->nb_streams; i++)
	{
		if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			video_stream_idx = i;
			m_dwWidth = (*iframe_c)->streams[i]->codec->width;
			m_dwHeight = (*iframe_c)->streams[i]->codec->height;
			m_dbFrameRate = av_q2d((*iframe_c)->streams[i]->r_frame_rate);
			video_codecID = (*iframe_c)->streams[i]->codec->codec_id;
			video_pixelfromat = (*iframe_c)->streams[i]->codec->pix_fmt;
			spspps_size = (*iframe_c)->streams[i]->codec->extradata_size;
			memcpy(spspps,(*iframe_c)->streams[i]->codec->extradata,spspps_size);
		}
		else if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audio_stream_idx = i;
			m_dwChannelCount = (*iframe_c)->streams[i]->codec->channels;
			switch ((*iframe_c)->streams[i]->codec->sample_fmt)
			{
			case AV_SAMPLE_FMT_U8:
				m_dwBitsPerSample  = 8;
				break;
			case AV_SAMPLE_FMT_S16:
				m_dwBitsPerSample  = 16;
				break;
			case AV_SAMPLE_FMT_S32:
				m_dwBitsPerSample  = 32;
				break;
			default:
				break;
			}
			m_dwFrequency = (*iframe_c)->streams[i]->codec->sample_rate;
			audio_codecID = (*iframe_c)->streams[i]->codec->codec_id;
			audio_frame_size = (*iframe_c)->streams[i]->codec->frame_size;
		}
	}
	return 1;
}

int InitOutput()
{
	int i = 0;
	/* allocate the output media context */
	avformat_alloc_output_context2(&oc, NULL, "flv", OUTPUTURL);
	if (!oc) 
	{
		return getchar();
	}
	ofmt = oc->oformat;

	/* open the output file, if needed */
	if (!(ofmt->flags & AVFMT_NOFILE))
	{
		if (avio_open(&oc->pb, OUTPUTURL, AVIO_FLAG_WRITE) < 0)
		{
			printf("Could not open '%s'\n", OUTPUTURL);
			return getchar();
		}
	}

	//添加音频信息到输出context
	if(audio_stream_idx != -1)
	{
		ofmt->audio_codec = audio_codecID;
		audio_st = Add_output_stream_2(oc, AVMEDIA_TYPE_AUDIO,audio_codecID,&audio_codec);
	}

	//添加视频信息到输出context
	ofmt->video_codec = video_codecID;
	video_st = Add_output_stream_2(oc, AVMEDIA_TYPE_VIDEO,video_codecID,&video_codec);

	if (OpenCodec(video_st,video_codec) < 0)   //打开视频编码器
	{
		printf("can not open video codec\n");
		return getchar();
	}

	if(audio_stream_idx != -1)
	{
		if (OpenCodec(audio_st,audio_codec) < 0)   //打开音频编码器
		{
			printf("can not open audio codec\n");
			return getchar();
		}
	}

	av_dump_format(oc, 0, OUTPUTURL, 1);

	if (avformat_write_header(oc, NULL))
	{
		printf("Call avformat_write_header function failed.\n");
		return 0;
	}

	if(audio_stream_idx != -1)
	{
		if ((strstr(oc->oformat->name, "flv") != NULL) || 
			(strstr(oc->oformat->name, "mp4") != NULL) || 
			(strstr(oc->oformat->name, "mov") != NULL) ||
			(strstr(oc->oformat->name, "3gp") != NULL))    
		{
			if (audio_st->codec->codec_id == AV_CODEC_ID_AAC) //AV_CODEC_ID_AAC
			{
				vbsf_aac_adtstoasc =  av_bitstream_filter_init("aac_adtstoasc");  
			}
		} 
	}
	if(vbsf_aac_adtstoasc == NULL)  
	{  
		return -1;  
	} 
	return 1;
}

AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec)
{
	AVCodecContext* output_codec_context = NULL;
	AVStream * output_stream = NULL;

	/* find the encoder */
	*codec = avcodec_find_encoder(codecID);
	if (!(*codec)) 
	{
		return NULL;
	}
	output_stream = avformat_new_stream(output_format_context, *codec);
	if (!output_stream)
	{
		return NULL;
	}

	output_stream->id = output_format_context->nb_streams - 1;
	output_codec_context = output_stream->codec;
	output_codec_context->codec_id = codecID;
	output_codec_context->codec_type = codec_type_t;

	switch (codec_type_t)
	{
	case AVMEDIA_TYPE_AUDIO:
		AVRational CodecContext_time_base;
		CodecContext_time_base.num = 1;
		CodecContext_time_base.den = m_dwFrequency;
		output_stream->time_base = CodecContext_time_base;
		output_codec_context->time_base = CodecContext_time_base;
		output_stream->start_time = 0;
		output_codec_context->sample_rate = m_dwFrequency;
		output_codec_context->channels = m_dwChannelCount;
		output_codec_context->frame_size = audio_frame_size;
		switch (m_dwBitsPerSample)
		{
		case 8:
			output_codec_context->sample_fmt  = AV_SAMPLE_FMT_U8;
			break;
		case 16:
			output_codec_context->sample_fmt  = AV_SAMPLE_FMT_S16;
			break;
		case 32:
			output_codec_context->sample_fmt  = AV_SAMPLE_FMT_S32;
			break;
		default:
			break;
		}
		output_codec_context->block_align = 0;
		if(! strcmp( output_format_context-> oformat-> name,  "mp4" ) ||
			!strcmp (output_format_context ->oformat ->name , "mov" ) ||
			!strcmp (output_format_context ->oformat ->name , "3gp" ) ||
			!strcmp (output_format_context ->oformat ->name , "flv" ))
		{
			output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
		}
		break;
	case AVMEDIA_TYPE_VIDEO:
		AVRational r_frame_rate_t;
		r_frame_rate_t.num = 1000;
		r_frame_rate_t.den = (int)(m_dbFrameRate * 1000);
		output_stream->time_base = r_frame_rate_t;
		output_codec_context->time_base = r_frame_rate_t;

		AVRational r_frame_rate_s;
		r_frame_rate_s.num = (int)(m_dbFrameRate * 1000);
		r_frame_rate_s.den = 1000;
		output_stream->r_frame_rate = r_frame_rate_s;

		output_stream->start_time = 0;
		output_codec_context->pix_fmt = video_pixelfromat;
		output_codec_context->width = m_dwWidth;
		output_codec_context->height = m_dwHeight;
		output_codec_context->extradata = (uint8_t *)spspps;
		output_codec_context->extradata_size = spspps_size;
		//这里注意不要加头,demux的时候 h264filter过滤器会改变文件本身信息
		//这里用output_codec_context->extradata 来显示缩略图
		//if(! strcmp( output_format_context-> oformat-> name,  "mp4" ) ||
		//	!strcmp (output_format_context ->oformat ->name , "mov" ) ||
		//	!strcmp (output_format_context ->oformat ->name , "3gp" ) ||
		//	!strcmp (output_format_context ->oformat ->name , "flv" ))
		//{
		//	output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
		//}
		break;
	default:
		break;
	}
	return output_stream;
}

int OpenCodec(AVStream * istream, AVCodec * icodec)
{
	AVCodecContext *c = istream->codec;
	nRet = avcodec_open2(c, icodec, NULL); 
	return nRet;
}

int UintInput()
{
	/* free the stream */
	av_free(icodec);
	return 1;
}

int UintOutput()
{
	int i = 0;
	nRet = av_write_trailer(oc);
	if (nRet < 0)
	{
		av_strerror(nRet, szError, 256);
		printf(szError);
		printf("\n");
		printf("Call av_write_trailer function failed\n");
	}
	if (vbsf_aac_adtstoasc !=NULL)
	{
		av_bitstream_filter_close(vbsf_aac_adtstoasc); 
		vbsf_aac_adtstoasc = NULL;
	}
	av_dump_format(oc, -1, OUTPUTURL, 1); 
	/* Free the streams. */
	for (i = 0; i < oc->nb_streams; i++) 
	{
		av_freep(&oc->streams[i]->codec);
		av_freep(&oc->streams[i]);
	}
	if (!(ofmt->flags & AVFMT_NOFILE))
	{
		/* Close the output file. */
		avio_close(oc->pb);
	}
	av_free(oc);
	return 1;
}

void read_frame(AVFormatContext *oc)
{
	int ret = 0;

	ret = av_read_frame(icodec, &pkt);

	if (pkt.stream_index == video_stream_idx)
	{
		write_frame_3(oc,VEDIO_ID,pkt);
	}
	else if (pkt.stream_index == audio_stream_idx)
	{
		write_frame_3(oc,AUDIO_ID,pkt);
	}
}

void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t)
{
	int64_t pts = 0, dts = 0;
	int nRet = -1;
	AVRational time_base_t;
	time_base_t.num = 1;
	time_base_t.den = 1000;

	if(ID == VEDIO_ID)
	{
		AVPacket videopacket_t;
		av_init_packet(&videopacket_t);

		if (av_dup_packet(&videopacket_t) < 0)
		{
			av_free_packet(&videopacket_t);
		}

		videopacket_t.pts = pkt_t.pts;
		videopacket_t.dts = pkt_t.dts;
		videopacket_t.pos = 0;
		videopacket_t.priv = 0;
		videopacket_t.flags = 1;
		videopacket_t.convergence_duration = 0;
		videopacket_t.side_data_elems = 0;
		videopacket_t.stream_index = VEDIO_ID;
		videopacket_t.duration = 0;
		videopacket_t.data = pkt_t.data;
		videopacket_t.size = pkt_t.size;
		nRet = av_interleaved_write_frame(oc, &videopacket_t);
		if (nRet != 0)
		{
			printf("error av_interleaved_write_frame _ video\n");
		}
		av_free_packet(&videopacket_t);
	}
	else if(ID == AUDIO_ID)
	{
		AVPacket audiopacket_t;
		av_init_packet(&audiopacket_t);

		if (av_dup_packet(&audiopacket_t) < 0)
		{
			av_free_packet(&audiopacket_t);
		}

		audiopacket_t.pts = pkt_t.pts;
		audiopacket_t.dts = pkt_t.dts;
		audiopacket_t.pos = 0;
		audiopacket_t.priv = 0;
		audiopacket_t.flags = 1;
		audiopacket_t.duration = 0;
		audiopacket_t.convergence_duration = 0;
		audiopacket_t.side_data_elems = 0;
		audiopacket_t.stream_index = AUDIO_ID;
		audiopacket_t.duration = 0;
		audiopacket_t.data = pkt_t.data;
		audiopacket_t.size = pkt_t.size;

		//添加过滤器
		if(! strcmp( oc-> oformat-> name,  "mp4" ) ||
			!strcmp (oc ->oformat ->name , "mov" ) ||
			!strcmp (oc ->oformat ->name , "3gp" ) ||
			!strcmp (oc ->oformat ->name , "flv" ))
		{
			if (audio_st->codec->codec_id == AV_CODEC_ID_AAC)
			{
				if (vbsf_aac_adtstoasc != NULL)
				{
					AVPacket filteredPacket = audiopacket_t; 
					int a = av_bitstream_filter_filter(vbsf_aac_adtstoasc,                                           
						audio_st->codec, NULL,&filteredPacket.data, &filteredPacket.size,
						audiopacket_t.data, audiopacket_t.size, audiopacket_t.flags & AV_PKT_FLAG_KEY); 
					if (a >  0)             
					{                
						av_free_packet(&audiopacket_t); 
						filteredPacket.destruct = av_destruct_packet;  
						audiopacket_t = filteredPacket;             
					}   
					else if (a == 0)
					{
						audiopacket_t = filteredPacket;   
					}
					else if (a < 0)            
					{                
						fprintf(stderr, "%s failed for stream %d, codec %s",
							vbsf_aac_adtstoasc->filter->name,audiopacket_t.stream_index,audio_st->codec->codec ?  audio_st->codec->codec->name : "copy");
						av_free_packet(&audiopacket_t);   

					}
				}
			}
		}
		nRet = av_interleaved_write_frame(oc, &audiopacket_t);
		if (nRet != 0)
		{
			printf("error av_interleaved_write_frame _ audio\n");
		}
		av_free_packet(&audiopacket_t);
	}
}

int Es2Mux_2()
{
	for (;;)
	{
		read_frame(oc);
	}
	return 1;
}

//main.cpp
#include "ffmpeg.h"

int main(int argc ,char ** argv)
{
	av_register_all();
	avformat_network_init();

	InitInput(INPUTURL,&icodec,&ifmt);
	InitOutput();
	printf("--------程序运行开始----------\n");
	//
	Es2Mux_2();
	//
	UintOutput();
	UintInput();
	printf("--------程序运行结束----------\n");
	printf("-------请按任意键退出---------\n");
	return getchar();
}


交流请加QQ群:62054820
QQ:379969650

  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值