ffmpeg读取rtsp并保存到mp4文件

来源:互联网 发布:深圳前海共享网络 编辑:程序博客网 时间:2024/05/22 09:58


本文章只讲述mp4文件的录像 至于音频录入 会在下个文章中介绍


总体思路为:初始化----连接相机获取码流--读取码流中的视频--创建输出mp4上下文---写mp4头----循环读取码流--写入mp4----写文件尾--关闭文件



第一步:初始化网络环境

//环境注册av_register_all();avcodec_register_all();avformat_network_init();


第二步:连接相机 读取rtsp码流

一个avformatcontext包含多个avstream--也就是说一个连接可能有多个码流通道--例如:0通道为视频 1通道为音频等等

他们的包含关系为:AVFormatContext----AVStream[N]                  AVStream----AVCodecContext----AVCodec

如果需要编解码--则需要为每个码流指定一个编解码器--

(1)如果是读取到的码流 则每个码流里面就自动包含了编码器和编码器上下文

(2)如果是需要构造码流 则需要创建编解码器上下文和编解码器


                                                                                         

BOOL CRecordRtspAndMicrophone::ConnectRtsp(){//分配网络文件格式m_pRtspFmt = avformat_alloc_context();if (!m_pRtspFmt) return FALSE;//连接rtspif (avformat_open_input(&m_pRtspFmt, m_strIPCAddr, NULL, NULL) != 0){return FALSE;}//读入一串流 用于分析if (avformat_find_stream_info(m_pRtspFmt, NULL) < 0){return FALSE;}//分析读入的流 读取其音频和视频下标for (int i = 0; i<m_pRtspFmt->nb_streams; i++){if (m_pRtspFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){m_pInVst = m_pRtspFmt->streams[i];m_nInViStreamIdx = i;}else if (m_pRtspFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){m_pInAst =m_pRtspFmt->streams[i];m_nInAuStreamIdx = i;}}return CreateMp4File();}

第三步:创建一个用于输出的mp4


ffmpeg对文件操作上下文是AVFormatContext-->AVOutputFormat

ffmpeg操作文件的对象是AVFormatContext-->AVIOContext

BOOL CRecordRtspAndMicrophone::CreateMp4File(){SYSTEMTIME st;m_nAudioPts = 0;m_nVideoPts = 0;GetLocalTime(&st);//关闭之前的文件if (m_pMp4Fmt){av_write_trailer(m_pMp4Fmt);avio_close(m_pMp4Fmt->pb);avcodec_close(m_pOutAst->codec);avcodec_close(m_pOutVst->codec);m_pOutAst = NULL;m_pOutVst = NULL;avformat_free_context(m_pMp4Fmt);RTSPLOG("Close mp4 file %s success!", m_strOutFilePath);}sprintf(m_strOutFilePath, "%s%04d%02d%02d%02d%02d%02d%03d.mp4", m_strFilePath,st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);//创建文件夹CreateMultistageFolder();//构造输出文件格式m_pMp4Fmt = avformat_alloc_context();if (!m_pMp4Fmt){RTSPLOG("Open mp4 file %s failed because of avformat_alloc_context failed!", m_strOutFilePath);return FALSE;}//输出上下文m_pOutFmt = av_guess_format(NULL, m_strOutFilePath, NULL);if (NULL == m_pOutFmt){RTSPLOG("Open mp4 file %s failed because of av_guess_format failed!", m_strOutFilePath);return FALSE;}m_pMp4Fmt->oformat = m_pOutFmt;m_pMp4Fmt->audio_codec_id = CODEC_ID_AAC;m_pMp4Fmt->video_codec_id = CODEC_ID_H264;if (avio_open2(&m_pMp4Fmt->pb, m_strOutFilePath, AVIO_FLAG_WRITE, NULL, NULL) < 0){RTSPLOG("Open mp4 file %s failed!", m_strOutFilePath);return FALSE;}RTSPLOG("Open mp4 file %s success!", m_strOutFilePath);if (NULL != m_pFun){m_pFun(m_strOutFilePath, m_pUser);}return TRUE;}


//创建多级目录BOOL CRecordRtspAndMicrophone::CreateMultistageFolder(){int i = 0;while (m_strOutFilePath[i] != '0'){//如果是文件夹目录if ('\\' == m_strOutFilePath[i] || '/' == m_strOutFilePath[i]){char szTempPath[URL_LEN] = { 0 };memcpy(szTempPath, m_strOutFilePath, i + 1);if (!PathIsDirectoryA(szTempPath)){RTSPLOG("File Folder %s not exisit! will create!", szTempPath);if (!CreateDirectoryA(szTempPath, NULL)){RTSPLOG("Close mp4 file %s failed!", szTempPath);return FALSE;}}}i++;}return TRUE;}


四、循环读取码流

思路为:创建一个AVPacket对象  循环读取数据到对象中 读取完成之后释放对象内存

DWORD WINAPI CRecordRtspAndMicrophone::ReadStreamThread(LPVOID param){AVPacket pkt;CRecordRtspAndMicrophone *pDlg = (CRecordRtspAndMicrophone*)param;if (NULL == pDlg) return -1;if (!pDlg->ConnectRtsp()){RTSPLOG("ConnectRtsp failed!");return 0;}pDlg->m_bPause = FALSE;av_init_packet(&pkt);av_read_play(pDlg->m_pRtspFmt);//play RTSPwhile (pDlg->m_bContinue ){if ( !pDlg->m_bRecord){if (!pDlg->m_bPause){av_read_pause(pDlg->m_pRtspFmt);pDlg->m_bPause = TRUE;}Sleep(1);continue;}pDlg->m_bPause = FALSE;if (av_read_frame(pDlg->m_pRtspFmt, &pkt) < 0 ){Sleep(1);continue;}pDlg->m_nVideoFrameCnt++;if (pDlg->NeedCreateNewFile()){pDlg->CreateMp4File();pDlg->m_nVideoFrameCnt = 0;}if (( pDlg->m_nAudioPts < pDlg->m_nVideoPts) && pDlg->m_pOutAst ){if (FALSE == pDlg->WriteAudioFrame()){Sleep(0);}}if (pDlg->m_nInViStreamIdx == pkt.stream_index){if (NULL == pDlg->m_pOutVst){if (!pDlg->AddVideoOutput()){RTSPLOG("AddVideoOutput failed!");break;}if (!pDlg->AddAudioOutput()){RTSPLOG("AddVideoOutput failed!");break;}if (!pDlg->WriteMP4Header()){RTSPLOG("AddVideoOutput failed!");break;}continue;}pDlg->WriteVideoFrame(&pkt);/*pDlg->VideoPacketPush(&pkt);if (pDlg->m_bDisplay){pDlg->VideoPacketPush( &pkt);}else{av_free_packet(&pkt);}*/if (!pDlg->m_player.InputStream(&pkt)){av_free_packet(&pkt);}}//if (pDlg->m_nInViStreamIdx == pkt.stream_index)//{////TODO:目前工程不需要接收来自于网络的音频//}}pDlg->WriteMP4Trailer();if (!pDlg->DisconnectRtsp()){RTSPLOG("ConnectRtsp failed!");}return TRUE;}
//将读取到的数据包写入到mp4文件中

这里有个很重要的问题 Pts和dts的问题 如果这两个值设置不争取 那么图像有可能会显示不出来
BOOL CRecordRtspAndMicrophone::WriteVideoFrame( AVPacket *pPkt ){pPkt->stream_index = m_nOutViStreamIdx;//输出文件中包含码流通道为0--视频 1音频pPkt->dts = m_nVideoFrameCnt;//dts为视频包数目pPkt->pts = 2 * av_rescale_q(m_nVideoFrameCnt, m_pOutVst->codec->time_base, m_pOutVst->time_base);//pts计算出来if (av_write_frame(m_pMp4Fmt, pPkt)){RTSPLOG("Error while writing video frame,fmt=0x%x,audiostream=0x%x,videostream=0x%x,pktsize=%d", m_pMp4Fmt,m_pOutAst,m_pOutVst, pPkt->size);}else{//RTSPLOG("[video] VIO frame len=%d", pPkt->size);}m_nVideoPts = pPkt->pts;return TRUE;}


五、文件操作

1、写文件头

BOOL CRecordRtspAndMicrophone::WriteMP4Header(){av_dump_format(m_pMp4Fmt, 0, "mydump0.txt", 1);if (NULL == m_pMp4Fmt) return FALSE;if (0 != avformat_write_header(m_pMp4Fmt, NULL)){RTSPLOG("Write file header error!");return FALSE;}return TRUE;}

2、写文件尾

BOOL CRecordRtspAndMicrophone::WriteMP4Trailer(){av_write_trailer( m_pMp4Fmt );return TRUE;}

3、关闭文件--关闭相机连接

BOOL CRecordRtspAndMicrophone::DisconnectRtsp(){if (m_pMp4Fmt)//关闭文件{avio_close(m_pMp4Fmt->pb);avformat_free_context(m_pMp4Fmt);//释放MP4文件上下文m_pMp4Fmt = NULL;}if (NULL != m_pVideoDecCtx)//释放视频解码器{avcodec_close(m_pVideoDecCtx);m_pVideoDecCtx = NULL;m_pVideoDec = NULL;}if (m_pRtspFmt)//关闭相机连接{avformat_close_input(&m_pRtspFmt);//avformat_free_context(m_pRtspFmt);m_pRtspFmt = NULL;}m_pInVst = NULL;m_pInAst = NULL;m_pOutVst = NULL;m_pOutAst = NULL;m_bFileInitOK = FALSE;m_bContinue = FALSE;return TRUE;}



1 0
原创粉丝点击