ffmpeg读取rtsp并保存到mp4文件
2014-07-28 09:05
411 查看
本文章只讲述mp4文件的录像 至于音频录入 会在下个文章中介绍总体思路为:初始化----连接相机获取码流--读取码流中的视频--创建输出mp4上下文---写mp4头----循环读取码流--写入mp4----写文件尾--关闭文件第一步:初始化网络环境
//环境注册 av_register_all(); avcodec_register_all(); avformat_network_init();第二步:连接相机 读取rtsp码流一个avformatcontext包含多个avstream--也就是说一个连接可能有多个码流通道--例如:0通道为视频 1通道为音频等等他们的包含关系为:AVFormatContext----AVStreamAVStream----AVCodecContext----AVCodec如果需要编解码--则需要为每个码流指定一个编解码器--(1)如果是读取到的码流 则每个码流里面就自动包含了编码器和编码器上下文(2)如果是需要构造码流 则需要创建编解码器上下文和编解码器
BOOL CRecordRtspAndMicrophone::ConnectRtsp() { //分配网络文件格式 m_pRtspFmt = avformat_alloc_context(); if (!m_pRtspFmt) return FALSE; //连接rtsp if (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(); }第三步:创建一个用于输出的mp4ffmpeg对文件操作上下文是AVFormatContext-->AVOutputFormatffmpeg操作文件的对象是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;}
相关文章推荐
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- ffmpeg 解析RTSP视频流并间隔保存成视频文件、图片
- FFmpeg 获取RTSP传过来的视频数据并保存成文件
- fmpeg 读取mp4 rtsp,存储为图片序列 ffmpeg 2017.08
- ffmpeg和SDL教程(一)文件读取和保存
- ffmpeg和SDL教程(一)文件读取和保存
- RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中
- javaCV开发详解之4:转流器实现(也可作为本地收流器、推流器,新增添加图片及文字水印,视频图像帧保存),实现rtsp/rtmp/本地文件转发到rtmp流媒体服务器(基于javaCV-FFMPEG)
- Live555 服务器保存RTSP协议数据为MP4文件
- 利用ffmpeg将RTSP传输的h264原始码流保存到文件中
- ffmpeg代码笔记5:MP4文件读取packet,计算DTS
- 利用ffmpeg将RTSP传输的h264原始码流保存到文件中
- ffmpeg和SDL教程(一)文件读取和保存
- RTSP转RTMP同时保存成mp4文件
- 通过rtsp获取H264裸流并保存到mp4文件
- ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件
- 实现用FFmpeg接收RTSP,把H264视频和AAC音频录制成MP4文件(附完整代码)
- javaCV开发详解之4:转流器实现(也可作为本地收流器、推流器,新增添加图片及文字水印,视频图像帧保存),实现rtsp/rtmp/本地文件转发到rtmp流媒体服务器(基于javaCV-FFMPEG)