视频安防平台-MP4文件存储和读取的封装
一般客户端存储录像文件会采用mp4的格式进行封装,下面针对mp4文件封装了一层,支持语音流的写入、语音流的读出、支持视频流的写入、支持视频流的读出,支持按帧读、按时间读取、支持倒放、随机读取等各种历史视频控制功能,支持文件加密等功能。
下面粘贴一下mp4文件的封装头文件:
#ifndef __LIBMP4_H_
#define __LIBMP4_H_
#ifndef _MP4V2_H_
#define _MP4V2_H_
#include "mp4v2/mp4v2.h"
#include <vector>
#endif
using namespace std;
typedef struct sps_pps_header
{
unsigned char start_code_prefix[5];
}sps_pps_header_t;
#define MP4ENCODER_ERROR(err) ((MP4EncoderResult)(-(err)))
#define DEFAULT_RECORD_TIME 0U
#define MAX_FRAME_LEN 2*1024*1024UL
#define MAX_AUDIO_SIZE 100*1024UL
//#define AUDIO_STEP_SIZE 160
#define AUDIO_STEP_SIZE 160 * 2 //双声道数据加倍
#define H264_VIDEO_TYPE "H264"
#define MPEG4_VIDEO_TYPE "MPEG4"
#define MPEG2_VIDEO_TYPE "MPEG2"
#define PRIVATE_VIDEO_TYPE "PRIVATE"
#define ALAW_AUDIO_TYPE "ALAW"
typedef enum
{
MP4ENCODER_ENONE = 0,
MP4ENCODER_E_CREATE_FAIL,
MP4ENCODER_E_ADD_VIDEO_TRACK,
MP4ENCODER_E_ADD_AUDIO_TRACK,
MP4ENCODER_WARN_RECORD_OVER,
MP4ENCODER_E_WRITE_VIDEO_DATA,
MP4ENCODER_E_WRITE_AUDIO_DATA,
MP4ENCODER_E_ALLOC_MEMORY_FAILED,
MP4ENCODER_E_UNKONOWN
}MP4EncoderResult;
enum StreamTypes
{
E_STREAM_UNKNOWN = -1,
E_STREAM_PRAVITE,
E_STREAM_H264,
E_STREAM_MPEG4,
E_STREAM_MPEG2,
E_STREAM_ALAW
};
class MP4Encoder
{
public:
MP4Encoder(void);
~MP4Encoder(void);
MP4EncoderResult MP4CreateFile(const char *sFileName,unsigned uRecordTime = DEFAULT_RECORD_TIME,bool bEnc = false);
MP4EncoderResult MP4AddH264Track(const uint8_t *sData, int nSize,int nWidth, int nHeight, int nFrameRate = 25);
MP4EncoderResult MP4AddAACTrack(const uint8_t *sData, int nSize);
MP4EncoderResult MP4AddALAWTrack(const uint8_t *sData, int nSize);
MP4EncoderResult MP4AddMPEGTrack(int nWidth, int nHeight, int nFrameRate = 25,uint8_t nVdeoType = MP4_MPEG4_VIDEO_TYPE);
MP4EncoderResult MP4WriteH264Data(uint8_t *sData, int nSize, uint64_t u64PTS);
MP4EncoderResult MP4WriteAACData(const uint8_t *sData, int nSize,uint64_t u64PTS);
MP4EncoderResult MP4WriteALAWData(uint8_t *sData, int nSize,uint64_t u64PTS);
MP4EncoderResult MP4WriteMPEGData(uint8_t *sData, int nSize, uint64_t u64PTS);
MP4EncoderResult MP4ModifyFile(const char *sFileName);
MP4EncoderResult MP4OptimizeFile(const char *sFileName);
void MP4ReleaseFile();
private:
unsigned m_uSecond;
MP4FileHandle m_hFile;
bool m_bFirstVideo,m_bFirstAudio;
MP4TrackId m_videoTrack, m_audioTrack;
uint64_t m_u64VideoPTS,m_u64AudioPTS, m_u64FirstPTS, m_u64LastPTS;
bool m_IsPrivateStream;
char* m_pAudioBuf;
int m_AudioSize;
bool m_bEnc;
};
struct MP4SampleData
{
MP4SampleData(void)
{
pData = NULL;
nLen = 0;
nPts = 0;
IsKeyFrame = false;
}
uint8_t *pData;
uint32_t nLen;
uint64_t nPts;
bool IsKeyFrame;
};
typedef vector<MP4SampleData> MP4SampleVector;
//回调接口
class IMP4Notify
{
public:
virtual void OnReadVideoData(uint8_t *pData,uint32_t nLen,uint64_t nPts,bool IsKeyFrame) = 0;
};
struct MP4_FRAME
{
unsigned short Sn; //
int bVideo; //
int PayLoadType; //payload type
int Manuf; //视频厂家
StreamTypes eType;
MP4SampleVector vmp4buf; //存放多帧数据
};
class MP4Decoder
{
public:
MP4Decoder();
MP4Decoder(IMP4Notify* pNotify);
~MP4Decoder(void);
int MP4ReadFileExt(const char *sFileName,uint64_t *msFileDuration,uint64_t msStartTime = 0,uint64_t msEndTime = 0,bool FirstKeyFrame = false,bool bInitDirection = false);//读取时需要给定一个初始方向,默认0正向
int MP4ReadSampleByFrame(uint64_t *CurSampleID,MP4_FRAME &mp4frame,bool wantKeyFrame = false,bool bDirection = false);//首次调用时,方向必须与打开文件时设置的初始方向一致
int MP4ReadSampleByDuration(uint64_t msDuration,MP4_FRAME &mp4frame,bool wantKeyFrame = false,bool bDirection = false);//首次调用时,方向必须与打开文件时设置的初始方向一致
int MP4ReadSampleByTime(uint64_t msStartTime,uint64_t msEndTime,MP4_FRAME &mp4frame);
int MP4ReadAudioByFrame(uint64_t *CurSampleID,MP4_FRAME &mp4audio);
int MP4ReadAudioByDuration(uint64_t msDuration,MP4_FRAME &mp4audio);
int MP4ReadAudioByTime(uint64_t msStartTime,uint64_t msEndTime,MP4_FRAME &mp4audio);
void MP4ReleaseFile();
uint64_t MP4GetTotalSampleNum()
{
return m_TotalSample;
}
uint64_t MP4GetCurSampleID()
{
if(m_bDirection == false)
{
return m_ReadIndex;
}
else
{
return m_ReverseReadIndex;
}
}
float MP4GetPos()
{
float nPos = 0;
if(m_bDirection == false)
{
nPos = (float)m_ReadIndex * 100.00 / (float)m_FileAllSample;
}
else
{
nPos = (float)m_ReverseReadIndex * 100.00 / (float)m_FileAllSample;
}
return nPos;
}
void MP4SetPos(uint64_t msStartTime)
{
MP4SampleId StartSampleID;
MP4Timestamp StartStamp = MP4ConvertToTrackTimestamp(m_hFile,m_videoTrack,msStartTime,MP4_MSECS_TIME_SCALE);
StartSampleID = MP4GetSampleIdFromTime(m_hFile,m_videoTrack,StartStamp,false);
if(StartSampleID == MP4_INVALID_SAMPLE_ID)
{
return ;
}
if(StartSampleID > m_FinalSample)
{
return;
}
m_ReadIndex = StartSampleID;
}
//通知接口
IMP4Notify* m_pMP4Notify;
private:
MP4FileHandle m_hFile;
MP4TrackId m_videoTrack, m_audioTrack;
uint64_t m_u64VideoPTS,m_u64AudioPTS;
uint64_t m_msTotalDuration;//单位ms
StreamTypes eStreamType;
uint8_t m_video_data[MAX_FRAME_LEN];
unsigned int m_video_len;
uint8_t m_audio_data[MAX_AUDIO_SIZE];
unsigned int m_audio_len;
uint64_t m_msStartTime;
uint64_t m_msEndTime;
uint32_t m_ReadIndex;
int32_t m_ReverseReadIndex;
uint64_t m_FileAllSample;
uint64_t m_TotalSample;
uint64_t m_StartSample;
uint64_t m_FinalSample;
bool m_bDirection;//false 正向 true反向
int MP4ReadSampleByFrameForward(uint64_t *CurSampleID,MP4_FRAME &mp4frame,bool wantKeyFrame = false);
int MP4ReadSampleByDurationForward(uint64_t msDuration,MP4_FRAME &mp4frame,bool wantKeyFrame = false);
int MP4ReadSampleByFrameReverse(uint64_t *CurSampleID,MP4_FRAME &mp4frame);
int MP4ReadSampleByDurationReverse(uint64_t msDuration,MP4_FRAME &mp4frame);
int MP4ReadChangeDirection(void);
void MP4FreeSample(uint8_t** pSample);
bool m_bEnc;
};
#endif