利用ffmpeg录制rtsp流的方法总结(三)

利用MP4v2进行MP4格式封装

       最开始只想用最简单的方法实现rtsp流保存为MP4,查找了很多资料,发现mp4v2比较简单,就开始用它进行MP4的封装,后来又实现了用ffmpeg进行格式的封装,发现MP4v2封装的文件效果比较好,同时,对于固定帧率的文件,不用手动写时间戳,只有设置默认的参数,就可以很容易实现音视频的同步。相比较ffmpeg操作流程确实要简单很多,这个毕竟是专门用来进行MP4格式的封装。

       其实MP4v2用法很简单,主要是几个重要的参数设置。

参考资料:https://blog.csdn.net/leixiaohua1020/article/details/12755173

MP4FileHandle MP4Create (const char* fileName,uint32_t  flags) 
功能:创建MP4文件句柄。  
返回:MP4文件句柄。  
参数:fileName 要录制的MP4文件名;flags 创建文件类型,如果要创建普通文件用默认值0就可以,如要录制大于4G的MP4文件此处要设置MP4_CREATE_64BIT_DATA。     


bool MP4SetTimeScale( MP4FileHandle hFile, uint32_t value ) 
功能:设置时间标度。 
返回:成功返回true,失败返回false。 
参数:hFile MP4文件句柄,value 要设置的值(每秒的时钟ticks数)。    
MP4TrackId MP4AddH264VideoTrack(MP4FileHandle hFile,
                 uint32_t timeScale, 
                 MP4Duration sampleDuration,
                 uint16_t width,
                 uint16_t height,
                 uint8_t AVCProfileIndication,
                 uint8_t profile_compat,
                 uint8_t AVCLevelIndication,
                 uint8_t sampleLenFieldSizeMinusOne)
功能:添加h264视频track。 
返回:返回track id号。 
参数:hFile MP4文件句柄,timeScale 视频每秒的ticks数(如90000),sampleDuration 设置为 MP4_INVALID_DURATION,width height 视频的宽高,AVCProfileIndication profile (baseline profile, main profile, etc. see),profile_compat compatible profile,AVCLevelIndication levels,sampleLenFieldSizeMinusOne 设置为3. 
注意: AVCProfileIndication,profile_compat, AVCLevelIndication,这三个参数值是在h264流中得到的。     


MP4TrackId MP4AddAudioTrack(MP4FileHandle hFile,
                     uint32_t timeScale,
                     MP4Duration sampleDuration,
                     uint8_t audioType)
功能:添加音频(aac)track。  
返回:返回track id号。  
参数:hFile MP4句柄,timeScale音频每秒的ticks数(如16000),下面两参数设置为MP4_INVALID_DURATION和MP4_MPEG4_AUDIO_TYPE。


bool MP4SetTrackESConfiguration(MP4FileHandle  hFile,
                          MP4TrackId   trackId,
                          const uint8_t*  pConfig,
                          uint32_t    configSize );
功能:设置音频解码信息(如果设置错误会导致没有声音)。  
返回:成功返回true,失败返回false。  
参数:hFile 文件句柄,trackId 音频的track id,pConfig 记录解码信息的二进制流,configSize 解码串的长度。  
注意:mpeg4ip 使用faac进行aac音频编码的,在编码时可以调用相应的函数得到二进制串pConfig和长度configSize,但是如果aac不是用faac编码的,这是需要自己填充pConfig,可以参考faac的实现,下面是一个填充结构例子:     
前五个字节为 AAC object types  LOW     2 接着4个字节为 码率index        16000      8 接着4个字节为 channels 个数                 1 应打印出的正确2进制形式为  00010 | 1000 | 0001 | 000                                                             2          8        1   
bool MP4WriteSample(MP4FileHandle  hFile,
                 MP4TrackId   trackId,
                 const uint8_t*   pBytes,     
                    uint32_t    numBytes,     
                    MP4Duration    duration DEFAULT(MP4_INVALID_DURATION),
                 MP4Duration    renderingOffset DEFAULT(0),     
                    bool       isSyncSample DEFAULT(true) );  
功能:写一帧视频数据或写一段音频数据。 
返回:成功返回true,失败返回false。 
参数:hFile 文件句柄,trackId 音频或视频的track id,pBytes为要写的数据流指针,numBytes为数据字节长度,duration为前一视频帧与当前视频帧之间的ticks数,或这是前一段音频数据和当前音频数据之间的ticks。isSyncSample 对视频来说是否为关键帧。 
注意:1,duration这个参数是用来实现音视频同步用的,如果设置错了会造成音视频不同步,甚至会出现crash现象(一般出现在调用MP4Close是crash)。 2,对于视频流MP4WriteSample函数每次调用是录制前一帧数据,用当前帧的时间戳和前一帧的时间戳计算duration值,然后把当前帧保存下来用做下次调用MP4WriteSample时用,写音频数据一样。        


void MP4AddH264SequenceParameterSet(MP4FileHandle  hFile,  
                                  MP4TrackId     trackId,     
                                    const uint8_t* pSequence,     
                                    uint16_t    sequenceLen ); 
和 
void MP4AddH264PictureParameterSet(MP4FileHandle  hFile,     
                                   MP4TrackId   trackId,
                                const uint8_t*  pPict,     
                                   uint16_t    pictLen );  


功能:添加序列参数集,添加图像参数集。 
参数:hFile 文件句柄,trackId 视频track id,pSequence和pPict为要写入的序列图像参数集的数据指针,sequenceLen和pictLen为串长度。 
注意:当检测到序列参数集或图像参数集更新时要调用MP4AddH264SequenceParameterSet或MP4AddH264PictureParameterSet进行更新。     


void MP4Close(MP4FileHandle hFile,     
              uint32_t flags DEFAULT(0) ); 
功能:关闭以打开的MP4文件。 
参数:hFile 文件句柄,flags 是否允许在关闭MP4文件前做一些额外的优化处理。 
注意:在录制较小的MP4文件时可以把flags设置为默认值,如果录制较大的文件最好把flags设置为MP4_CLOSE_DO_NOT_COMPUTE_BITRATE否则调用MP4Close函数会用掉很长的时间。

以下是代码中对mp4v2接口的封装类代码

#include "mp4v2/mp4v2.h"

class MP4Encoder
{
public:
    MP4Encoder(void);
    ~MP4Encoder(void);
    int MP4CreateFile(const char *sFileName,int Movie_Time_Scale=90000);
    int MP4AddH264Track(const uint8_t *sData, int nSize,int nWidth, int nHeight, int nFrameRate = 25,int Video_Time_Scale=90000);
    int MP4AddAACTrack(const uint8_t *sData, int nSize,int Audio_Time_Scale=44100);
    int MP4WriteH264Data(uint8_t *sData, int nSize);
    int MP4WriteAACData(const uint8_t *sData, int nSize);
    void MP4ReleaseFile();
private:

    MP4FileHandle m_hFile;
    MP4TrackId m_videoTrack, m_audioTrack;
};

#define DEFAULT_VIDEO_TRACK_NUM 3
#define DEFAULT_VIDEO_PROFILE_LEVEL 1
#define DEFAULT_AUDIO_PROFILE_LEVEL 2


MP4Encoder::MP4Encoder(void)
    : m_hFile(MP4_INVALID_FILE_HANDLE)
    , m_videoTrack(MP4_INVALID_TRACK_ID)
    , m_audioTrack(MP4_INVALID_TRACK_ID)
{
}

MP4Encoder::~MP4Encoder(void)
{
}

int MP4Encoder::MP4CreateFile(const char *sFileName,int Movie_Time_Scale)
{
    m_hFile = MP4Create(sFileName);
    if (m_hFile == MP4_INVALID_FILE_HANDLE)
    {
        printf("create mp4 file error!\n");
        return -1;
    }
    if (!MP4SetTimeScale(m_hFile, Movie_Time_Scale))
    {
        printf("set time Scale error!\n");
        return -1;
    }
    return 0;
}

int  MP4Encoder::MP4AddH264Track(const uint8_t *sData, int nSize,
    int nWidth, int nHeight, int nFrameRate ,int Video_Time_Scale)
{
    int sps, pps;
    for (sps = 0; sps < nSize;)
        if (sData[sps++] == 0x00 && sData[sps++] == 0x00 && sData[sps++] == 0x00
            && sData[sps++] == 0x01)
            break;
    for (pps = sps; pps < nSize;)
        if (sData[pps++] == 0x00 && sData[pps++] == 0x00 && sData[pps++] == 0x00
            && sData[pps++] == 0x01)
            break;
    if (sps >= nSize || pps >= nSize)
        return -1;
    m_videoTrack = MP4AddH264VideoTrack(m_hFile, Video_Time_Scale,
        Video_Time_Scale / nFrameRate, nWidth, nHeight,
        sData[sps + 1], sData[sps + 2], sData[sps + 3], DEFAULT_VIDEO_TRACK_NUM);//sps 后面的三个字节
    if (MP4_INVALID_TRACK_ID == m_videoTrack)
    {
        printf("add video track error!\n");
        return -1;
    }
    MP4SetVideoProfileLevel(m_hFile, DEFAULT_VIDEO_PROFILE_LEVEL);
    MP4AddH264SequenceParameterSet(m_hFile, m_videoTrack, sData + sps,pps - sps - 4);//sps  内容  起始位置+长度  
    MP4AddH264PictureParameterSet(m_hFile, m_videoTrack, sData + pps,nSize - pps);// pps 内容  起始位置+长度
    return 0;
}

int   MP4Encoder::MP4AddAACTrack(const uint8_t *sData, int nSize,int Audio_Time_Scale)
{
    m_audioTrack = MP4AddAudioTrack(m_hFile, Audio_Time_Scale,1024, MP4_MPEG4_AUDIO_TYPE);
    if (MP4_INVALID_TRACK_ID == m_audioTrack)
    {
        printf("add audio track error!\n");
        return -1;
    }
    MP4SetAudioProfileLevel(m_hFile, DEFAULT_AUDIO_PROFILE_LEVEL);
    if (!MP4SetTrackESConfiguration(m_hFile, m_audioTrack, sData, nSize))
    {
        printf("set track ESConfiguration error!\n");
        return -1;
    }

    return 0;
}

int MP4Encoder::MP4WriteH264Data(uint8_t *sData, int nSize)
{

    bool result = false;
    sData[0] = (nSize - 4) >> 24;
    sData[1] = (nSize - 4) >> 16;
    sData[2] = (nSize - 4) >> 8;
    sData[3] = nSize - 4;

    if(nSize<0||sData==NULL)
        return -1;

    if ((sData[4] & 0x1F) == 5)//判断I帧
        result = MP4WriteSample(m_hFile, m_videoTrack, sData, nSize,MP4_INVALID_DURATION,0,true);
    else
        result = MP4WriteSample(m_hFile, m_videoTrack, sData, nSize,MP4_INVALID_DURATION,0,false);
    if (!result)
    {
        printf("write h264 frame error!\n");
        return -1;
    }
    return 0;
}

int  MP4Encoder::MP4WriteAACData(const uint8_t *sData, int nSize)
{
    bool result = false;
    if(nSize<0||sData==NULL)
        return -1;
    result = MP4WriteSample(m_hFile, m_audioTrack, sData, nSize,MP4_INVALID_DURATION,0,true);
    if (!result)
    {
        printf("write aac frame error!\n");
        return -1;
    }
    return 0;
}

void MP4Encoder::MP4ReleaseFile()
{
    if (m_hFile != MP4_INVALID_FILE_HANDLE)
    {
        //MP4Close(m_hFile,MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
        MP4Close(m_hFile);
        m_hFile = MP4_INVALID_FILE_HANDLE;
    }
}

根据上面给出的类,在录制时只设置相关的参数即可。

MP4v2录制rtsp工程地址下载:https://download.csdn.net/download/unfound/10620870

缺失的dll下载地址:链接:https://pan.baidu.com/s/1TvpgLOs1YDPvY9HurpJ_iA 密码:fkoj

猜你喜欢

转载自blog.csdn.net/unfound/article/details/81950003
今日推荐