media_desc_t *faac_create_audio_sdp (CAudioProfile *pConfig, bool *mpeg4, bool *isma_compliant, uint8_t *audioProfile, uint8_t **audioConfig, uint32_t *audioConfigLen) { media_desc_t *sdpMediaAudio; format_list_t *sdpMediaAudioFormat; rtpmap_desc_t *sdpAudioRtpMap; char audioFmtpBuf[512]; faac_mp4_fileinfo(pConfig, mpeg4, isma_compliant, audioProfile, audioConfig, audioConfigLen, NULL); sdpMediaAudio = MALLOC_STRUCTURE(media_desc_t); memset(sdpMediaAudio, 0, sizeof(*sdpMediaAudio)); sdpMediaAudioFormat = MALLOC_STRUCTURE(format_list_t); memset(sdpMediaAudioFormat, 0, sizeof(*sdpMediaAudioFormat)); sdpMediaAudioFormat->media = sdpMediaAudio; sdpMediaAudioFormat->fmt = strdup("97"); sdpAudioRtpMap = MALLOC_STRUCTURE(rtpmap_desc_t); memset(sdpAudioRtpMap, 0, sizeof(*sdpAudioRtpMap)); sdpAudioRtpMap->clock_rate = pConfig->GetIntegerValue(CFG_AUDIO_SAMPLE_RATE); char* sConfig = MP4BinaryToBase16(*audioConfig, *audioConfigLen); if(pConfig->GetBoolValue(CFG_RTP_RFC3016)) { sdpAudioRtpMap->encode_name = strdup("MP4A-LATM"); sprintf(audioFmtpBuf, "profile-level-id=15;object=2;cpresent=0; config=%s ", sConfig); } else { sdp_add_string_to_list(&sdpMediaAudio->unparsed_a_lines, "a=mpeg4-esid:10"); sdpAudioRtpMap->encode_name = strdup("mpeg4-generic"); sprintf(audioFmtpBuf, "streamtype=5; profile-level-id=15; mode=AAC-hbr; config=%s; " "SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;", sConfig); } free(sConfig); sdpMediaAudioFormat->fmt_param = strdup(audioFmtpBuf); sdpMediaAudioFormat->rtpmap = sdpAudioRtpMap; sdpMediaAudio->fmt = sdpMediaAudioFormat; return sdpMediaAudio; }
extern "C" bool MP4AV_RfcIsmaHinter( MP4FileHandle mp4File, MP4TrackId mediaTrackId, bool interleave, u_int16_t maxPayloadSize) { // gather information, and check for validity u_int32_t numSamples = MP4GetTrackNumberOfSamples(mp4File, mediaTrackId); if (numSamples == 0) { return false; } u_int32_t timeScale = MP4GetTrackTimeScale(mp4File, mediaTrackId); if (timeScale == 0) { return false; } u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, mediaTrackId); if (audioType != MP4_MPEG4_AUDIO_TYPE && !MP4_IS_AAC_AUDIO_TYPE(audioType)) { return false; } u_int8_t mpeg4AudioType = MP4GetTrackAudioMpeg4Type(mp4File, mediaTrackId); if (audioType == MP4_MPEG4_AUDIO_TYPE) { // check that track contains either MPEG-4 AAC or CELP if (!MP4_IS_MPEG4_AAC_AUDIO_TYPE(mpeg4AudioType) && mpeg4AudioType != MP4_MPEG4_CELP_AUDIO_TYPE) { return false; } } MP4Duration sampleDuration = MP4AV_GetAudioSampleDuration(mp4File, mediaTrackId); if (sampleDuration == MP4_INVALID_DURATION) { return false; } /* get the ES configuration */ u_int8_t* pConfig = NULL; u_int32_t configSize; uint8_t channels; if (MP4GetTrackESConfiguration(mp4File, mediaTrackId, &pConfig, &configSize) == false) return false; if (!pConfig) { return false; } channels = MP4AV_AacConfigGetChannels(pConfig); /* convert ES Config into ASCII form */ char* sConfig = MP4BinaryToBase16(pConfig, configSize); free(pConfig); if (!sConfig) { return false; } /* create the appropriate SDP attribute */ uint sdpBufLen = strlen(sConfig) + 256; char* sdpBuf = (char*)malloc(sdpBufLen); if (!sdpBuf) { free(sConfig); return false; } // now add the hint track MP4TrackId hintTrackId = MP4AddHintTrack(mp4File, mediaTrackId); if (hintTrackId == MP4_INVALID_TRACK_ID) { free(sConfig); free(sdpBuf); return false; } u_int8_t payloadNumber = MP4_SET_DYNAMIC_PAYLOAD; char buffer[10]; if (channels != 1) { snprintf(buffer, sizeof(buffer), "%u", channels); } if (MP4SetHintTrackRtpPayload(mp4File, hintTrackId, "mpeg4-generic", &payloadNumber, 0, channels != 1 ? buffer : NULL) == false) { MP4DeleteTrack(mp4File, hintTrackId); free(sConfig); free(sdpBuf); return false; } MP4Duration maxLatency; bool OneByteHeader = false; if (mpeg4AudioType == MP4_MPEG4_CELP_AUDIO_TYPE) { snprintf(sdpBuf, sdpBufLen, "a=fmtp:%u " "streamtype=5; profile-level-id=15; mode=CELP-vbr; config=%s; " "SizeLength=6; IndexLength=2; IndexDeltaLength=2; Profile=0;" "\015\012", payloadNumber, sConfig); // 200 ms max latency for ISMA profile 1 maxLatency = timeScale / 5; OneByteHeader = true; } else { // AAC snprintf(sdpBuf, sdpBufLen, "a=fmtp:%u " "streamtype=5; profile-level-id=15; mode=AAC-hbr; config=%s; " "SizeLength=13; IndexLength=3; IndexDeltaLength=3;" "\015\012", payloadNumber, sConfig); // 500 ms max latency for ISMA profile 1 maxLatency = timeScale / 2; } /* add this to the track's sdp */ bool val = MP4AppendHintTrackSdp(mp4File, hintTrackId, sdpBuf); free(sConfig); free(sdpBuf); if (val == false) { MP4DeleteTrack(mp4File, hintTrackId); return false; } u_int32_t samplesPerPacket = 0; if (interleave) { u_int32_t maxSampleSize = MP4GetTrackMaxSampleSize(mp4File, mediaTrackId); // compute how many maximum size samples would fit in a packet samplesPerPacket = (maxPayloadSize - 2) / (maxSampleSize + 2); // can't interleave if this number is 0 or 1 if (samplesPerPacket < 2) { interleave = false; } } bool rc; if (interleave) { u_int32_t samplesPerGroup = maxLatency / sampleDuration; u_int32_t stride; stride = samplesPerGroup / samplesPerPacket; if (OneByteHeader && stride > 3) stride = 3; if (!OneByteHeader && stride > 7) stride = 7; #if 0 printf("max latency %llu sampleDuration %llu spg %u spp %u strid %u\n", maxLatency, sampleDuration, samplesPerGroup, samplesPerPacket, stride); #endif rc = MP4AV_AudioInterleaveHinter( mp4File, mediaTrackId, hintTrackId, sampleDuration, stride, // stride samplesPerPacket, // bundle maxPayloadSize, MP4AV_RfcIsmaConcatenator); } else { rc = MP4AV_AudioConsecutiveHinter( mp4File, mediaTrackId, hintTrackId, sampleDuration, 2, // perPacketHeaderSize 2, // perSampleHeaderSize maxLatency / sampleDuration, // maxSamplesPerPacket maxPayloadSize, MP4GetSampleSize, MP4AV_RfcIsmaConcatenator, MP4AV_RfcIsmaFragmenter); } if (!rc) { MP4DeleteTrack(mp4File, hintTrackId); return false; } return true; }
extern "C" MP4TrackId MP4AV_Rfc3016_HintTrackCreate (MP4FileHandle mp4File, MP4TrackId mediaTrackId) { MP4TrackId hintTrackId = MP4AddHintTrack(mp4File, mediaTrackId); if (hintTrackId == MP4_INVALID_TRACK_ID) { return MP4_INVALID_TRACK_ID; } u_int8_t payloadNumber = MP4_SET_DYNAMIC_PAYLOAD; if (MP4SetHintTrackRtpPayload(mp4File, hintTrackId, "MP4V-ES", &payloadNumber, 0) == false) { MP4DeleteTrack(mp4File, hintTrackId); return MP4_INVALID_TRACK_ID; } /* get the mpeg4 video configuration */ u_int8_t* pConfig; u_int32_t configSize; u_int8_t systemsProfileLevel = 0xFE; if (MP4GetTrackESConfiguration(mp4File, mediaTrackId, &pConfig, &configSize) == false) { MP4DeleteTrack(mp4File, hintTrackId); return MP4_INVALID_TRACK_ID; } if (pConfig) { // attempt to get a valid profile-level static u_int8_t voshStartCode[4] = { 0x00, 0x00, 0x01, MP4AV_MPEG4_VOSH_START }; if (configSize >= 5 && !memcmp(pConfig, voshStartCode, 4)) { systemsProfileLevel = pConfig[4]; } if (systemsProfileLevel == 0xFE) { u_int8_t iodProfileLevel = MP4GetVideoProfileLevel(mp4File); if (iodProfileLevel > 0 && iodProfileLevel < 0xFE) { systemsProfileLevel = iodProfileLevel; } else { systemsProfileLevel = 1; } } /* convert it into ASCII form */ char* sConfig = MP4BinaryToBase16(pConfig, configSize); free(pConfig); if (sConfig == NULL) { MP4DeleteTrack(mp4File, hintTrackId); return MP4_INVALID_TRACK_ID; } /* create the appropriate SDP attribute */ char* sdpBuf = (char*)malloc(strlen(sConfig) + 128); if (sdpBuf == NULL) { free(sConfig); MP4DeleteTrack(mp4File, hintTrackId); return MP4_INVALID_TRACK_ID; } snprintf(sdpBuf, strlen(sConfig) + 128, "a=fmtp:%u profile-level-id=%u; config=%s;\015\012", payloadNumber, systemsProfileLevel, sConfig); free(sConfig); /* add this to the track's sdp */ if (MP4AppendHintTrackSdp(mp4File, hintTrackId, sdpBuf) == false) { MP4DeleteTrack(mp4File, hintTrackId); hintTrackId = MP4_INVALID_TRACK_ID; } free(sdpBuf); } return hintTrackId; }
extern "C" bool MP4AV_Rfc3016LatmHinter (MP4FileHandle mp4File, MP4TrackId mediaTrackId, u_int16_t maxPayloadSize) { u_int32_t numSamples = MP4GetTrackNumberOfSamples(mp4File, mediaTrackId); u_int32_t maxSampleSize = MP4GetTrackMaxSampleSize(mp4File, mediaTrackId); MP4Duration sampleDuration = MP4AV_GetAudioSampleDuration(mp4File, mediaTrackId); if (sampleDuration == MP4_INVALID_DURATION) { return false; } if (numSamples == 0 || maxSampleSize == 0) { return false; } /* get the mpeg4 video configuration */ u_int8_t* pAudioSpecificConfig; u_int32_t AudioSpecificConfigSize; if (MP4GetTrackESConfiguration(mp4File, mediaTrackId, &pAudioSpecificConfig, &AudioSpecificConfigSize) == false) return false; if (pAudioSpecificConfig == NULL || AudioSpecificConfigSize == 0) return false; uint8_t channels = MP4AV_AacConfigGetChannels(pAudioSpecificConfig); uint32_t freq = MP4AV_AacConfigGetSamplingRate(pAudioSpecificConfig); uint8_t type = MP4AV_AacConfigGetAudioObjectType(pAudioSpecificConfig); uint8_t *pConfig; uint32_t configSize; MP4AV_LatmGetConfiguration(&pConfig, &configSize, pAudioSpecificConfig, AudioSpecificConfigSize); free(pAudioSpecificConfig); if (pConfig == NULL || configSize == 0) { CHECK_AND_FREE(pConfig); return false; } MP4TrackId hintTrackId = MP4AddHintTrack(mp4File, mediaTrackId); if (hintTrackId == MP4_INVALID_TRACK_ID) { free(pConfig); return false; } u_int8_t payloadNumber = MP4_SET_DYNAMIC_PAYLOAD; char buffer[10]; if (channels != 1) { snprintf(buffer, sizeof(buffer), "%u", channels); } /* convert it into ASCII form */ char* sConfig = MP4BinaryToBase16(pConfig, configSize); free(pConfig); if (sConfig == NULL || MP4SetHintTrackRtpPayload(mp4File, hintTrackId, "MP4A-LATM", &payloadNumber, 0, channels != 1 ? buffer : NULL) == false) { MP4DeleteTrack(mp4File, hintTrackId); return false; } uint32_t profile_level; // from gpac code switch (type) { case 2: if (channels <= 2) profile_level = freq <= 24000 ? 0x28 : 0x29; else profile_level = freq <= 48000 ? 0x2a : 0x2b; break; case 5: if (channels <= 2) profile_level = freq < 24000 ? 0x2c : 0x2d; else profile_level = freq <= 48000 ? 0x2e : 0x2f; break; default: if (channels <= 2) profile_level = freq < 24000 ? 0x0e : 0x0f; else profile_level = 0x10; break; } /* create the appropriate SDP attribute */ char* sdpBuf = (char*)malloc(strlen(sConfig) + 128); if (sdpBuf == NULL) { free(sConfig); MP4DeleteTrack(mp4File, hintTrackId); return false; } snprintf(sdpBuf, strlen(sConfig) + 128, "a=fmtp:%u profile-level-id=%u; cpresent=0; config=%s;\015\012", payloadNumber, profile_level, sConfig); /* add this to the track's sdp */ bool val = MP4AppendHintTrackSdp(mp4File, hintTrackId, sdpBuf); free(sConfig); free(sdpBuf); if (val == false) { MP4DeleteTrack(mp4File, hintTrackId); return false; } for (MP4SampleId sampleId = 1; sampleId <= numSamples; sampleId++) { uint8_t buffer[32]; uint32_t offset = 0; uint32_t sampleSize = MP4GetSampleSize(mp4File, mediaTrackId, sampleId); uint32_t size_left = sampleSize; while (size_left > 0) { if (size_left > 0xff) { size_left -= 0xff; buffer[offset] = 0xff; } else { buffer[offset] = size_left; size_left = 0; } offset++; } if (MP4AddRtpHint(mp4File, hintTrackId) == false || MP4AddRtpPacket(mp4File, hintTrackId, true) == false || MP4AddRtpImmediateData(mp4File, hintTrackId, buffer, offset) == false || MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, 0, sampleSize) == false || MP4WriteRtpHint(mp4File, hintTrackId, sampleDuration) == false) { MP4DeleteTrack(mp4File, hintTrackId); return false; } } return true; }