bool MP4AV_RfcIsmaFragmenter( MP4FileHandle mp4File, MP4TrackId mediaTrackId, MP4TrackId hintTrackId, MP4SampleId sampleId, u_int32_t sampleSize, MP4Duration sampleDuration, u_int16_t maxPayloadSize) { if (MP4AddRtpHint(mp4File, hintTrackId) == false || MP4AddRtpPacket(mp4File, hintTrackId, false) == false) return false; // Note: CELP is never fragmented // so we assume the two byte AAC-hbr payload header u_int8_t payloadHeader[4]; payloadHeader[0] = 0; payloadHeader[1] = 16; payloadHeader[2] = sampleSize >> 5; payloadHeader[3] = (sampleSize & 0x1F) << 3; if (MP4AddRtpImmediateData(mp4File, hintTrackId, (u_int8_t*)&payloadHeader, sizeof(payloadHeader)) == false) return false; u_int16_t sampleOffset = 0; u_int16_t fragLength = maxPayloadSize - 4; do { if (MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, sampleOffset, fragLength) == false) return false; sampleOffset += fragLength; if (sampleSize - sampleOffset > maxPayloadSize) { fragLength = maxPayloadSize; if (MP4AddRtpPacket(mp4File, hintTrackId, false) == false) return false; } else { fragLength = sampleSize - sampleOffset; if (fragLength) { if (MP4AddRtpPacket(mp4File, hintTrackId, true) == false) return false; } } } while (sampleOffset < sampleSize); return MP4WriteRtpHint(mp4File, hintTrackId, sampleDuration); }
//#define DEBUG_G711 1 extern "C" bool G711Hinter (MP4FileHandle mp4file, MP4TrackId trackid, uint16_t maxPayloadSize) { uint32_t numSamples; uint8_t audioType; MP4SampleId sampleId; uint32_t sampleSize; MP4TrackId hintTrackId; uint8_t payload; uint32_t bytes_this_hint; uint32_t sampleOffset; numSamples = MP4GetTrackNumberOfSamples(mp4file, trackid); if (numSamples == 0) return false; audioType = MP4GetTrackEsdsObjectTypeId(mp4file, trackid); if (audioType != MP4_ALAW_AUDIO_TYPE && audioType != MP4_ULAW_AUDIO_TYPE) return false; hintTrackId = MP4AddHintTrack(mp4file, trackid); if (hintTrackId == MP4_INVALID_TRACK_ID) { return false; } const char *type; if (audioType == MP4_ALAW_AUDIO_TYPE) { payload = 8; type = "PCMA"; } else { payload = 0; type = "PCMU"; } MP4SetHintTrackRtpPayload(mp4file, hintTrackId, type, &payload, 0,NULL, false); MP4Duration sampleDuration; bool have_skip; sampleId = 1; sampleSize = MP4GetSampleSize(mp4file, trackid, sampleId); sampleDuration = MP4GetSampleDuration(mp4file, trackid, sampleId); have_skip = sampleDuration != sampleSize; sampleOffset = 0; bytes_this_hint = 0; if (maxPayloadSize > 160) maxPayloadSize = 160; while (1) { if (bytes_this_hint == 0) { #ifdef DEBUG_G711 printf("Adding hint/packet\n"); #endif MP4AddRtpHint(mp4file, hintTrackId); MP4AddRtpPacket(mp4file, hintTrackId, false); // marker bit 0 } uint16_t bytes_left_this_packet; bytes_left_this_packet = maxPayloadSize - bytes_this_hint; if (sampleSize >= bytes_left_this_packet) { MP4AddRtpSampleData(mp4file, hintTrackId, sampleId, sampleOffset, bytes_left_this_packet); bytes_this_hint += bytes_left_this_packet; sampleSize -= bytes_left_this_packet; sampleOffset += bytes_left_this_packet; #ifdef DEBUG_G711 printf("Added sample with %u bytes\n", bytes_left_this_packet); #endif } else { MP4AddRtpSampleData(mp4file, hintTrackId, sampleId, sampleOffset, sampleSize); bytes_this_hint += sampleSize; #ifdef DEBUG_G711 printf("Added sample with %u bytes\n", sampleSize); #endif sampleSize = 0; } if (bytes_this_hint >= maxPayloadSize) { // Write the hint // duration is bytes written MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); #ifdef DEBUG_G711 printf("Finished packet - bytes %u\n", bytes_this_hint); #endif bytes_this_hint = 0; } if (sampleSize == 0) { // next sample if (have_skip && bytes_this_hint != 0) { #ifdef DEBUG_G711 printf("duration - ending packet - bytes %u\n", bytes_this_hint); #endif MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); bytes_this_hint = 0; } sampleId++; if (sampleId > numSamples) { // finish it and exit if (bytes_this_hint != 0) { MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); } return true; } sampleSize = MP4GetSampleSize(mp4file, trackid, sampleId); sampleDuration = MP4GetSampleDuration(mp4file, trackid, sampleId); have_skip = sampleDuration != sampleSize; #ifdef DEBUG_G711 printf("Next sample %u - size %u %u\n", sampleId, sampleSize, have_skip); #endif sampleOffset = 0; } } return true; // will never reach here }
bool MP4AV_RfcIsmaConcatenator( MP4FileHandle mp4File, MP4TrackId mediaTrackId, MP4TrackId hintTrackId, u_int8_t samplesThisHint, MP4SampleId* pSampleIds, MP4Duration hintDuration, u_int16_t maxPayloadSize) { // handle degenerate case if (samplesThisHint == 0) { return true; } u_int8_t auPayloadHdrSize; // LATER would be more efficient if this were a parameter u_int8_t mpeg4AudioType = MP4GetTrackAudioMpeg4Type(mp4File, mediaTrackId); if (mpeg4AudioType == MP4_MPEG4_CELP_AUDIO_TYPE) { auPayloadHdrSize = 1; } else { auPayloadHdrSize = 2; } // construct the new hint if (MP4AddRtpHint(mp4File, hintTrackId) == false || MP4AddRtpPacket(mp4File, hintTrackId, true) == false) return false; u_int8_t payloadHeader[2]; u_int16_t numHdrBits = samplesThisHint * auPayloadHdrSize * 8; payloadHeader[0] = numHdrBits >> 8; payloadHeader[1] = numHdrBits & 0xFF; if (MP4AddRtpImmediateData(mp4File, hintTrackId, (u_int8_t*)&payloadHeader, sizeof(payloadHeader)) == false) return false; u_int8_t i; // first the headers for (i = 0; i < samplesThisHint; i++) { MP4SampleId sampleId = pSampleIds[i]; u_int32_t sampleSize = MP4GetSampleSize(mp4File, mediaTrackId, sampleId); if (auPayloadHdrSize == 1) { // AU payload header is 6 bits of size // follow by 2 bits of the difference between sampleId's - 1 payloadHeader[0] = sampleSize << 2; } else { // auPayloadHdrSize == 2 // AU payload header is 13 bits of size // follow by 3 bits of the difference between sampleId's - 1 payloadHeader[0] = sampleSize >> 5; payloadHeader[1] = (sampleSize & 0x1F) << 3; } if (i > 0) { payloadHeader[auPayloadHdrSize - 1] |= ((sampleId - pSampleIds[i-1]) - 1); } #if 0 printf("sample %u size %u %02x %02x prev sample %d\n", sampleId, sampleSize, payloadHeader[0], payloadHeader[1], pSampleIds[i-1]); #endif if (MP4AddRtpImmediateData(mp4File, hintTrackId, (u_int8_t*)&payloadHeader, auPayloadHdrSize) == false) return false; } // then the samples for (i = 0; i < samplesThisHint; i++) { MP4SampleId sampleId = pSampleIds[i]; u_int32_t sampleSize = MP4GetSampleSize(mp4File, mediaTrackId, sampleId); if (MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, 0, sampleSize) == false) return false; } // write the hint return MP4WriteRtpHint(mp4File, hintTrackId, hintDuration); }
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; }