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); }
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); }
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); }