音视频封装demo:将h264数据和aac数据封装(mux)成FLV文件(纯手工,不依赖第三方开源库)
#include "h264_nalu.h"
#include "aac_adts.h"
#include "flv.h"
static int generateFlvHeader(int hasVideo, int hasAudio, uint8_t *pOutData, uint32_t *pOutDataLen)
{
T_FlvHeader flvheader = {0};
if(!pOutData || !pOutDataLen)
{
printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__);
return -1;
}
flvheader.signature[0] = 'F';
flvheader.signature[1] = 'L';
flvheader.signature[2] = 'V';
flvheader.version = 0x01;
flvheader.type_flag = AUDIO_SUPPORT(hasAudio) | VIDEO_SUPPORT(hasVideo);
flvheader.data_offset = 0x09000000; /* 9 Bytes, size of flv header. big-endian. */
memcpy(pOutData, (uint8_t *)&flvheader, sizeof(T_FlvHeader));
*pOutDataLen = SIZE_FLV_HEADER;
return 0;
}
static int generatePreviousTagSize(uint32_t size, uint8_t *pOutData, uint32_t *pOutDataLen)
{
if(!pOutData || !pOutDataLen)
{
printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__);
return -1;
}
/* storge in file with big-endian */
pOutData[0] = (uint8_t)((size >> 24) & 0xFF);
pOutData[1] = (uint8_t)((size >> 16) & 0xFF);
pOutData[2] = (uint8_t)((size >> 8) & 0xFF);
pOutData[3] = (uint8_t)(size & 0xFF);
*pOutDataLen = SIZE_PREVIOUS_TAG_SIZE;
return 0;
}
static int generateScriptTag(uint8_t *pOutData, uint32_t *pOutDataLen)
{
PT_FlvTag ptScriptTag = NULL;
char *pString = NULL;
uint32_t stringLen = -1;
uint32_t dataSize = -1;
uint32_t amf2ArrayCnt = 0; /* have no member in this demo! */
uint32_t curPos = 0;
#if 0
/* have no member in this demo, so not need to convert! */
union{
double d;
uint8_t c[8];
} un;
#endif
if(!pOutData || !pOutDataLen)
{
printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__);
return -1;
}
ptScriptTag = (PT_FlvTag)pOutData;
//dataSize = -1; // calcurate bihind.
//*pOutDataLen = -1; // calcurate bihind.
ptScriptTag->flvheader.TagType = FLVTAG_TYPE_SCRIPT;
//ptScriptTag->flvheader.DataSize[0] = -1; // calcurate bihind.
//ptScriptTag->flvheader.DataSize[1] = -1;
//ptScriptTag->flvheader.DataSize[2] = -1;
ptScriptTag->flvheader.Timestamp[0] = 0;
ptScriptTag->flvheader.Timestamp[1] = 0;
ptScriptTag->flvheader.Timestamp[2] = 0;
ptScriptTag->flvheader.TimestampExtended = 0;
ptScriptTag->flvheader.StreamID[0] = 0;
ptScriptTag->flvheader.StreamID[1] = 0;
ptScriptTag->flvheader.StreamID[2] = 0;
/* AMF1 */
pString = "onMetaData";
stringLen = strlen(pString);
ptScriptTag->flvdata[curPos + 0] = AMF_DATA_TYPE_STRING;
ptScriptTag->flvdata[curPos + 1] = (uint8_t)((stringLen >> 8) & 0xFF);
ptScriptTag->flvdata[curPos + 2] = (uint8_t)(stringLen & 0xFF);
memcpy(&ptScriptTag->flvdata[curPos + 3], pString, stringLen);
curPos += (3 + stringLen);
/* AMF2 */
ptScriptTag->flvdata[curPos + 0] = AMF_DATA_TYPE_MIXEDARRAY;
ptScriptTag->flvdata[curPos + 1] = (uint8_t)((amf2ArrayCnt >> 24) & 0xFF);
ptScriptTag->flvdata[curPos + 2] = (uint8_t)((amf2ArrayCnt >> 16) & 0xFF);
ptScriptTag->flvdata[curPos + 3] = (uint8_t)((amf2ArrayCnt >> 8) & 0xFF);
ptScriptTag->flvdata[curPos + 4] = (uint8_t)((amf2ArrayCnt >> 0) & 0xFF);
curPos += 5;
#if 0 /* reference to `uint32_t amf2ArrayCnt = ...` */
pString = "duration";
stringLen = strlen(pString);
ptScriptTag->flvdata[curPos + 0] = (uint8_t)((stringLen >> 8) & 0xFF);
ptScriptTag->flvdata[curPos + 1] = (uint8_t)(stringLen & 0xFF);
memcpy(&ptScriptTag->flvdata[curPos + 2], pString, stringLen);
curPos += (2 + stringLen);
un.d = 30; /* Second */
ptScriptTag->flvdata[curPos + 0] = AMF_DATA_TYPE_NUMBER;
ptScriptTag->flvdata[curPos + 1] = un.c[7];
ptScriptTag->flvdata[curPos + 2] = un.c[6];
ptScriptTag->flvdata[curPos + 3] = un.c[5];
ptScriptTag->flvdata[curPos + 4] = un.c[4];
ptScriptTag->flvdata[curPos + 5] = un.c[3];
ptScriptTag->flvdata[curPos + 6] = un.c[2];
ptScriptTag->flvdata[curPos + 7] = un.c[1];
ptScriptTag->flvdata[curPos + 8] = un.c[0];
curPos += (1+8);
#endif
/* end */
ptScriptTag->flvdata[curPos + 0] = 0x00;
ptScriptTag->flvdata[curPos + 1] = 0x00;
ptScriptTag->flvdata[curPos + 2] = AMF_DATA_TYPE_OBJECT_END;
curPos += 3;
// now we can calculate it.
dataSize = curPos;
*pOutDataLen = SIZE_FLV_TAG_HEADER + dataSize;
ptScriptTag->flvheader.DataSize[0] = (uint8_t)((dataSize >> 16 & 0xFF));
ptScriptTag->flvheader.DataSize[1] = (uint8_t)((dataSize >> 8 & 0xFF));
ptScriptTag->flvheader.DataSize[2] = (uint8_t)(dataSize & 0xFF);
return 0;
}
static int generateAvcSequenceHeader(uint8_t *spsBuf, uint16_t spsLen, uint8_t *ppsBuf, uint16_t ppsLen,
uint32_t timestamp_ms, uint8_t *pOutData, uint32_t *pOutDataLen)
{
PT_FlvTag ptVideoTag = NULL;
uint32_t dataSize = 0;
if(!spsBuf || !spsLen || !ppsBuf || !ppsLen || !pOutData || !pOutDataLen)
{
printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__);
return -1;
}
ptVideoTag = (PT_FlvTag)pOutData;
dataSize = (13 + spsLen) + (3 + ppsLen);
*pOutDataLen = SIZE_FLV_TAG_HEADER + dataSize;
ptVideoTag->flvheader.TagType = FLVTAG_TYPE_VIDEO;
ptVideoTag->flvheader.DataSize[0] = (uint8_t)((dataSize >> 16 & 0xFF));
ptVideoTag->flvheader.DataSize[1] = (uint8_t)((dataSize >> 8 & 0xFF));
ptVideoTag->flvheader.DataSize[2] = (uint8_t)(dataSize & 0xFF);
ptVideoTag->flvheader.Timestamp[0] = (uint8_t)((timestamp_ms >> 16) & 0xFF);
ptVideoTag->flvheader.Timestamp[1] = (uint8_t)((timestamp_ms >> 8) & 0xFF);
ptVideoTag->flvheader.Timestamp[2] = (uint8_t)((timestamp_ms) & 0xFF);
ptVideoTag->flvheader.TimestampExtended = (uint8_t)((timestamp_ms >> 24) & 0xFF);
ptVideoTag->flvheader.StreamID[0] = 0;
ptVideoTag->flvheader.StreamID[1] = 0;
ptVideoTag->flvheader.StreamID[2] = 0;
ptVideoTag->flvdata[0] = ((VIDEOTAG_FRAMETYPE_KEYFRAME << 4) |\
(VIDEOTAG_CODECID_AVC)); /* 0x17, keyframe, avc */
/* next for AVCVIDEOPACKET */
ptVideoTag->flvdata[1] = AVC_PACKET_TYPE_SEQUENCE_HEADER; /* AVCPacketType: 0, AVC sequence header */
ptVideoTag->flvdata[2] = 0x00; /* CompositionTime: AVCPacketType != 1, so it is 0, otherwise data[2~4] is CTS */
ptVideoTag->flvdata[3] = 0x00; /* CompositionTime: AVCPacketType != 1, so it is 0, otherwise data[2~4] is CTS */
ptVideoTag->flvdata[4] = 0x00; /* CompositionTime: AVCPacketType != 1, so it is 0, otherwise data[2~4] is CTS */
/* next for AVCDecoderConfigurationRecord */
ptVideoTag->flvdata[5] = 0x01; /* ConfigurationVersion: always 0x01*/
ptVideoTag->flvdata[6] = spsBuf[1]; /* AVCProfileIndication: the first byte after the 'nalu type'(buf no include 'start code') */
ptVideoTag->flvdata[7] = spsBuf[2]; /* profile_compatibility: the second byte after the 'nalu type'(buf no include 'start code') */
ptVideoTag->flvdata[8] = spsBuf[3]; /* AVCLevelIndication: the third byte after the 'nalu type'(buf no include 'start code') */
ptVideoTag->flvdata[9] = 0xFF; /* lengthSi*usOne: always 0xFF, bit[7:2]: '111111b'reversed */
ptVideoTag->flvdata[10] = 0xE1; /* NumOfSequenceParmeterSets: always 0xE1, bit[7:5]: '111b'reversed */
ptVideoTag->flvdata[11] = (uint8_t)((spsLen >> 8) & 0xFF); /* SequenceParamterSetLength: big-endian, H */
ptVideoTag->flvdata[12] = (uint8_t)(spsLen & 0xFF); /* SequenceParamterSetLength: big-endian, L */
memcpy(&ptVideoTag->flvdata[13], spsBuf, spsLen);
ptVideoTag->flvdata[13+spsLen] = 0x01; /* NumOfPictureParmeterSets: always 0x01 */
ptVideoTag->flvdata[13+spsLen+1] = (uint8_t)((ppsLen >> 8) & 0xFF); /* PictureParamterSetLength: big-endian, H */
ptVideoTag->flvdata[13+spsLen+2] = (uint8_t)(ppsLen& 0xFF); /* PictureParamterSetLength: big-endian, L */
memcpy(&ptVideoTag->flvdata[13+spsLen+3], ppsBuf, ppsLen);
return 0;
}
static int generateAvcNALU(uint8_t *pNaluData, uint32_t naluDataLen, uint32_t isIDRNalu, uint32_t timestamp_ms, uint8_t *pOutData, uint32_t *pOutDataLen)
{
PT_FlvTag ptVideoTag = NULL;
uint32_t dataSize = 0;
if(!pNaluData || !naluDataLen || !pOutData || !pOutDataLen)
{
printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__);
return -1;
}
ptVideoTag = (PT_FlvTag)pOutData;
dataSize = 9 + naluDataLen;
*pOutDataLen = SIZE_FLV_TAG_HEADER + dataSize;
ptVideoTag->flvheader.TagType = FLVTAG_TYPE_VIDEO;
ptVideoTag->flvheader.DataSize[0] = (uint8_t)((dataSize >> 16 & 0xFF));
ptVideoTag->flvheader.DataSize[1] = (uint8_t)((dataSize >> 8 & 0xFF));
ptVideoTag->flvheader.DataSize[2] = (uint8_t)(dataSize & 0xFF);
ptVideoTag->flvheader.Timestamp[0] = (uint8_t)((timestamp_ms >> 16) & 0xFF);
ptVideoTag->flvheader.Timestamp[1] = (uint8_t)((timestamp_ms >> 8) & 0xFF);
ptVideoTag->flvheader.Timestamp[2] = (uint8_t)((timestamp_ms) & 0xFF);
ptVideoTag->flvheader.TimestampExtended = (uint8_t)((timestamp_ms >> 24) & 0xFF);
ptVideoTag->flvheader.StreamID[0] = 0;
ptVideoTag->flvheader.StreamID[1] = 0;
ptVideoTag->flvheader.StreamID[2] = 0;
if(isIDRNalu)
{
ptVideoTag->flvdata[0] = ((VIDEOTAG_FRAMETYPE_KEYFRAME << 4) |\
(VIDEOTAG_CODECID_AVC)); /* 0x17, keyframe, avc */
}
else
{
ptVideoTag->flvdata[0] = ((VIDEOTAG_FRAMETYPE_INTER_FRAME << 4) |\
(VIDEOTAG_CODECID_AVC)); /* 0x27, inter frame, avc */
}
/* next for AVCVIDEOPACKET */
ptVideoTag->flvdata[1] = AVC_PACKET_TYPE_NALU; /* AVCPacketType: 1, NALU */
ptVideoTag->flvdata[2] = 0x00; /* CompositionTime: CTS = 0, because no b'frame */
ptVideoTag->flvdata[3] = 0x00; /* CompositionTime: CTS = 0, because no b'frame */
ptVideoTag->flvdata[4] = 0x00; /* CompositionTime: CTS = 0, because no b'frame */
ptVideoTag->flvdata[5] = (ui