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); }
extern "C" bool MP4AV_Rfc3016_HintAddSample ( MP4FileHandle mp4File, MP4TrackId hintTrackId, MP4SampleId sampleId, uint8_t *pSampleBuffer, uint32_t sampleSize, MP4Duration duration, MP4Duration renderingOffset, bool isSyncSample, uint16_t maxPayloadSize) { bool isBFrame = (MP4AV_Mpeg4GetVopType(pSampleBuffer, sampleSize) == VOP_TYPE_B); if (MP4AddRtpVideoHint(mp4File, hintTrackId, isBFrame, renderingOffset) == false) return false; if (sampleId == 1) { if (MP4AddRtpESConfigurationPacket(mp4File, hintTrackId) == false) return false; } u_int32_t offset = 0; u_int32_t remaining = sampleSize; // TBD should scan for resync markers (if enabled in ES config) // and packetize on those boundaries while (remaining) { bool isLastPacket = false; u_int32_t length; if (remaining <= maxPayloadSize) { length = remaining; isLastPacket = true; } else { length = maxPayloadSize; } if (MP4AddRtpPacket(mp4File, hintTrackId, isLastPacket) == false || MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, length) == false) return false; offset += length; remaining -= length; } return MP4WriteRtpHint(mp4File, hintTrackId, duration, isSyncSample); }
//#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 }
extern "C" bool MP4AV_Rfc2429Hinter (MP4FileHandle file, MP4TrackId mediaTrackId, uint16_t maxPayloadSize) { uint32_t numSamples, maxSampleSize; MP4TrackId hid; MP4Duration duration; numSamples = MP4GetTrackNumberOfSamples(file, mediaTrackId); if (numSamples == 0) { return false; } maxSampleSize = MP4GetTrackMaxSampleSize(file, mediaTrackId); u_int8_t* pSampleBuffer = (u_int8_t*)malloc(maxSampleSize); if (pSampleBuffer == NULL) { return false; } hid = MP4AddHintTrack(file, mediaTrackId); if (hid == MP4_INVALID_TRACK_ID) { return false; } uint8_t payloadNumber = MP4_SET_DYNAMIC_PAYLOAD; MP4SetHintTrackRtpPayload(file, hid, "H263-2000", &payloadNumber, 0, NULL, true, false); // strictly speaking, this is not required for H.263 - it's a quicktime // thing. u_int16_t videoWidth = MP4GetTrackVideoWidth(file, mediaTrackId); u_int16_t videoHeight = MP4GetTrackVideoHeight(file, mediaTrackId); char sdpString[80]; sprintf(sdpString, "a=cliprect:0,0,%d,%d\015\012", videoHeight, videoWidth); MP4AppendHintTrackSdp(file, hid, sdpString); for (uint32_t sid = 1; sid <= numSamples; sid++) { duration = MP4GetSampleDuration(file, mediaTrackId, sid); MP4AddRtpVideoHint(file, hid, false, 0); u_int32_t sampleSize = maxSampleSize; MP4Timestamp startTime; MP4Duration duration; MP4Duration renderingOffset; bool isSyncSample; bool rc = MP4ReadSample(file, mediaTrackId, sid, &pSampleBuffer, &sampleSize, &startTime, &duration, &renderingOffset, &isSyncSample); if (!rc) { MP4DeleteTrack(file, hid); free(pSampleBuffer); return false; } // need to skip the first 2 bytes of the packet - it is the //start code uint16_t payload_head = htons(0x400); uint32_t offset = sizeof(payload_head); uint32_t remaining = sampleSize - sizeof(payload_head); while (remaining) { bool last_pak = false; uint32_t len; if (remaining + 2 <= maxPayloadSize) { len = remaining; last_pak = true; } else { len = maxPayloadSize - 2; } MP4AddRtpPacket(file, hid, last_pak); MP4AddRtpImmediateData(file, hid, (u_int8_t*)&payload_head, sizeof(payload_head)); payload_head = 0; MP4AddRtpSampleData(file, hid, sid, offset, len); offset += len; remaining -= len; } MP4WriteRtpHint(file, hid, duration, true); } free(pSampleBuffer); return true; }
extern "C" void MP4AV_AVSM_HintAddSample (MP4FileHandle mp4File, MP4TrackId hintTrackId, MP4SampleId sampleId, uint8_t *pSampleBuffer, uint32_t sampleSize, //sampleSize:整个sample多少个byte uint32_t sizeLength, MP4Duration duration, MP4Duration renderingOffset, bool isSyncSample, uint16_t maxPayloadSize) { uint8_t nal_type = avsm_get_sample_nal_type(pSampleBuffer, sampleSize, sizeLength); bool pic_is_idr = false; if (nal_type == AVSM_NAL_TYPE_IDR_PIC_HEADER) { pic_is_idr=true; } // for now, we don't know if we can drop frames, so don't indiate // that any are "b" frames bool isBFrame = false; uint32_t nal_size; //nal_size:一个nalu多少个byte,不包括前面打包的4byte,包括nalu头 uint32_t offset = 0; uint32_t remaining = sampleSize; /*#ifdef DEBUG_H264_HINT printf("hint for sample %d %u\n", sampleId, remaining); #endif*/ MP4AddRtpVideoHint(mp4File, hintTrackId, isBFrame, renderingOffset); if (sampleSize - sizeLength < maxPayloadSize) { //sample小于MTU sizelength相当于是头信息不包括在sample的净荷内 uint32_t first_nal = avsm_get_nal_size(pSampleBuffer, sizeLength); if (first_nal + sizeLength == sampleSize) { //一个sample只有一个nalu,且小于MTU时的情况 // we have a single nal, less than the maxPayloadSize, //只要打一个rtp包就可以了 // so, we have Single Nal unit mode MP4AddRtpPacket(mp4File, hintTrackId, true); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, sizeLength, sampleSize - sizeLength); MP4WriteRtpHint(mp4File, hintTrackId, duration, pic_is_idr);//nal_type改变规则 return; } } // TBD should scan for resync markers (if enabled in ES config) // and packetize on those boundaries while (remaining) { //sample数据大于MTU时,只要这个sample还有数据就执行********sample层**循环******************* nal_size = avsm_get_nal_size(pSampleBuffer + offset, sizeLength); //sizelength=4 //******* skip the sizeLength /*#ifdef DEBUG_H264_HINT//不执行 printf("offset %u nal size %u remain %u\n", offset, nal_size, remaining); #endif*/ offset += sizeLength; remaining -= sizeLength; if (nal_size > maxPayloadSize) { //如果nalu_size大于最大允许值MTU 需要分割 FU #ifdef DEBUG_H264_HINT //FU头8bit含义见说明 printf("fragmentation units\n"); #endif uint8_t head = pSampleBuffer[offset];//************************************************************************ offset++; nal_size--; remaining--; uint8_t fu_header[2]; // uint8_t *final_fu_header; fu_header[0] = (head&0xe0)|0x1c; //FU指示 fu_header[1] = head; //FU头 fu_header[1] |= 0x80; fu_header[1] &= 0x9f; //100+type5bit while (nal_size > 0) { //******************只要nalu还有值************************nalu层**循环******************* uint32_t write_size; if (nal_size + 2 <= maxPayloadSize) {//如果nalu净荷加nalu头和FU头小于等于MTU则结束分割单元 fu_header[1] |= 0x40;//分割单元结束 write_size = nal_size; } else { write_size = maxPayloadSize - 2;//write_size就是这个分割单元包括的payload=MTU-2 } #ifdef DEBUG_H264_HINT printf("frag off %u write %u left in nal %u remain %u\n", offset, write_size, nal_size, remaining); #endif remaining -= write_size;//remaining为分完一个MTU后这个sample剩余的数据 MP4AddRtpPacket(mp4File, hintTrackId, remaining == 0); MP4AddRtpImmediateData(mp4File, hintTrackId, fu_header, 2); fu_header[1] &= 0x7f;//表示结束第一个分割单元 MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, write_size); offset += write_size; nal_size -= write_size;//nal_size为这个nalu分割后剩余的数据,如果还有数据就在此循环,组成新的分割单元 } } //************FU****end******** else { //如果nalu_size 小于 最大允许值MTU,则要考虑是否打复合包stap // we have a smaller than MTU nal. check the next sample //这句话什么意思,为什么是next sample,应该是nalu吧 // see if the next one fits; uint32_t next_size_offset; bool have_stap = false; next_size_offset = offset + nal_size; if (next_size_offset < sampleSize) {//if (next_size_offset < remaining) {//是否有下一个nalu?有,则执行下面 remaining改成samplesize // we have a remaining NAL uint32_t next_nal_size = avsm_get_nal_size(pSampleBuffer + next_size_offset, sizeLength); if (next_nal_size + nal_size + 4 + 1 <= maxPayloadSize) { //判断下一个两个nalu大小加起来是否小于MTU have_stap = true; //小于MTU的话,我们就用STAP复合包 } //4 两个NALU尺寸 1 一个STAP-A净载头 (NALU尺寸为1byte) } if (have_stap == false) { //如果两个nalu大于MTU则不用STAP,直接把这个nalu打成一个rtp包,下一个再重新判断 MP4AddRtpPacket(mp4File, hintTrackId, next_size_offset >= remaining); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; } else { //****************如果两个NALU小于MTU则使用STAP-A复合包实现****** uint32_t bytes_in_stap = 1 + 2 + nal_size; //1byte STAP-A净载头;2byte是STAP的nalu尺寸:表明随后nalu(包括nalu头)大小 uint8_t max_nri = pSampleBuffer[offset] & 0x60; //0 11 00000 //**重要问题:一个sample包含几个nalu** while (next_size_offset <= sampleSize && bytes_in_stap <= maxPayloadSize)//while (next_size_offset < sampleSize && bytes_in_stap <= maxPayloadSize) //remaining改成samplesize//while (next_size_offset < remaining && bytes_in_stap < maxPayloadSize) { uint8_t nri; //在一个stap内比较出nalu最大的NRI优先级,在净载头中NRI单元就用最大优先级 nri = pSampleBuffer[next_size_offset + sizeLength] & 0x60; //0 11 00000 if (nri > max_nri) max_nri = nri; uint32_t next_nal_size = avsm_get_nal_size(pSampleBuffer + next_size_offset, sizeLength); bytes_in_stap += 2 + next_nal_size; next_size_offset += sizeLength + next_nal_size; } //下面验证是否是最后一个nalu bool last; if (next_size_offset > sampleSize)// && bytes_in_stap <= maxPayloadSizey)//if (next_size_offset <= remaining && bytes_in_stap <= maxPayloadSize) // stap is last frame //已经到了这个sample最后一个nalu,M标志位置1,表示sample结束 { last = true; } else last = false; MP4AddRtpPacket(mp4File, hintTrackId, last);//last=true时z说明这个stap是这个sample最后一个stap,把M标志位置1 uint8_t data[3]; data[0] = max_nri | 24; //stap-A 净载头 data[1] = nal_size >> 8; data[2] = nal_size & 0xff; //NALU尺寸2 byte MP4AddRtpImmediateData(mp4File, hintTrackId, data, 3); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; bytes_in_stap = 1 + 2 + nal_size; //1byte STAP-A净载头;2byte是STAP的nalu尺寸:表明随后nalu(包括nalu头)大小 nal_size = avsm_get_nal_size(pSampleBuffer + offset, sizeLength); while (bytes_in_stap + nal_size + 2 <= maxPayloadSize &&remaining) { //***当两个NALU以后还小于MTU则下面的nalu再打入stap**循环* offset += sizeLength; remaining -= sizeLength; data[0] = nal_size >> 8; data[1] = nal_size & 0xff; MP4AddRtpImmediateData(mp4File, hintTrackId, data, 2); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; bytes_in_stap += nal_size + 2; //再次将nalu打入stap时就不用再加1byte净载头了,只需要加2byte nalu尺寸 if (remaining) { nal_size = avsm_get_nal_size(pSampleBuffer + offset, sizeLength); } } // end while stap } // end have stap } // end check size } MP4WriteRtpHint(mp4File, hintTrackId, duration, //整个sample读完后写??! pic_is_idr); }
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" void MP4AV_H264_HintAddSample (MP4FileHandle mp4File, MP4TrackId hintTrackId, MP4SampleId sampleId, uint8_t *pSampleBuffer, uint32_t sampleSize, uint32_t sizeLength, MP4Duration duration, MP4Duration renderingOffset, bool isSyncSample, uint16_t maxPayloadSize) { uint8_t nal_type = h264_get_sample_nal_type(pSampleBuffer, sampleSize, sizeLength); // for now, we don't know if we can drop frames, so don't indiate // that any are "b" frames bool isBFrame = false; uint32_t nal_size; uint32_t offset = 0; uint32_t remaining = sampleSize; #ifdef DEBUG_H264_HINT printf("hint for sample %d %u\n", sampleId, remaining); #endif MP4AddRtpVideoHint(mp4File, hintTrackId, isBFrame, renderingOffset); if (sampleSize - sizeLength < maxPayloadSize) { uint32_t first_nal = h264_get_nal_size(pSampleBuffer, sizeLength); if (first_nal + sizeLength == sampleSize) { // we have a single nal, less than the maxPayloadSize, // so, we have Single Nal unit mode MP4AddRtpPacket(mp4File, hintTrackId, true); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, sizeLength, sampleSize - sizeLength); MP4WriteRtpHint(mp4File, hintTrackId, duration, nal_type == H264_NAL_TYPE_IDR_SLICE); return; } } // TBD should scan for resync markers (if enabled in ES config) // and packetize on those boundaries while (remaining) { nal_size = h264_get_nal_size(pSampleBuffer + offset, sizeLength); // skip the sizeLength #ifdef DEBUG_H264_HINT printf("offset %u nal size %u remain %u\n", offset, nal_size, remaining); #endif offset += sizeLength; remaining -= sizeLength; if (nal_size > maxPayloadSize) { /* * We have fragmentation of a NAL here */ #ifdef DEBUG_H264_HINT printf("fragmentation units\n"); #endif uint8_t head = pSampleBuffer[offset]; offset++; nal_size--; remaining--; uint8_t fu_header[2]; fu_header[0] = (head & 0xe0) | 28; fu_header[1] = 0x80; head &= 0x1f; while (nal_size > 0) { fu_header[1] |= head; uint32_t write_size; if (nal_size + 2 <= maxPayloadSize) { fu_header[1] |= 0x40; write_size = nal_size; } else { write_size = maxPayloadSize - 2; } #ifdef DEBUG_H264_HINT printf("frag off %u write %u left in nal %u remain %u\n", offset, write_size, nal_size, remaining); #endif remaining -= write_size; MP4AddRtpPacket(mp4File, hintTrackId, remaining == 0); MP4AddRtpImmediateData(mp4File, hintTrackId, fu_header, 2); fu_header[1] = 0; MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, write_size); offset += write_size; nal_size -= write_size; } } else { // we have a smaller than MTU nal. check the next sample // see if the next one fits; uint32_t next_size_offset; bool have_stap = false; next_size_offset = offset + nal_size; if (next_size_offset < remaining) { // we have a remaining NAL uint32_t next_nal_size = h264_get_nal_size(pSampleBuffer + next_size_offset, sizeLength); #ifdef DEBUG_H264_HINT printf("next nal size %u\n", next_nal_size); #endif if (next_nal_size + nal_size + 4 + 1 <= maxPayloadSize) { have_stap = true; } } if (have_stap == false) { // we have to fit this nal into a packet - the next one is too big #ifdef DEBUG_H264_HINT printf("have single NAL packet \n"); #endif MP4AddRtpPacket(mp4File, hintTrackId, next_size_offset >= remaining); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; } else { // we can fit multiple NALs into this packet uint32_t bytes_in_stap = 1 + 2 + nal_size; uint8_t max_nri = pSampleBuffer[offset] & 0x70; #ifdef DEBUG_H264_HINT printf("Start processing stap\n"); #endif while (next_size_offset < remaining && bytes_in_stap < maxPayloadSize) { uint8_t nri; nri = pSampleBuffer[next_size_offset + sizeLength] & 0x70; if (nri > max_nri) max_nri = nri; uint32_t next_nal_size = h264_get_nal_size(pSampleBuffer + next_size_offset, sizeLength); bytes_in_stap += 2 + next_nal_size; next_size_offset += sizeLength + next_nal_size; #ifdef DEBUG_H264_HINT printf("next nal %u bytes in stap %u next offset %u\n", next_nal_size, bytes_in_stap, next_size_offset); #endif } bool last; #ifdef DEBUG_H264_HINT printf("done - next_size offset %u remain %u bytes %u\n", next_size_offset, remaining, bytes_in_stap); #endif if (next_size_offset >= (offset + remaining) && bytes_in_stap <= maxPayloadSize) { // stap is last frame last = true; } else last = false; MP4AddRtpPacket(mp4File, hintTrackId, last); uint8_t data[3]; data[0] = max_nri | 24; data[1] = nal_size >> 8; data[2] = nal_size & 0xff; MP4AddRtpImmediateData(mp4File, hintTrackId, data, 3); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; bytes_in_stap = 1 + 2 + nal_size; nal_size = h264_get_nal_size(pSampleBuffer + offset, sizeLength); while (bytes_in_stap + nal_size <= maxPayloadSize && remaining) { offset += sizeLength; remaining -= sizeLength; data[0] = nal_size >> 8; data[1] = nal_size & 0xff; MP4AddRtpImmediateData(mp4File, hintTrackId, data, 2); MP4AddRtpSampleData(mp4File, hintTrackId, sampleId, offset, nal_size); offset += nal_size; remaining -= nal_size; bytes_in_stap += nal_size + 2; if (remaining) { nal_size = h264_get_nal_size(pSampleBuffer + offset, sizeLength); } } // end while stap } // end have stap } // end check size } MP4WriteRtpHint(mp4File, hintTrackId, duration, nal_type == H264_NAL_TYPE_IDR_SLICE); }
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; }