ffmpeg save rtsp stream
#include <stdio.h> #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> //#include <libswscale/swscale.h> #ifdef _MSC_VER int strcasecmp(const char *s1, const char *s2) { while ((*s1 != '\0') && (tolower(*(unsigned char *) s1) == tolower(*(unsigned char *) s2))) { s1++; s2++; } return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); } int strncasecmp(const char *s1, const char *s2, unsigned int n) { if (n == 0) return 0; while ((n-- != 0) && (tolower(*(unsigned char *) s1) == tolower(*(unsigned char *) s2))) { if (n == 0 || *s1 == '\0' || *s2 == '\0') return 0; s1++; s2++; } return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); } #endif //_MSC_VER #ifdef __cplusplus } #endif /*********************************************************** '** stream_component_open * Description: * //open the stream component for video/audio * * * Params:ic-pointer which contains the url and codec details :stream_index-index denoting video or audio stream ***********************************************************/ int stream_component_open(AVFormatContext *ic, int stream_index) { AVCodecContext *enc; AVCodec *codec; if (stream_index < 0 || stream_index >= (int)ic->nb_streams) return -1; enc = ic->streams[stream_index]->codec; /* prepare audio output */ if (enc->codec_type == CODEC_TYPE_AUDIO) { if (enc->channels > 0) enc->request_channels = FFMIN(2, enc->channels); else enc->request_channels = 2; /*Hardcoding the codec id to PCM_MULAW if the audio codec id returned by the lib is CODEC_ID_NONE */ if(enc->codec_id == CODEC_ID_NONE) { enc->codec_id = CODEC_ID_PCM_MULAW; enc->channels = 1; enc->sample_rate = 16000; enc->bit_rate = 128; } } codec = avcodec_find_decoder(enc->codec_id); enc->idct_algo = FF_IDCT_AUTO; enc->flags2 |= CODEC_FLAG2_FAST; enc->skip_frame = AVDISCARD_DEFAULT; enc->skip_idct = AVDISCARD_DEFAULT; enc->skip_loop_filter = AVDISCARD_DEFAULT; enc->error_concealment = 3; if (!codec || avcodec_open(enc, codec) < 0) return -1; avcodec_thread_init(enc, 1); enc->thread_count= 1; ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; return 0; } //声明函数 int enable_local_record(AVFormatContext *ic/*已经打开的视频文件上下文*/, int videostream,int audiostream, AVFormatContext **out_oc,const char * ofile,const char * ofileformat); int exit_onerr(const char * err_desc/*错误描述*/,int err_code/*错误码*/) { printf("%s\n",err_desc); system("pause");//暂停,查看错误描述 return err_code; } int main(int argc, char* argv[]) { //初始化ffmpeg链表结构 avcodec_register_all(); // Register all formats and codecs av_register_all(); AVPacket packet; //打开文件 AVFormatContext * ic = NULL; const char * rtsp_url = "rtsp://192.168.0.168:8557/PSIA/Streaming/channels/2?videoCodecType=H.264"; if(av_open_input_file(&ic, rtsp_url, NULL, 0, NULL)!=0) { return exit_onerr("can't open file.",-1); } if(!ic) { return exit_onerr("unknow error.",-2); } ic ->max_analyze_duration = 1000; //get streams information if(av_find_stream_info(ic)<0) { av_close_input_file(ic);//退出前,记得释放资源 ic = NULL; return exit_onerr("con't init streams information.",-3); } //find stream int videoStream=-1; // Didn't find a video stream int audioStream=-1; // Didn't find a audio stream // Find the first video stream for(int i=0; i<ic ->nb_streams; i++) { if( ic ->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) { videoStream=i; break; } } // Find the first audio stream for(int i=0; i<ic ->nb_streams; i++) { if( ic ->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) { audioStream=i; break; } } //判断视频文件中是否包含音视频流,如果没有,退出 if(audioStream<0 && videoStream<0) { av_close_input_file(ic);//退出前,记得释放资源 ic = NULL; return exit_onerr("con't find a audio stream or video stream",-4); } //open the stream component for video int videoComponent = -1; if(videoStream >= 0) { videoComponent = stream_component_open(ic, videoStream); if(videoComponent<0) { av_close_input_file(ic);//退出前,记得释放资源 return exit_onerr("not supported video stream",-5);//要求重新编译ffmpeg以支持该种编码格式 } } //open the stream component for audio int audioComponent = -1; if(audioStream >= 0) { audioComponent = stream_component_open(ic, audioStream); if(audioComponent<0) { av_close_input_file(ic);//退出前,记得释放资源 return exit_onerr("not supported audio stream",-6);//要求重新编译ffmpeg以支持该种编码格式 } } ////////////////////////////////////////////////////// int ret = 0; //初始化并打开录像 AVFormatContext * oc = NULL; const char * out_file_name = "D:\\test.avi"; //获取一帧完整的图像用于初始化ffmpeg内部的一些结构体数据(这里使用什么数据尚未清楚,请自行查看),否则会出现写文件头失败 int got_picture = 0; AVFrame *frame = avcodec_alloc_frame(); while( 1 ) { if(av_read_frame( ic, &packet)<0) { av_free_packet(&packet); av_close_input_file(ic);//退出前,记得释放资源 return exit_onerr("read frame error.",-7);//读取视频帧失败 } if(packet.stream_index == videoStream) { avcodec_decode_video(ic->streams[videoStream]->codec, frame, &got_picture, packet.data, packet.size); } av_free_packet(&packet); if(got_picture) break; } av_free(frame); ret = enable_local_record(ic,videoStream,audioStream,&oc,out_file_name,"avi"); if(ret <0 || !oc) { //退出前,记得释放资源,现在又多了个oc参数需要释放 if(oc) { ///cleanup the output contents format for(unsigned int i=0;i< oc->nb_streams;i++) { av_metadata_free(&oc ->streams[i]->metadata); av_free(oc ->streams[i]->codec); av_free(oc ->streams[i]); } for(unsigned int i=0;i<oc ->nb_programs;i++) { av_metadata_free(&oc ->programs[i]->metadata); } for(unsigned int i=0;i<oc ->nb_chapters;i++) { av_metadata_free(&oc ->chapters[i]->metadata); } av_metadata_free(&oc ->metadata); av_free(oc); } av_close_input_file(ic); return exit_onerr("can't init out file.",-8); } //开始录像 int video_dts = 0,audio_dts = 0;//时间戳 int total_frame = 300;//写300帧文件 while(total_frame--) { if( av_read_frame( ic, &packet) <0 ) //read the packet { //读取数据出错 av_free_packet(&packet); break; } if(packet.data && (packet.stream_index == videoStream || packet.stream_index == audioStream) ) { //计算时间视频戳,顺序+1,这里可以多研究几种编码的音视频文件,求其时间戳生成格式,h264编码顺序加1即可 if(packet.stream_index == videoStream) { packet.dts = video_dts++; packet.pts = video_dts; } else if(packet.stream_index == audioStream)//计算音频时间戳 { packet.dts = audio_dts++; packet.pts = audio_dts * (1000 * packet.size /ic ->streams[packet.stream_index]->codec ->sample_rate); } packet.flags |= PKT_FLAG_KEY; if(av_interleaved_write_frame(oc,&packet)<0) { printf("st:%d\twrite frame failed.\n",packet.stream_index); } } av_free_packet(&packet); } //关闭录像文件和输入文件 //写文件尾 av_write_trailer(oc); /* close the output file if need.*/ if (!(oc ->oformat->flags & AVFMT_NOFILE)) { url_fclose(oc->pb); } //释放资源 /*cleanup the output contents format*/ for(unsigned int i=0;i< oc->nb_streams;i++) { av_metadata_free(&oc ->streams[i]->metadata); av_free(oc ->streams[i]->codec); av_free(oc ->streams[i]); } for(unsigned int i=0;i<oc ->nb_programs;i++) { av_metadata_free(&oc ->programs[i]->metadata); } for(unsigned int i=0;i<oc ->nb_chapters;i++) { av_metadata_free(&oc ->chapters[i]->metadata); } av_metadata_free(&oc ->metadata); av_free(oc); av_close_input_file(ic); return 0; } //初始化录像文件,代码较长,可写成函数 /* *return <0 failed, 0 success */ int enable_local_record(AVFormatContext *ic/*已经打开的视频文件上下文*/,int videostream,int audiostream,AVFormatContext **out_oc,const char * ofile,const char * ofileformat) { AVFormatContext *oc = NULL;// AVOutputFormat *fmt = NULL; if( ofileformat ) { fmt = av_guess_format(ofileformat, NULL, NULL); } if(!fmt) { printf("out file format \"%s\" invalidate.\n",ofileformat); return -1; } /*******************************************init output contents begin**********************************************************/ /* allocate the output media context */ oc = avformat_alloc_context(); if (!oc) { printf("out of memory.\n"); return -2;//内存分配失败 } *out_oc = oc; oc ->oformat = fmt; sprintf_s( oc ->filename, sizeof( oc ->filename), "%s", ofile); av_metadata_conv(oc, fmt->metadata_conv, NULL); if( videostream >=0 ) { //AVCodecContext* pCodecCtx= ->streams[videostream]->codec;; //add video stream AVStream * st = av_new_stream(oc, videostream); if(!st) { printf("can not add a video stream.\n"); return -3; } st->codec->codec_id = ic ->streams[videostream] ->codec->codec_id; st->codec->codec_type = CODEC_TYPE_VIDEO; st->codec->bit_rate = ic ->streams[videostream] ->codec->bit_rate; st->codec->width = ic ->streams[videostream] ->codec->width; st->codec->height = ic ->streams[videostream] ->codec->height; st->codec->gop_size = ic ->streams[videostream] ->codec->gop_size; st->codec->pix_fmt = ic ->streams[videostream] ->codec->pix_fmt; st->codec->frame_size = ic ->streams[videostream] ->codec->frame_size; st->codec->has_b_frames = ic ->streams[videostream] ->codec->has_b_frames; st->codec->extradata = ic ->streams[videostream] ->codec->extradata; st->codec->extradata_size = ic ->streams[videostream] ->codec->extradata_size; st->codec->codec_tag = ic ->streams[videostream] ->codec->codec_tag; st->codec->bits_per_raw_sample = ic ->streams[videostream] ->codec->bits_per_raw_sample; st->codec->chroma_sample_location = ic ->streams[videostream] ->codec->chroma_sample_location; st->time_base.den = ic ->streams[videostream] ->time_base.den; st->time_base.num = ic ->streams[videostream] ->time_base.num; st->cur_dts = ic ->streams[videostream] ->cur_dts; st->stream_copy = 1; st->pts.den = ic ->streams[videostream] ->time_base.den; st->pts.num = ic ->streams[videostream] ->time_base.num; if( oc ->oformat->flags & AVFMT_GLOBALHEADER) st ->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; if(av_q2d(ic ->streams[videostream] ->codec->time_base)*ic ->streams[videostream] ->codec->ticks_per_frame > av_q2d(ic ->streams[videostream]->time_base) && av_q2d(ic ->streams[videostream]->time_base) < 1.0/1000) { st->codec->time_base = ic ->streams[videostream] ->codec->time_base; st->codec->time_base.num *= ic ->streams[videostream] ->codec->ticks_per_frame; } else { st->codec->time_base = ic ->streams[videostream] ->time_base; } st->disposition = ic ->streams[videostream] ->disposition; } if(audiostream >= 0 ) { AVStream * st = av_new_stream( oc, audiostream); if(!st) { printf("can not add a audio stream.\n"); return -4; } st->codec->codec_id = ic ->streams[audiostream] ->codec->codec_id; st->codec->codec_type = CODEC_TYPE_AUDIO; st->codec->bit_rate = ic ->streams[audiostream] ->codec->bit_rate; st->codec->gop_size = ic ->streams[audiostream] ->codec->gop_size; st->codec->pix_fmt = ic ->streams[audiostream] ->codec->pix_fmt; st->codec->bit_rate = ic ->streams[audiostream] ->codec->bit_rate; st->codec->channel_layout = ic ->streams[audiostream] ->codec->channel_layout; st->codec->frame_size = ic ->streams[audiostream] ->codec->frame_size; st->codec->sample_rate = ic ->streams[audiostream] ->codec->sample_rate; st->codec->channels = ic ->streams[audiostream] ->codec->channels; st->codec->block_align = ic ->streams[audiostream] ->codec->block_align; st->time_base.den = ic ->streams[audiostream] ->time_base.den; st->time_base.num = ic ->streams[audiostream] ->time_base.num; st->stream_copy = 1; st->pts.den = ic ->streams[audiostream] ->time_base.den; st->pts.num = ic ->streams[audiostream] ->time_base.num; if( oc->oformat->flags & AVFMT_GLOBALHEADER) st ->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; if(av_q2d(ic ->streams[audiostream] ->codec->time_base)*ic ->streams[audiostream] ->codec->ticks_per_frame > av_q2d(ic ->streams[audiostream]->time_base) && av_q2d(ic ->streams[audiostream]->time_base) < 1.0/1000) { st->codec->time_base = ic ->streams[audiostream] ->codec->time_base; st->codec->time_base.num *= ic ->streams[audiostream] ->codec->ticks_per_frame; } else { st->codec->time_base = ic ->streams[audiostream] ->time_base; } } /* set the output parameters (must be done even if no parameters). */ //AVFormatParameters params, *ap = ¶ms; //memset(ap, 0, sizeof(*ap)); if (av_set_parameters(oc, NULL/*ap*/) < 0) { printf("invalid output format parameters.\n"); return -5; } oc ->flags |= AVFMT_FLAG_NONBLOCK; /*******************************************init output contents end**********************************************************/ /* open the output file, if needed */ if (!(oc ->oformat ->flags & AVFMT_NOFILE)) { try { if (url_fopen(&oc->pb, ofile, URL_WRONLY) < 0) { printf("Could not open file.\n"); return -6; } } catch(...) { printf("Could not open file.\n"); return -6; } } /* write the stream header, if any */ if( 0 != av_write_header(oc)) { printf("write the stream header failed.\n"); return -7; } return 0; }