GPAC主要针对学生和内容创作者,代表了一个跨平台的多媒体框架,开发人员可以使用它在 LGPL 许可下制作开源媒体。GPAC多媒体框架兼容范围广泛的流行文件类型,从常见格式(如 AVI、MPEG 和 MOV)到复杂格式(如 MPEG-4 系统或 VRML/X3D)和 360 电影。
一、MP4Writer类
MP4Writer.h文件
#ifndef _MP4WRITER_H_
#define _MP4WRITER_H_
#define GPAC_MP4BOX_MINI
#include "gpac/setup.h"
#define MP4_AUDIO_TYPE_INVALID 0
#define MP4_AUDIO_TYPE_AAC_MAIN 1
#define MP4_AUDIO_TYPE_AAC_LC 2
#define MP4_AUDIO_TYPE_AAC_SSR 3
#define MP4_AUDIO_TYPE_AAC_LD 23
#ifdef __cplusplus
extern "C" {
#endif
void* MP4_Init();
s32 MP4_CreatFile(void *pCMP4Writer, char *strFileName);
s32 MP4_InitVideo265(void *pCMP4Writer, u32 TimeScale);
s32 MP4_Write265Sample(void *pCMP4Writer, u8 *pData, u32 Size, u64 TimeStamp);
s32 MP4_InitVideo264(void *pCMP4Writer, u32 TimeScale);
s32 MP4_Write264Sample(void *pCMP4Writer, u8 *pData, u32 Size, u64 TimeStamp);
s32 MP4_InitAudioAAC(void *pCMP4Writer, u8 AudioType, u32 SampleRate, u8 Channel, u32 TimeScale);
s32 MP4_WriteAACSample(void *pCMP4Writer, u8 *pData, u32 Size, u64 TimeStamp);
void MP4_CloseFile(void *pCMP4Writer);
void MP4_Exit(void *pCMP4Writer);
#ifdef __cplusplus
}
#endif
#endif
MP4Writer.cpp文件
#include "MP4Writer.h"
#include <Winsock2.h>
extern "C" {
#include "gpac/isomedia.h"
#include "gpac/constants.h"
#include "gpac/internal/media_dev.h"
}
#define INIT_STATUS 0
#define CONFIG_STATUS 1
#define CONFIG_FINISH 2
static s8 GetSampleRateID(u32 SamplRate)
{
switch (SamplRate)
{
case 96000: return 0;
case 88200: return 1;
case 64000: return 2;
case 48000: return 3;
case 44100: return 4;
case 32000: return 5;
case 24000: return 6;
case 22050: return 7;
case 16000: return 8;
case 12000: return 9;
case 11025: return 10;
case 8000 : return 11;
case 7350 : return 12;
default: return -1;
}
}
//gf_m4a_get_profile
static u8 GetAACProfile(u8 AudioType, u32 SampleRate, u8 Channel)
{
switch (AudioType)
{
case 2:
{
if (Channel <= 2) return (SampleRate <= 24000) ? 0x28 : 0x29;
if (Channel <= 5) return (SampleRate <= 48000) ? 0x2A : 0x2B;
return (SampleRate <= 48000) ? 0x50 : 0x51;
}
case 5:
{
if (Channel <= 2) return (SampleRate <= 24000) ? 0x2C : 0x2D;
if (Channel <= 5) return (SampleRate <= 48000) ? 0x2E : 0x2F;
return (SampleRate <= 48000) ? 0x52 : 0x53;
}
case 29:
{
if (Channel <= 2) return (SampleRate <= 24000) ? 0x30 : 0x31;
if (Channel <= 5) return (SampleRate <= 48000) ? 0x32 : 0x33;
return (SampleRate <= 48000) ? 0x54 : 0x55;
}
default:
{
if (Channel <= 2) return (SampleRate < 24000) ? 0x0E : 0x0F;
return 0x10;
}
}
}
static void GetAudioSpecificConfig(u8 AudioType, u8 SampleRateID, u8 Channel, u8 *pHigh, u8 *pLow)
{
u16 Config;
Config = (AudioType & 0x1f);
Config <<= 4;
Config |= SampleRateID & 0x0f;
Config <<= 4;
Config |= Channel & 0x0f;
Config <<= 3;
*pLow = Config & 0xff;
Config >>= 8;
*pHigh = Config & 0xff;
}
static u8* FindNalu(u8 *pStart, u32 Size, u8 *pNaluType, u32 *pNaluSize)
{
u8 *pEnd;
u8 *pCur;
u8 *pOut;
u8 NaluType;
if (4 >= Size)
return NULL;
pCur = pStart;
pEnd = pStart + Size - 4;
while (pCur < pEnd)
{
if ( (0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]) )
break;
pCur++;
}
if (pCur >= pEnd)
return NULL;
NaluType = (pCur[4] >> 1) & 0x3f;
*pNaluType = NaluType;
if (1 == NaluType || 19 == NaluType)
{
*pNaluSize = Size - (pCur - pStart);
return pCur;
}
pOut = pCur;
pCur += 5;
while (pCur <= pEnd)
{
if ( (0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]) )
break;
pCur++;
}
if (pCur <= pEnd)
{
*pNaluSize = pCur - pOut;
return pOut;
}
*pNaluSize = Size - (pOut - pStart);
return pOut;
}
static u8* FindNalu264(u8 *pStart, u32 Size, u8 *pNaluType, u32 *pNaluSize)
{
u8 *pEnd;
u8 *pCur;
u8 *pOut;
u8 NaluType;
if (4 >= Size)
return NULL;
pCur = pStart;
pEnd = pStart + Size - 4;
while (pCur < pEnd)
{
if ((0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]))
break;
pCur++;
}
if (pCur >= pEnd)
return NULL;
NaluType = (pCur[4]) & 0x1f;
*pNaluType = NaluType;
if (1 == NaluType || 5 == NaluType)
{
*pNaluSize = Size - (pCur - pStart);
return pCur;
}
pOut = pCur;
pCur += 5;
while (pCur <= pEnd)
{
if ((0 == pCur[0]) && (0 == pCur[1]) && (0 == pCur[2]) && (1 == pCur[3]))
break;
pCur++;
}
if (pCur <= pEnd)
{
*pNaluSize = pCur - pOut;
return pOut;
}
*pNaluSize = Size - (pOut - pStart);
return pOut;
}
// aac数据时记得除掉adts头
static int AdtsDemux(unsigned char * data, unsigned int size, unsigned char** raw, int* raw_size)
{
int ret = 1;
if (size < 7) {
ttf_human_trace("adts: demux size too small");
return 0;
}
unsigned char* p = data;
unsigned char* pend = data + size;
//unsigned char* startp = 0;
while (p < pend) {
// decode the ADTS.
// @see aac-iso-13818-7.pdf, page 26
// 6.2 Audio Data Transport Stream, ADTS
// @see https://github.com/ossrs/srs/issues/212#issuecomment-64145885
// byte_alignment()
// adts_fixed_header:
// 12bits syncword,
// 16bits left.
// adts_variable_header:
// 28bits
// 12+16+28=56bits
// adts_error_check:
// 16bits if protection_absent
// 56+16=72bits
// if protection_absent:
// require(7bytes)=56bits
// else
// require(9bytes)=72bits
//startp = p;
// for aac, the frame must be ADTS format.
// syncword 12 bslbf
p++;
// 4bits left.
// adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS
// ID 1 bslbf
// layer 2 uimsbf
// protection_absent 1 bslbf
int8_t pav = (*p++ & 0x0f);
//int8_t id = (pav >> 3) & 0x01;
int8_t protection_absent = pav & 0x01;
//if (id != 0x01) {
// //ttf_human_trace("adts: id must be 1(aac), actual 0(mp4a).");
// // well, some system always use 0, but actually is aac format.
// // for example, houjian vod ts always set the aac id to 0, actually 1.
// // we just ignore it, and alwyas use 1(aac) to demux.
// id = 0x01;
//}
//else {
// //ttf_human_trace("adts: id must be 1(aac), actual 1(mp4a).");
//}
//int16_t sfiv = (*p << 8) | (*(p + 1));
p += 2;
// profile 2 uimsbf
// sampling_frequency_index 4 uimsbf
// private_bit 1 bslbf
// channel_configuration 3 uimsbf
// original/copy 1 bslbf
// home 1 bslbf
//int8_t profile = (sfiv >> 14) & 0x03;
//int8_t sampling_frequency_index = (sfiv >> 10) & 0x0f;
//int8_t channel_configuration = (sfiv >> 6) & 0x07;
//int8_t Emphasis; @remark, Emphasis is removed, @see https://github.com/ossrs/srs/issues/212#issuecomment-64154736
// 4bits left.
// adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
// copyright_identification_bit 1 bslbf
// copyright_identification_start 1 bslbf
// frame_length 13 bslbf: Length of the frame including headers and error_check in bytes.
// use the left 2bits as the 13 and 12 bit,
// the frame_length is 13bits, so we move 13-2=11.
//int16_t frame_length = (sfiv << 11) & 0x1800;
//int32_t abfv = ((*p) << 16)
// | ((*(p + 1)) << 8)
// | (*(p + 2));
p += 3;
// frame_length 13 bslbf: consume the first 13-2=11bits
// the fh2 is 24bits, so we move right 24-11=13.
//frame_length |= (abfv >> 13) & 0x07ff;
// adts_buffer_fullness 11 bslbf
// number_of_raw_data_blocks_in_frame 2 uimsbf
// adts_error_check(), 1.A.2.2.3 Error detection
if (!protection_absent) {
if (size < 9) {
ttf_human_trace("adts: protection_absent disappare.");
return 0;
}
// crc_check 16 Rpchof
p += 2;
}
// TODO: check the sampling_frequency_index
// TODO: check the channel_configuration
// raw_data_blocks
//adts_codec_.protection_absent = protection_absent;
//adts_codec_.aac_object = srs_codec_aac_ts2rtmp((SrsAacProfile)profile);
//adts_codec_.sampling_frequency_index = sampling_frequency_index;
//adts_codec_.channel_configuration = channel_configuration;
//adts_codec_.frame_length = frame_length;
@see srs_audio_write_raw_frame().
TODO: FIXME: maybe need to resample audio.
//adts_codec_.sound_format = 10; // AAC
//if (sampling_frequency_index <= 0x0c && sampling_frequency_index > 0x0a) {
// adts_codec_.sound_rate = SrsCodecAudioSampleRate5512;
//}
//else if (sampling_frequency_index <= 0x0a && sampling_frequency_index > 0x07) {
// adts_codec_.sound_rate = SrsCodecAudioSampleRate11025;
//}
//else if (sampling_frequency_index <= 0x07 && sampling_frequency_index > 0x04) {
// adts_codec_.sound_rate = SrsCodecAudioSampleRate22050;
//}
//else if (sampling_frequency_index <= 0x04) {
// adts_codec_.sound_rate = SrsCodecAudioSampleRate44100;
//}
//else {
// adts_codec_.sound_rate = SrsCodecAudioSampleRate44100;
// //srs_warn("adts invalid sample rate for flv, rate=%#x", sampling_frequency_index);
//}
//adts_codec_.sound_type = srs_max(0, srs_min(1, channel_configuration - 1));
TODO: FIXME: finger it out the sound size by adts.
//adts_codec_.sound_size = 1; // 0(8bits) or 1(16bits).
// frame data.
*raw = p;
*raw_size = pend - p;
break;
}
return ret;
}
class MP4Writer
{
public:
MP4Writer();
~MP4Writer();
s32 CreatFile(char *strFileName);
s32 Init265(u32 TimeScale);
s32 Write265Sample(u8 *pData, u32 Size, u64 TimeStamp);
s32 Init264(u32 TimeScale);
s32 Write264Sample(u8 *pData, u32 Size, u64 TimeStamp);
s32 InitAAC(u8 AudioType, u32 SampleRate, u8 Channel, u32 TimeScale);
s32 WriteAACSample(u8 *pData, u32 Size, u64 TimeStamp);
void CloseFile();
private:
GF_ISOFile *m_ptFile;
u32 m_265TrackIndex;
u32 m_265StreamIndex;
u8 m_Video265Statue;
u32 m_264TrackIndex;
u32 m_264StreamIndex;
u8 m_Video264Statue;
s64 m_VideoTimeStampStart;
GF_HEVCConfig *m_ptHEVCConfig;
GF_HEVCParamArray* m_tHEVCNaluParam_VPS;
GF_HEVCParamArray* m_tHEVCNaluParam_SPS;
GF_HEVCParamArray* m_tHEVCNaluParam_PPS;
GF_AVCConfigSlot* m_tAVCConfig_VPS;
GF_AVCConfigSlot* m_tAVCConfig_SPS;
GF_AVCConfigSlot* m_tAVCConfig_PPS;
GF_AVCConfigSlot* m_tAVCConfig_SPS264;
GF_AVCConfigSlot* m_tAVCConfig_PPS264;
GF_AVCConfig *m_ptAVCConfig;
HEVCState *m_ptHEVCState;
AVCState *m_ptAVCState;
u32 m_AACTrackIndex;
u32 m_AACStreamIndex;
u8 m_AudioAACStatue;
s64 m_AudioTimeStampStart;
bool findfirsidr_;
void FreeAllMem();
};
MP4Writer::MP4Writer()
{
m_ptFile = NULL;
m_ptHEVCConfig = NULL;
m_ptAVCConfig = NULL;
m_Video265Statue = INIT_STATUS;
m_AudioAACStatue = INIT_STATUS;
m_Video264Statue = INIT_STATUS;
m_tHEVCNaluParam_VPS = NULL;
m_tHEVCNaluParam_SPS = NULL;
m_tHEVCNaluParam_PPS = NULL;
m_tAVCConfig_VPS = NULL;
m_tAVCConfig_SPS = NULL;
m_tAVCConfig_PPS = NULL;
m_tAVCConfig_SPS264 = NULL;
m_tAVCConfig_PPS264 = NULL;
m_ptHEVCState = NULL;
m_ptAVCState = NULL;
findfirsidr_ = false;
}
MP4Writer::~MP4Writer()
{
CloseFile();
}
s32 MP4Writer::CreatFile(char *strFileName)
{
if (NULL != m_ptFile)
{
return -1;
}
m_ptFile = gf_isom_open(strFileName, GF_ISOM_OPEN_WRITE, NULL);
if (NULL == m_ptFile)
{
return -1;
}
gf_isom_set_brand_info(m_ptFile, GF_ISOM_BRAND_MP42, 0);
return 0;
}
void MP4Writer::CloseFile()
{
if (m_ptFile)
{
gf_isom_close(m_ptFile);
m_ptFile = NULL;
FreeAllMem();
}
}
void MP4Writer::FreeAllMem()
{
if (m_ptHEVCConfig)
{
gf_odf_hevc_cfg_del(m_ptHEVCConfig);
//gf_list_del(m_ptHEVCConfig->param_array);
//free(m_ptHEVCConfig);
m_ptHEVCConfig = NULL;
}
if (m_ptAVCConfig) {
gf_odf_avc_cfg_del(m_ptAVCConfig);
//gf_list_del(m_ptAVCConfig->param_array);
//free(m_ptAVCConfig);
m_ptAVCConfig = NULL;
}
if (m_ptHEVCState) {
free(m_ptHEVCState);
m_ptHEVCState = NULL;
}
if (m_ptAVCState) {
free(m_ptAVCState);
m_ptAVCState = NULL;
}
m_tHEVCNaluParam_VPS = NULL;
m_tHEVCNaluParam_SPS = NULL;
m_tHEVCNaluParam_PPS = NULL;
m_tAVCConfig_VPS = NULL;
m_tAVCConfig_SPS = NULL;
m_tAVCConfig_PPS = NULL;
m_tAVCConfig_SPS264 = NULL;
m_tAVCConfig_PPS264 = NULL;
m_Video265Statue = INIT_STATUS;
m_AudioAACStatue = INIT_STATUS;
m_Video264Statue = INIT_STATUS;
findfirsidr_ = false;
}
s32 MP4Writer::Init265(u32 TimeScale)
{
if (NULL == m_ptFile || INIT_STATUS != m_Video265Statue)
return -1;
m_VideoTimeStampStart = -1;
m_265TrackIndex = gf_isom_new_track(m_ptFile, 0, GF_ISOM_MEDIA_VISUAL, TimeScale);
if (0 == m_265TrackIndex)
return -1;
if (GF_OK != gf_isom_set_track_enabled(m_ptFile, m_265TrackIndex, 1))
return -1;
m_ptHEVCConfig = gf_odf_hevc_cfg_new();
if (NULL == m_ptHEVCConfig)
return -1;
m_ptHEVCConfig->nal_unit_size = 4;
m_ptHEVCConfig->configurationVersion = 1;
if (GF_OK != gf_isom_hevc_config_new(m_ptFile, m_265TrackIndex, m_ptHEVCConfig, NULL, NULL, &m_265StreamIndex))
return -1;
GF_SAFEALLOC(m_tHEVCNaluParam_VPS, GF_HEVCParamArray);
GF_SAFEALLOC(m_tHEVCNaluParam_SPS, GF_HEVCParamArray);
GF_SAFEALLOC(m_tHEVCNaluParam_PPS, GF_HEVCParamArray);
GF_SAFEALLOC(m_tAVCConfig_VPS, GF_AVCConfigSlot);
GF_SAFEALLOC(m_tAVCConfig_SPS, GF_AVCConfigSlot);
GF_SAFEALLOC(m_tAVCConfig_PPS, GF_AVCConfigSlot);
if (GF_OK != gf_list_add(m_ptHEVCConfig->param_array, m_tHEVCNaluParam_VPS))
return -1;
if (GF_OK != gf_list_add(m_ptHEVCConfig->param_array, m_tHEVCNaluParam_SPS))
return -1;
if (GF_OK != gf_list_add(m_ptHEVCConfig->param_array, m_tHEVCNaluParam_PPS))
return -1;
m_tHEVCNaluParam_VPS->nalus = gf_list_new();
if (NULL == m_tHEVCNaluParam_VPS->nalus)
return -1;
m_tHEVCNaluParam_SPS->nalus = gf_list_new();
if (NULL == m_tHEVCNaluParam_SPS->nalus)
return -1;
m_tHEVCNaluParam_PPS->nalus = gf_list_new();
if (NULL == m_tHEVCNaluParam_PPS->nalus)
return -1;
m_tHEVCNaluParam_VPS->type = GF_HEVC_NALU_VID_PARAM;
m_tHEVCNaluParam_SPS->type = GF_HEVC_NALU_SEQ_PARAM;
m_tHEVCNaluParam_PPS->type = GF_HEVC_NALU_PIC_PARAM;
m_tHEVCNaluParam_VPS->array_completeness = 1;
m_tHEVCNaluParam_SPS->array_completeness = 1;
m_tHEVCNaluParam_PPS->array_completeness = 1;
if (GF_OK != gf_list_add(m_tHEVCNaluParam_VPS->nalus, m_tAVCConfig_VPS))
return -1;
if (GF_OK != gf_list_add(m_tHEVCNaluParam_SPS->nalus, m_tAVCConfig_SPS))
return -1;
if (GF_OK != gf_list_add(m_tHEVCNaluParam_PPS->nalus, m_tAVCConfig_PPS))
return -1;
m_ptHEVCState = (HEVCState *)malloc(sizeof(HEVCState));//gf_malloc
if (NULL == m_ptHEVCState)
return -1;
memset(m_ptHEVCState, 0, sizeof(HEVCState));
m_Video265Statue = CONFIG_STATUS;
return 0;
}
s32 MP4Writer::Write265Sample(u8 *pData, u32 Size, u64 TimeStamp)
{
u8 *pStart = pData;
u8 NaluType;
u32 NaluSize = 0;
s32 ID;
GF_ISOSample tISOSample;
if (INIT_STATUS == m_Video265Statue)
return -1;
while (1)
{
pData = FindNalu(pData + NaluSize, Size - (u32)(pData - pStart) - NaluSize, &NaluType, &NaluSize);
if (NULL == pData)
break;
if (CONFIG_FINISH == m_Video265Statue)
{
if (1 != NaluType && 19 != NaluType)
continue;
if (-1 == m_VideoTimeStampStart)
m_VideoTimeStampStart = TimeStamp;
*((u32 *)pData) = htonl(NaluSize - 4);
tISOSample.data = (char *)pData;
tISOSample.dataLength = NaluSize;
tISOSample.IsRAP = (19 == NaluType)? RAP: RAP_NO;
tISOSample.DTS = TimeStamp - m_VideoTimeStampStart;
tISOSample.CTS_Offset = 0;
tISOSample.nb_pack = 0;
if (GF_OK != gf_isom_add_sample(m_ptFile, m_265TrackIndex, m_265StreamIndex, &tISOSample))
{
*((u32 *)pData) = htonl(1);
return -1;
}
*((u32 *)pData) = htonl(1);
}
else if (CONFIG_STATUS == m_Video265Statue)
{
pData += 4;
NaluSize -= 4;
if (32 == NaluType && NULL == m_tAVCConfig_VPS->data)
{
ID = gf_media_hevc_read_vps((char *)pData , NaluSize, m_ptHEVCState);
m_ptHEVCConfig->avgFrameRate = m_ptHEVCState->vps[ID].rates[0].avg_pic_rate;
m_ptHEVCConfig->temporalIdNested = m_ptHEVCState->vps[ID].temporal_id_nesting;
m_ptHEVCConfig->constantFrameRate = m_ptHEVCState->vps[ID].rates[0].constand_pic_rate_idc;
m_ptHEVCConfig->numTemporalLayers = m_ptHEVCState->vps[ID].max_sub_layers;
m_tAVCConfig_VPS->id = ID;
m_tAVCConfig_VPS->size = (u16)NaluSize;
m_tAVCConfig_VPS->data = (char *)malloc(NaluSize);
if (NULL == m_tAVCConfig_VPS->data)
continue;
memcpy(m_tAVCConfig_VPS->data, pData, NaluSize);
}
else if (33 == NaluType && NULL == m_tAVCConfig_SPS->data)
{
ID = gf_media_hevc_read_sps((char *)pData, NaluSize, m_ptHEVCState);
m_ptHEVCConfig->tier_flag = m_ptHEVCState->sps[ID].ptl.tier_flag;
m_ptHEVCConfig->profile_idc = m_ptHEVCState->sps[ID].ptl.profile_idc;
m_ptHEVCConfig->profile_space = m_ptHEVCState->sps[ID].ptl.profile_space;
m_tAVCConfig_SPS->id = ID;
m_tAVCConfig_SPS->size = (u16)NaluSize;
m_tAVCConfig_SPS->data = (char *)malloc(NaluSize);
if (NULL == m_tAVCConfig_SPS->data)
continue;
memcpy(m_tAVCConfig_SPS->data, pData, NaluSize);
gf_isom_set_visual_info(m_ptFile, m_265TrackIndex, m_265StreamIndex, m_ptHEVCState->sps[ID].width, m_ptHEVCState->sps[ID].height);
}
else if (34 == NaluType && NULL == m_tAVCConfig_PPS->data)
{
ID = gf_media_hevc_read_pps((char *)pData, NaluSize, m_ptHEVCState);
m_tAVCConfig_PPS->id = ID;
m_tAVCConfig_PPS->size = (u16)NaluSize;
m_tAVCConfig_PPS->data = (char *)malloc(NaluSize);
if (NULL == m_tAVCConfig_PPS->data)
continue;
memcpy(m_tAVCConfig_PPS->data, pData, NaluSize);
}
else
{
continue;
}
if (m_tAVCConfig_VPS->data && m_tAVCConfig_SPS->data && m_tAVCConfig_PPS->data)
{
gf_isom_hevc_config_update(m_ptFile, m_265TrackIndex, m_265StreamIndex, m_ptHEVCConfig);
m_Video265Statue = CONFIG_FINISH;
if (m_ptHEVCState) {
free(m_ptHEVCState);
m_ptHEVCState = NULL;
}
}
}
}
return 0;
}
s32 MP4Writer::Init264(u32 TimeScale)
{
if (NULL == m_ptFile || INIT_STATUS != m_Video265Statue)
return -1;
m_VideoTimeStampStart = -1;
m_264TrackIndex = gf_isom_new_track(m_ptFile, 0, GF_ISOM_MEDIA_VISUAL, TimeScale);
if (0 == m_264TrackIndex)
return -1;
if (GF_OK != gf_isom_set_track_enabled(m_ptFile, m_264TrackIndex, 1))
return -1;
m_ptAVCConfig = gf_odf_avc_cfg_new();
if (NULL == m_ptAVCConfig)
return -1;
//m_ptAVCConfig->nal_unit_size = 4;
m_ptAVCConfig->configurationVersion = 1;
if (GF_OK != gf_isom_avc_config_new(m_ptFile, m_264TrackIndex, m_ptAVCConfig, NULL, NULL, &m_264StreamIndex))
return -1;
GF_SAFEALLOC(m_tAVCConfig_SPS264, GF_AVCConfigSlot);
GF_SAFEALLOC(m_tAVCConfig_PPS264, GF_AVCConfigSlot);
gf_list_add(m_ptAVCConfig->sequenceParameterSets, m_tAVCConfig_SPS264);
gf_list_add(m_ptAVCConfig->pictureParameterSets, m_tAVCConfig_PPS264);
m_ptAVCState = (AVCState *)malloc(sizeof(AVCState));//gf_malloc
if (NULL == m_ptAVCState)
return -1;
memset(m_ptAVCState, 0, sizeof(AVCState));
m_Video264Statue = CONFIG_STATUS;
return 0;
}
s32 MP4Writer::Write264Sample(u8 *pData, u32 Size, u64 TimeStamp)
{
u8 *pStart = pData;
u8 NaluType;
u32 NaluSize = 0;
s32 ID;
GF_ISOSample tISOSample;
if (INIT_STATUS == m_Video264Statue)
return -1;
while (1)
{
pData = FindNalu264(pData + NaluSize, Size - (u32)(pData - pStart) - NaluSize, &NaluType, &NaluSize);
if (NULL == pData)
break;
if (CONFIG_FINISH == m_Video264Statue)
{
if (!findfirsidr_) {
if (5 != NaluType)
continue;
else
findfirsidr_ = true;
}
if (1 != NaluType && 5 != NaluType)
continue;
if (-1 == m_VideoTimeStampStart)
m_VideoTimeStampStart = TimeStamp;
*((u32 *)pData) = htonl(NaluSize - 4);
tISOSample.data = (char *)pData;
tISOSample.dataLength = NaluSize;
tISOSample.IsRAP = (5 == NaluType) ? RAP : RAP_NO;
tISOSample.DTS = TimeStamp - m_VideoTimeStampStart;
tISOSample.CTS_Offset = 0;
tISOSample.nb_pack = 0;
int ret = gf_isom_add_sample(m_ptFile, m_264TrackIndex, m_264StreamIndex, &tISOSample);
if (GF_OK != ret)
{
/
return -1;
}
/
}
else if (CONFIG_STATUS == m_Video264Statue)
{
pData += 4;
NaluSize -= 4;
if (7 == NaluType && NULL == m_tAVCConfig_SPS264->data)
{
ID = gf_media_avc_read_sps((char *)pData, NaluSize, m_ptAVCState,0,NULL);
m_ptAVCConfig->AVCProfileIndication = m_ptAVCState->sps[ID].profile_idc;
m_ptAVCConfig->profile_compatibility = m_ptAVCState->sps[ID].prof_compat;
m_ptAVCConfig->AVCLevelIndication = m_ptAVCState->sps[ID].level_idc;
m_tAVCConfig_SPS264->id = ID;
m_tAVCConfig_SPS264->size = (u16)NaluSize;
m_tAVCConfig_SPS264->data = (char *)malloc(NaluSize);
if (NULL == m_tAVCConfig_SPS264->data)
continue;
memcpy(m_tAVCConfig_SPS264->data, pData, NaluSize);
gf_isom_set_visual_info(m_ptFile, m_264TrackIndex, m_264StreamIndex, m_ptAVCState->sps[ID].width, m_ptAVCState->sps[ID].height);
}
else if (8 == NaluType && NULL == m_tAVCConfig_PPS264->data)
{
m_tAVCConfig_PPS264->id = ID;
m_tAVCConfig_PPS264->size = (u16)NaluSize;
m_tAVCConfig_PPS264->data = (char *)malloc(NaluSize);
if (NULL == m_tAVCConfig_PPS264->data)
continue;
memcpy(m_tAVCConfig_PPS264->data, pData, NaluSize);
}
else
{
continue;
}
if (m_tAVCConfig_SPS264->data && m_tAVCConfig_PPS264->data)
{
gf_isom_avc_config_update(m_ptFile, m_264TrackIndex, m_264StreamIndex, m_ptAVCConfig);
m_Video264Statue = CONFIG_FINISH;
if (m_ptAVCState) {
free(m_ptAVCState);
m_ptAVCState = NULL;
}
}
}
}
return 0;
}
s32 MP4Writer::InitAAC(u8 AudioType, u32 SampleRate, u8 Channel, u32 TimeScale)
{
GF_ESD *ptStreamDesc;
s8 SampleRateID;
u16 AudioConfig = 0;
u8 AACProfile;
s32 res = 0;
if (NULL == m_ptFile || INIT_STATUS != m_AudioAACStatue)
return -1;
m_AudioTimeStampStart = -1;
m_AACTrackIndex = gf_isom_new_track(m_ptFile, 0, GF_ISOM_MEDIA_AUDIO, TimeScale);
if (0 == m_AACTrackIndex)
return -1;
if (GF_OK != gf_isom_set_track_enabled(m_ptFile, m_AACTrackIndex, 1))
return -1;
SampleRateID = GetSampleRateID(SampleRate);
if (0 > SampleRateID)
return -1;
GetAudioSpecificConfig(AudioType, (u8)SampleRateID, Channel, (u8*)(&AudioConfig), ((u8*)(&AudioConfig))+1);
ptStreamDesc = gf_odf_desc_esd_new(SLPredef_MP4);
ptStreamDesc->slConfig->timestampResolution = TimeScale;
ptStreamDesc->decoderConfig->streamType = GF_STREAM_AUDIO;
//ptStreamDesc->decoderConfig->bufferSizeDB = 20; //这参数干什么的
ptStreamDesc->decoderConfig->objectTypeIndication = 0x40;//ptStreamDesc->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_AAC_MPEG4;
ptStreamDesc->decoderConfig->decoderSpecificInfo->dataLength = 2;
ptStreamDesc->decoderConfig->decoderSpecificInfo->data = (char *)&AudioConfig;
ptStreamDesc->ESID = gf_isom_get_track_id(m_ptFile, m_AACTrackIndex);
if (GF_OK != gf_isom_new_mpeg4_description(m_ptFile, m_AACTrackIndex, ptStreamDesc, NULL, NULL, &m_AACStreamIndex))
{
res = -1;
goto ERR;
}
if (gf_isom_set_audio_info(m_ptFile, m_AACTrackIndex, m_AACStreamIndex, SampleRate, Channel, 16, GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET))
{
res = -1;
goto ERR;
}
AACProfile = GetAACProfile(AudioType, SampleRate, Channel);
gf_isom_set_pl_indication(m_ptFile, GF_ISOM_PL_AUDIO, AACProfile);
m_AudioAACStatue = CONFIG_FINISH;
ERR:
ptStreamDesc->decoderConfig->decoderSpecificInfo->data = NULL;
gf_odf_desc_del((GF_Descriptor *)ptStreamDesc);
return res;
}
s32 MP4Writer::WriteAACSample(u8 *pData, u32 Size, u64 TimeStamp)
{
GF_ISOSample tISOSample;
if (CONFIG_FINISH != m_AudioAACStatue)
return 0;
if (!findfirsidr_) {
return 0;
}
if (-1 == m_AudioTimeStampStart)
m_AudioTimeStampStart = TimeStamp;
tISOSample.IsRAP = RAP;
tISOSample.dataLength = Size;
tISOSample.data = (char *)pData;
tISOSample.DTS = TimeStamp - m_AudioTimeStampStart;
tISOSample.CTS_Offset = 0;
tISOSample.nb_pack = 0;
if (GF_OK != gf_isom_add_sample(m_ptFile, m_AACTrackIndex, m_AACStreamIndex, &tISOSample))
return -1;
return 0;
}
extern "C" {
void* MP4_Init()
{
return (void *)(new MP4Writer());
}
s32 MP4_CreatFile(void *pCMP4Writer, char *strFileName)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->CreatFile(strFileName);
}
s32 MP4_InitVideo265(void *pCMP4Writer, u32 TimeScale)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->Init265(TimeScale);
}
s32 MP4_Write265Sample(void *pCMP4Writer, u8 *pData, u32 Size, u64 TimeStamp)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->Write265Sample(pData, Size, TimeStamp);
}
s32 MP4_InitVideo264(void * pCMP4Writer, u32 TimeScale)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->Init264(TimeScale);
}
s32 MP4_Write264Sample(void * pCMP4Writer, u8 * pData, u32 Size, u64 TimeStamp)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->Write264Sample(pData, Size, TimeStamp);
}
s32 MP4_InitAudioAAC(void *pCMP4Writer, u8 AudioType, u32 SampleRate, u8 Channel, u32 TimeScale)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->InitAAC(AudioType, SampleRate, Channel, TimeScale);
}
s32 MP4_WriteAACSample(void *pCMP4Writer, u8 *pData, u32 Size, u64 TimeStamp)
{
if (NULL == pCMP4Writer)
return -1;
return ((MP4Writer *)pCMP4Writer)->WriteAACSample(pData, Size, TimeStamp);
}
void MP4_CloseFile(void *pCMP4Writer)
{
if (NULL == pCMP4Writer)
return;
((MP4Writer *)pCMP4Writer)->CloseFile();
}
void MP4_Exit(void *pCMP4Writer)
{
if (NULL == pCMP4Writer)
return;
delete pCMP4Writer;
}
}
注意:
1)MP4_InitAudioAAC那个audiotype指的是aac的profile,lc就是2。
GF_M4A_AAC_MAIN = 1,
GF_M4A_AAC_LC = 2,
GF_M4A_AAC_SSR = 3,
GF_M4A_AAC_LTP = 4,
GF_M4A_AAC_SBR = 5,
GF_M4A_AAC_SCALABLE = 6,
GF_M4A_TWINVQ = 7,
GF_M4A_CELP = 8,
GF_M4A_HVXC = 9,
GF_M4A_TTSI = 12,
GF_M4A_MAIN_SYNTHETIC = 13,
GF_M4A_WAVETABLE_SYNTHESIS = 14,
GF_M4A_GENERAL_MIDI = 15,
GF_M4A_ALGO_SYNTH_AUDIO_FX = 16,
GF_M4A_ER_AAC_LC = 17,
GF_M4A_ER_AAC_LTP = 19,
GF_M4A_ER_AAC_SCALABLE = 20,
GF_M4A_ER_TWINVQ = 21,
GF_M4A_ER_BSAC = 22,
GF_M4A_ER_AAC_LD = 23,
GF_M4A_ER_CELP = 24,
GF_M4A_ER_HVXC = 25,
GF_M4A_ER_HILN = 26,
GF_M4A_ER_PARAMETRIC = 27,
GF_M4A_SSC = 28,
GF_M4A_AAC_PS = 29,
GF_M4A_LAYER1 = 32,
GF_M4A_LAYER2 = 33,
GF_M4A_LAYER3 = 34,
GF_M4A_DST = 35,
GF_M4A_ALS = 36
2)填aac数据时记得除掉adts头,使用上面的AdtsDemux函数即可。
二、写h264流数据为mp4文件步骤
void * handleMp4 = NULL;
handleMp4 = MP4_Init();
Mp4_CreateFile(handleMp4,"d:\\test.mp4");
MP4_InitVideo264(handleMp4,1000);
//写h264流数据,包含00 00 00 01标记头的数据
MP4_Write264Sample(handleMp4,h264data,datasize,timestamp);//h264data表示264数据,datasize表示数据大小,timestamp表示时间戳,需要转换成基准为1000时间戳(1000\90000两种)
...
MP4_CloseFile(handleMp4);
MP4_Exit(handleMp4);
三、写h265流数据为mp4文件步骤
void * handleMp4 = NULL;
handleMp4 = MP4_Init();
Mp4_CreateFile(handleMp4,"d:\\test.mp4");
MP4_InitVideo265(handleMp4,1000);
//写h265流数据,包含00 00 00 01标记头的数据
MP4_Write265Sample(handleMp4,h264data,datasize,timestamp);//h264data表示264数据,datasize表示数据大小,timestamp表示时间戳,需要转换成基准为1000时间戳
...
MP4_CloseFile(handleMp4);
MP4_Exit(handleMp4);
四、利用命令行生成mp4文件
GPAC除了 GPAC 核心,完整的软件包还包括一个 GPAC 插件:MPEG-4 BIF(场景解码器)、MPEG-4 ODF(对象描述符解码器)、MPEG-4 LASeR(场景解码器)、MPEG-4 SAF 解复用器、文本 MPEG- 4 加载器(支持未压缩的 MPEG-4 BT 和 XMT、VRML 和 X3D 文本格式)、图像包(支持 PNG、JPEG、BMP、JPEG2000)等。
1)将h265流转换成MP4,执行下面命令行。
./MP4Box -v -add Catus_1920x1080_50_qp32.bin:FMT=HEVC -fps 50 -new output.mp4
./MP4Box -add name_of_annexB_bitstream.(bit,bin,265) -fps 50 -new output.mp4
播放h265视频文件信息,执行下面命令。
./MP4Client output.mp4
2)将h265流转换成ts,执行下面命令行。
./mp42ts -prog=hevc.mp4 -dst-file=test.ts
播放h265视频文件流信息,执行下面命令。
./MP4Client test.ts
备注:
1、GPAC模块下载链接
GitHub - gpac/gpac: Modular Multimedia framework for packaging, streaming and playing your favorite content, see http://netflix.gpac.io或Downloads | GPAC
2、编译指导
Build Introduction · gpac/gpac Wiki · GitHub
Windows:GPAC Build Guide for Windows · gpac/gpac Wiki · GitHub
Linux:GPAC Build Guide for Linux · gpac/gpac Wiki · GitHub
到此这篇关于C++利用GPAC实现生成MP4文件的示例代码的文章就介绍到这了,更多相关C++ GPAC生成MP4文件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!