利用ffmpeg进行MP4格式封装
不论是利用ffmpeg,还是MP4v2进行MP4格式的封装,前面获取流的操作基本相同。
首先是打开RTSP流,这里需要注意的是,在avformat_open_input()函数中设置超时时间,否则没有流时,会发生阻塞。设置代码如下:
AVDictionary* options = NULL;
av_dict_set(&options,"rtsp_transport", "tcp", 0);
av_dict_set(&options,"stimeout","10000000",0);
if(avformat_open_input(ic,url,NULL,&options)!=0)
这里设置后,如果断网后,av_read_frame()也不会阻塞。
这里直接给出利用ffmpeg进行rtsp录制的源码。ffmpeg版本为4.0.1
RTSP流测试地址:rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 只要连网就可以访问。注意:此版本只是对固定帧率适用,如果帧率不固定,那么音视频就有可能不同步,变帧率录制方式请参考RTSP流封装成MP4。
#include
#include
#include
extern "C"{
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
}
using namespace std;
bool rtsp_state=false;
int pts_rtsp=0;
int audio_frame_index_rtsp=0;
int video_frame_index_rtsp=0;
//打开Rtsp流
int OpenRtspStream(const char* url,AVFormatContext **ic)
{
AVDictionary* options = NULL;
int ret=-1;
av_dict_set(&options,"rtsp_transport", "tcp", 0);
av_dict_set(&options,"stimeout","10000000",0);
if(avformat_open_input(ic,url,NULL,&options)!=0) //avformat_close_input 关闭
return -1;
if(avformat_find_stream_info(*ic,NULL)<0)
return -1;
printf("-----------rtsp流输入信息--------------\n");
av_dump_format(*ic, 0, url,0);
printf("---------------------------------------\n");
printf("\n");
return 0;
}
//查找流ID 索引号
int Find_StreamIndex(AVFormatContext* ic,enum AVMediaType type)
{
int Index=-1;
for(unsigned int i=0; i< ic->nb_streams; i++)
if(ic->streams[i]->codecpar->codec_type==type)
{
Index=i;
break;
}
if(Index==-1)
return -1;
return Index;
}
//打开解码器
int Open_Decoder(AVFormatContext** ic,AVCodecContext** pInCodecCtx,int index,bool open)
{
AVCodec* pInCodec=NULL;
int ret=-1;
pInCodec=avcodec_find_decoder((*ic)->streams[index]->codecpar->codec_id);
if(pInCodec==NULL)
return -1;
*pInCodecCtx=avcodec_alloc_context3(pInCodec);
if(!(*pInCodecCtx))
return -1;
ret=avcodec_parameters_to_context(*pInCodecCtx,(*ic)->streams[index]->codecpar);
if(ret<0)
return -1;
if(open)
{
if(avcodec_open2(*pInCodecCtx, pInCodec,NULL)<0)
return -1;
}
return 0;
}
//添加输出流视频信息
AVStream* AddVideoStream(AVFormatContext* oc,AVCodecContext** pOutCodecCtx,AVFormatContext* ic,int index,bool mark,bool open)
{
AVCodec *video_codec=NULL;
AVStream *VideoSt=NULL;
video_codec=avcodec_find_encoder(ic->streams[index]->codecpar->codec_id);
if(!video_codec)
return NULL;
VideoSt=avformat_new_stream(oc,video_codec);
if (!VideoSt)
{
return NULL;
}
*pOutCodecCtx=avcodec_alloc_context3(video_codec);
if(!(*pOutCodecCtx))
return NULL;
if(avcodec_parameters_copy(VideoSt->codecpar,ic->streams[index]->codecpar)<0)
return NULL;
if(avcodec_parameters_to_context(*pOutCodecCtx,VideoSt->codecpar)<0)
return NULL;
if(av_q2d(ic->streams[index]->avg_frame_rate)<20.0||av_q2d(ic->streams[index]->avg_frame_rate)>30.0||ic->streams[index]->avg_frame_rate.den==0)
(*pOutCodecCtx)->time_base.den=25;
else
(*pOutCodecCtx)->time_base.den = av_q2d(ic->streams[index]->avg_frame_rate);//帧率: 30
VideoSt->time_base.num=1;
VideoSt->time_base.den=ic->streams[index]->time_base.den;
if(!mark)
{
if(oc->oformat->flags&AVFMT_GLOBALHEADER)
(*pOutCodecCtx)->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
}
if(open)
{
if(avcodec_open2(*pOutCodecCtx,video_codec,NULL)<0)
return NULL;
}
return VideoSt;
}
//添加输出流音频信息
AVStream* AddAudioStream(AVFormatContext* oc,AVCodecContext** pOutCodecCtx,AVFormatContext* ic,int index,bool mark,bool open)
{
AVStream *AudioSt=NULL;
AVCodec *audio_codec=NULL;
audio_codec=avcodec_find_encoder(AV_CODEC_ID_AAC);
if(!audio_codec)
return NULL;
AudioSt=avformat_new_stream(oc,audio_codec);
if (!AudioSt)
return NULL;
*pOutCodecCtx=avcodec_alloc_context3(audio_codec);
if(!(*pOutCodecCtx))
return NULL;
(*pOutCodecCtx)->channels=2;
(*pOutCodecCtx)->channel_layout=av_get_default_channel_layout((*pOutCodecCtx)->channels);
(*pOutCodecCtx)->sample_rate=ic->streams[index]->codecpar->sample_rate;
(*pOutCodecCtx)->sample_fmt=audio_codec->sample_fmts[0];
(*pOutCodecCtx)->bit_rate=64000;
(*pOutCodecCtx)->strict_std_compliance=FF_COMPLIANCE_EXPERIMENTAL;
(*pOutCodecCtx)->time_base.num=1;
(*pOutCodecCtx)->time_base.den=ic->streams[index]->codecpar->sample_rate;
AudioSt->time_base.num=1;
AudioSt->time_base.den=ic->streams[index]->codecpar->sample_rate;//是否变化,注意测试 时基在发生改变
if(!mark)
{
if(oc->oformat->flags&AVFMT_GLOBALHEADER)
(*pOutCodecCtx)->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
}
if(open)
{
if(avcodec_open2(*pOutCodecCtx,audio_codec,NULL)<0)
return NULL;
}
if(avcodec_parameters_from_context(AudioSt->codecpar,(*pOutCodecCtx))<0)
return NULL;
return AudioSt;
}
//寻找packet.data中00 00 00 01后的位置,为了只获取H264裸流数据
int Search_I_Frame(uint8_t* buf,int size)
{
int i=0,j=0;
for(i=size-1;i>=3;i--)
{
if(buf[i] == 0x01 && buf[i-1] == 0x00 && buf[i-2] == 0x00 && buf[i-3] == 0x00)
break;
}
j=i-3;
if(i<3)
return -1;
else
return j;
}
int Init_Converted_Samples(uint8_t ***converted_input_samples,AVCodecContext *output_codec_context,int frame_size)
{
if (!(*converted_input_samples =(uint8_t **)calloc(output_codec_context->channels,sizeof(**converted_input_samples))))
return -1;
if (av_samples_alloc(*converted_input_samples, NULL,output_codec_context->channels,frame_size,output_codec_context->sample_fmt, 0) < 0)
{
av_freep(&(*converted_input_samples)[0]);
free(*converted_input_samples);
return -1;
}
return 0;
}
int Convert_Samples(const uint8_t **input_data,uint8_t **converted_data, const int frame_size,SwrContext *resample_context)
{
if (swr_convert(resample_context,converted_data, frame_size,input_data, frame_size) < 0)
{
return -1;
}
return 0;
}
int Add_Samples_To_Fifo(AVAudioFifo *fifo,uint8_t **converted_input_samples,const int frame_size)
{
if (av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size) < 0)
return -1;
if (av_audio_fifo_write(fifo, (void **)converted_input_samples,frame_size) < frame_size)
return -1;
return 0;
}
int Audio_Resampler(AVCodecContext *input_codec_context,AVCodecContext *output_codec_context,AVFrame *input_frame,AVAudioFifo *fifo_buffer)
{
SwrContext *resample_context=NULL;
uint8_t **converted_input_samples = NULL;
int ret=-1;
const int frame_size=input_frame->nb_samples;
//初始化
resample_context = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(output_codec_context->channels), //输出通道格式
output_codec_context->sample_fmt, //输出采样格式
output_codec_context->sample_rate, //输出采样率
av_get_default_channel_layout(input_codec_context->channels), //输入通道格式
input_codec_context->sample_fmt, //输入采样格式
input_codec_context->sample_rate, //输入采样率
0, NULL);
if (!resample_context||swr_init(resample_context) < 0)
return -1;
ret=Init_Converted_Samples(&converted_input_samples, output_codec_context, input_frame->nb_samples);
if(ret<0)
goto cleanup;
ret=Convert_Samples((const uint8_t**)input_frame->extended_data, converted_input_samples,input_frame->nb_samples, resample_context);
if(ret<0)
goto cleanup;
ret=Add_Samples_To_Fifo(fifo_buffer, converted_input_samples, input_frame->nb_samples);
if(ret<0)
goto cleanup;
ret=0;
cleanup:
if (converted_input_samples)
{
av_freep(&converted_input_samples[0]);
free(converted_input_samples);
}
swr_free(&resample_context);
return ret;
}
int Init_Output_Frame(AVFrame **frame,AVCodecContext *output_codec_context,int frame_size)
{
if (!(*frame = av_frame_alloc()))
return -1;
(*frame)->nb_samples = frame_size;
(*frame)->channel_layout = output_codec_context->channel_layout;
(*frame)->format = output_codec_context->sample_fmt;
(*frame)->sample_rate = output_codec_context->sample_rate;
if (av_frame_get_buffer(*frame, 0) < 0)
{
av_frame_free(frame);
return -1;
}
return 0;
}
int Encode_Audio_Frame_Rtsp(AVFrame *frame,AVFormatContext *oc,AVStream *audio_st,AVCodecContext *out_codec_ctx,int index,AVFormatContext *ic)
{
int data_present=0;
int ret=-1;
AVPacket output_packet;
av_init_packet(&output_packet);
output_packet.data=NULL;
output_packet.size=0;
if (frame)
{
frame->pts = pts_rtsp;
pts_rtsp += frame->nb_samples;
}
ret=avcodec_send_frame(out_codec_ctx, frame);
if (ret == AVERROR_EOF)
{
ret=0;
goto end;
}else if(ret<0)
{
av_packet_unref(&output_packet);
return -1;
}
ret=avcodec_receive_packet(out_codec_ctx,&output_packet);
if (ret==AVERROR(EAGAIN))
{
ret=0;
goto end;
}else if(ret==AVERROR_EOF)
{
ret=0;
goto end;
}else if(ret<0)
{
av_packet_unref(&output_packet);
return -1;
}
output_packet.pts=frame->pts;
output_packet.pts = av_rescale_q_rnd(output_packet.pts, ic->streams[index]->time_base, audio_st->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
output_packet.dts=output_packet.pts;
output_packet.duration = av_rescale_q(output_packet.duration, ic->streams[index]->time_base, audio_st->time_base);
output_packet.stream_index=index;
if (av_interleaved_write_frame(oc, &output_packet)< 0)
{
av_packet_unref(&output_packet);
return -1;
}
end:
av_packet_unref(&output_packet);
if(ret<0)
return ret;
else
return 0;
}
int Load_Encode_And_Write(AVAudioFifo *fifo,AVFormatContext *oc,AVStream *audio_st,AVCodecContext *out_codec_ctx,int index,AVFormatContext *ic)
{
AVFrame *output_frame;
int ret=-1;
const int frame_size = FFMIN(av_audio_fifo_size(fifo),out_codec_ctx->frame_size);
ret=Init_Output_Frame(&output_frame, out_codec_ctx, frame_size);
if(ret<0)
return ret;
if (av_audio_fifo_read(fifo, (void **)output_frame->data, frame_size) < frame_size)
{
av_frame_free(&output_frame);
return -1;
}
ret=Encode_Audio_Frame_Rtsp(output_frame,oc,audio_st,out_codec_ctx,index,ic);
if(ret<0)
{
av_frame_free(&output_frame);
return ret;
}
av_frame_free(&output_frame);
return 0;
}
//录制音视频
int RecordVideoAudio( )
{
AVFormatContext *ofmt=NULL;
AVFormatContext *ifmt=NULL;
AVCodecContext *in_video_ctx=NULL,*in_audio_ctx=NULL;
AVCodecContext *out_video_ctx=NULL,*out_audio_ctx=NULL;
AVStream *audio_stream=NULL,*video_stream=NULL;
AVPacket packet,pktV;
AVFrame *pInFrame=NULL;
AVAudioFifo *fifo=NULL;
AVOutputFormat *fmt=NULL;
int ret=-1;
int result=0;
int pos=0,size=0;
int got_frame=0;
int audio_index=-1,video_index=-1;
int sps_pps_size=0;
const char* filename="test.mp4";
int recordtime=9;
char* url="rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";
long long record_Time=recordtime*1000*60;//时间以分钟为单位
long long StartToSecond=0;
long long EndToSecond=0;
long long Totaltime=0;
ifmt=avformat_alloc_context();
if(!ifmt)
{
ret=-1;
goto end;
}
ret=OpenRtspStream(url,&ifmt);
if(ret<0)
goto end;
video_index=Find_StreamIndex(ifmt,AVMEDIA_TYPE_VIDEO);
audio_index=Find_StreamIndex(ifmt,AVMEDIA_TYPE_AUDIO);
if(video_index<0)
{
cout<<"没有视频流"<oformat;
audio_stream=AddAudioStream(ofmt,&out_audio_ctx,ifmt,audio_index,false,true);
if(!audio_stream)
{
ret= -1;
goto end;
}
video_stream=AddVideoStream(ofmt,&out_video_ctx,ifmt,video_index,false,false);
if(!video_stream)
{
ret=-1;
goto end;
}
sps_pps_size=ifmt->streams[video_index]->codecpar->extradata_size;
for(int i=0;icodecpar->extradata[i]=ifmt->streams[video_index]->codecpar->extradata[i];
video_stream->codecpar->extradata_size=sps_pps_size;
//显示输出文件信息
printf("-----------输出文件信息--------------\n");
av_dump_format(ofmt, 0, filename,1);
printf("---------------------------------------\n");
if(!(fmt->flags&AVFMT_NOFILE))
{
ret=avio_open(&ofmt->pb,filename,AVIO_FLAG_WRITE);
if(ret<0)
{
ret=-1;
goto end;
}
}
ret=avformat_write_header(ofmt,NULL);
if(ret<0)
{
ret=-1;
goto end;
}
pInFrame=av_frame_alloc();
if(!pInFrame)
{
ret= -1;
goto end;
}
fifo=av_audio_fifo_alloc(out_audio_ctx->sample_fmt,out_audio_ctx->channels,1);
if(!fifo)
{
ret= -1;
goto end;
}
const int video_duration=video_stream->time_base.den/out_video_ctx->time_base.den;
const int output_frame_size=out_audio_ctx->frame_size;
StartToSecond=GetTickCount();
rtsp_state=true;
while(rtsp_state)
{
av_init_packet(&packet);
packet.data=NULL;
packet.size=0;
EndToSecond=GetTickCount();
Totaltime=EndToSecond-StartToSecond;
if(Totaltime>record_Time)
break;
ret=av_read_frame(ifmt, &packet) ;
if(ret>=0)
{
if(packet.stream_index==video_index)
{
pos=Search_I_Frame(packet.data,packet.size);
if(pos<0)
{
ret=pos;
av_packet_unref(&packet);
break;
}
size=packet.size-pos;
av_new_packet(&pktV,size);
memset(pktV.data,0,size);
memcpy(pktV.data,packet.data+pos,size);
pktV.flags=packet.flags;
pktV.pts=video_duration*video_frame_index_rtsp;
pktV.pts = av_rescale_q_rnd(pktV.pts, ifmt->streams[video_index]->time_base, video_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pktV.dts = pktV.pts;
pktV.duration = av_rescale_q(packet.duration, ifmt->streams[video_index]->time_base, video_stream->time_base);
pktV.stream_index=video_index;
pktV.flags |= AV_PKT_FLAG_KEY;
ret=av_interleaved_write_frame(ofmt, &pktV) ;
if (ret< 0)
{
ret=-1;
av_packet_unref(&pktV);
av_packet_unref(&packet);
break;
}
video_frame_index_rtsp++;
av_packet_unref(&pktV);
}
else if(packet.stream_index==audio_index)
{
ret=avcodec_send_packet(in_audio_ctx,&packet);
if(ret<0)
{
ret=-1;
break;
}
ret=avcodec_receive_frame(in_audio_ctx,pInFrame);
if(ret==AVERROR(EAGAIN))
{
continue;
}
else if(ret==AVERROR_EOF)
{
continue;
}
else if(ret<0)
{
ret=-1;
break;
}
ret=Audio_Resampler(in_audio_ctx,out_audio_ctx,pInFrame,fifo);
if(ret<0)
goto end;
if(av_audio_fifo_size(fifo) >= output_frame_size)
{
ret=Load_Encode_And_Write(fifo,ofmt,audio_stream,out_audio_ctx,audio_index,ifmt);
if(ret<0)
goto end;
}
}
}
else
{
result=-1;
break;
}
av_packet_unref(&packet);
}
ret=av_write_trailer(ofmt);
if(ret<0)
{
ret=-1;
}
end:
if(in_audio_ctx)
avcodec_close(in_audio_ctx);
if(out_audio_ctx)
avcodec_close(out_audio_ctx);
if(fifo)
av_audio_fifo_free(fifo);
if(pInFrame)
av_frame_free(&pInFrame);
if(in_video_ctx)
avcodec_free_context(&in_video_ctx);
if(in_audio_ctx)
avcodec_free_context(&in_audio_ctx);
if(out_video_ctx)
avcodec_free_context(&out_video_ctx);
if(out_audio_ctx)
avcodec_free_context(&out_audio_ctx);
if (ofmt && !(fmt->flags & AVFMT_NOFILE))
avio_close(ofmt->pb);
if(ofmt)
avformat_free_context(ofmt);
if(ifmt)
{
avformat_close_input(&ifmt);
avformat_free_context(ifmt);
}
if(result<0)
ret=-2;
if(ret<0)
{
cout<<"录制失败,"<<"ret="<
工程下载地址:
链接:https://pan.baidu.com/s/1gN6NnT7n8ghpJRoDfgywBA 密码:m4oq
github下载地址: https://github.com/JSong-NJ/FFmpeg