MP4Reader(const std::string &file_path) : time_scale(9 * MP4_MSECS_TIME_SCALE) , file_path(file_path) , handle(MP4_INVALID_FILE_HANDLE) , video_track_id(MP4_INVALID_TRACK_ID) , next_video_sample_idx(1) , video_sample(nullptr) , video_timescale(0) , video_sample_max_size(0) , video_sample_number(0) , video_duration(0) , pSeqHeaders(nullptr) , pSeqHeaderSize(nullptr) , pPictHeaders(nullptr) , pPictHeaderSize(nullptr) { handle = MP4Read(this->file_path.c_str()); video_track_id = MP4FindTrackId(handle, 0, MP4_VIDEO_TRACK_TYPE); if (video_track_id != MP4_INVALID_TRACK_ID) { video_timescale = MP4GetTrackTimeScale(handle, video_track_id); video_sample_max_size = MP4GetTrackMaxSampleSize(handle, video_track_id) * 2; video_duration = MP4GetTrackDuration(handle, video_track_id); video_sample = new unsigned char[video_sample_max_size]; video_sample_number = MP4GetTrackNumberOfSamples(handle, video_track_id); if (MP4GetTrackH264SeqPictHeaders(handle, video_track_id, &pSeqHeaders, &pSeqHeaderSize, &pPictHeaders, &pPictHeaderSize)) { printf("Get SPS(%d) and PPS(%d)\n", *pSeqHeaderSize, *pPictHeaderSize); for(int i = 0; (pSeqHeaders[i] && pSeqHeaderSize[i]); i++) { printf("SPS(%d): %02x %02x %02x %02x %02x\n", i, pSeqHeaders[i][0], pSeqHeaders[i][1], pSeqHeaders[i][2], pSeqHeaders[i][3], pSeqHeaders[i][4]); } for(int i = 0; (pPictHeaders[i] && pPictHeaderSize[i]); i++) { printf("PPS(%d): %02x %02x %02x %02x %02x\n", i, pPictHeaders[i][0], pPictHeaders[i][1], pPictHeaders[i][2], pPictHeaders[i][3], pPictHeaders[i][4]); } } } }
int CMp4File::create_media (CPlayerSession *psptr, int have_audio_driver, control_callback_vft_t *cc_vft) { uint video_count, video_offset; uint text_count, text_offset; uint audio_count, audio_offset; MP4TrackId trackId; video_query_t *vq; audio_query_t *aq; text_query_t *tq; uint ix; codec_plugin_t *plugin; int ret_value = 0; uint8_t *foo; u_int32_t bufsize; uint32_t verb = MP4GetVerbosity(m_mp4file); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); video_count = MP4GetNumberOfTracks(m_mp4file, MP4_VIDEO_TRACK_TYPE); audio_count = MP4GetNumberOfTracks(m_mp4file, MP4_AUDIO_TRACK_TYPE); text_count = MP4GetNumberOfTracks(m_mp4file, MP4_CNTL_TRACK_TYPE); mp4f_message(LOG_DEBUG, "cntl tracks %u", text_count); MP4SetVerbosity(m_mp4file, verb); if (video_count == 0 && audio_count == 0 && text_count == 0) { psptr->set_message("No audio, video or control tracks in file"); return -1; } if (video_count > 0) { vq = (video_query_t *)malloc(sizeof(video_query_t) * video_count); memset(vq, 0, sizeof(video_query_t) * video_count); } else { vq = NULL; } if (have_audio_driver && audio_count > 0) { aq = (audio_query_t *)malloc(sizeof(audio_query_t) * audio_count); memset(aq, 0, sizeof(audio_query_t) * audio_count); } else { aq = NULL; } if (text_count > 0) { tq = (text_query_t *)malloc(sizeof(text_query_t) * text_count); memset(tq, 0, sizeof(text_query_t) * text_count); } else { tq = NULL; } for (ix = 0, video_offset = 0; ix < video_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_VIDEO_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); // for now, treat mp4v and encv the same vq[video_offset].track_id = trackId; vq[video_offset].stream_type = STREAM_TYPE_MP4_FILE; vq[video_offset].compressor = media_data_name; if (strcasecmp(media_data_name, "mp4v") == 0 || strcasecmp(media_data_name, "encv") == 0) { uint8_t video_type = MP4GetTrackEsdsObjectTypeId(m_mp4file, trackId); uint8_t profileID = MP4GetVideoProfileLevel(m_mp4file, trackId); mp4f_message(LOG_DEBUG, "MP4 - got track %x profile ID %d", trackId, profileID); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); MP4GetTrackESConfiguration(m_mp4file, trackId, &foo, &bufsize); MP4SetVerbosity(m_mp4file, verb); vq[video_offset].type = video_type; vq[video_offset].profile = profileID; vq[video_offset].fptr = NULL; vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } else if (strcasecmp(media_data_name, "avc1") == 0) { uint8_t profile, level; uint8_t **seqheader, **pictheader; uint32_t *pictheadersize, *seqheadersize; uint32_t ix; MP4GetTrackH264ProfileLevel(m_mp4file, trackId, &profile, &level); MP4GetTrackH264SeqPictHeaders(m_mp4file, trackId, &seqheader, &seqheadersize, &pictheader, &pictheadersize); bufsize = 0; for (ix = 0; seqheadersize[ix] != 0; ix++) { bufsize += seqheadersize[ix] + 4; } for (ix = 0; pictheadersize[ix] != 0; ix++) { bufsize += pictheadersize[ix] + 4; } foo = (uint8_t *)malloc(bufsize + 4); memset(foo, 0, bufsize + 4); uint32_t copied = 0; // headers do not have the byte stream start code stored in the file for (ix = 0; seqheadersize[ix] != 0; ix++) { foo[copied] = 0; foo[copied + 1] = 0; foo[copied + 2] = 0; foo[copied + 3] = 1; copied += 4; // add header memcpy(foo + copied, seqheader[ix], seqheadersize[ix]); copied += seqheadersize[ix]; free(seqheader[ix]); } free(seqheader); free(seqheadersize); for (ix = 0; pictheadersize[ix] != 0; ix++) { foo[copied] = 0; foo[copied + 1] = 0; foo[copied + 2] = 0; foo[copied + 3] = 1; copied += 4; // add header memcpy(foo + copied, pictheader[ix], pictheadersize[ix]); copied += pictheadersize[ix]; free(pictheader[ix]); } free(pictheader); free(pictheadersize); vq[video_offset].type = level; vq[video_offset].profile = profile; vq[video_offset].fptr = NULL; vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } else { MP4GetTrackVideoMetadata(m_mp4file, trackId, &foo, &bufsize); vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } plugin = check_for_video_codec(vq[video_offset].stream_type, vq[video_offset].compressor, NULL, vq[video_offset].type, vq[video_offset].profile, vq[video_offset].config, vq[video_offset].config_len, &config); if (plugin == NULL) { psptr->set_message("Can't find plugin for video %s type %d, profile %d", vq[video_offset].compressor, vq[video_offset].type, vq[video_offset].profile); m_illegal_video_codec++; ret_value = 1; // possibly memleak for foo here } else { vq[video_offset].h = MP4GetTrackVideoHeight(m_mp4file, trackId); vq[video_offset].w = MP4GetTrackVideoWidth(m_mp4file, trackId); vq[video_offset].frame_rate = MP4GetTrackVideoFrameRate(m_mp4file, trackId); vq[video_offset].enabled = 0; vq[video_offset].reference = NULL; video_offset++; } } audio_offset = 0; if (have_audio_driver) { for (ix = 0; ix < audio_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_AUDIO_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); aq[audio_offset].track_id = trackId; aq[audio_offset].stream_type = STREAM_TYPE_MP4_FILE; aq[audio_offset].compressor = media_data_name; if (strcasecmp(media_data_name, "mp4a") == 0 || strcasecmp(media_data_name, "enca") == 0) { uint8_t *userdata = NULL; u_int32_t userdata_size; aq[audio_offset].type = MP4GetTrackEsdsObjectTypeId(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); aq[audio_offset].profile = MP4GetAudioProfileLevel(m_mp4file); MP4GetTrackESConfiguration(m_mp4file, trackId, &userdata, &userdata_size); MP4SetVerbosity(m_mp4file, verb); aq[audio_offset].config = userdata; aq[audio_offset].config_len = userdata_size; } plugin = check_for_audio_codec(aq[audio_offset].stream_type, aq[audio_offset].compressor, NULL, aq[audio_offset].type, aq[audio_offset].profile, aq[audio_offset].config, aq[audio_offset].config_len, &config); if (plugin != NULL) { aq[audio_offset].fptr = NULL; aq[audio_offset].sampling_freq = MP4GetTrackTimeScale(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); aq[audio_offset].chans = MP4GetTrackAudioChannels(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb); aq[audio_offset].enabled = 0; aq[audio_offset].reference = NULL; audio_offset++; m_have_audio = true; } else { m_illegal_audio_codec++; ret_value = 1; } } } else { if (audio_count) ret_value = 1; } text_offset = 0; for (ix = 0; ix < text_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_CNTL_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); tq[text_offset].track_id = trackId; tq[text_offset].stream_type = STREAM_TYPE_MP4_FILE; tq[text_offset].compressor = media_data_name; plugin = check_for_text_codec(tq[text_offset].stream_type, tq[text_offset].compressor, NULL, NULL, 0, &config); if (plugin != NULL) { tq[text_offset].fptr = NULL; tq[text_offset].enabled = 0; tq[text_offset].reference = NULL; text_offset++; } else { m_illegal_text_codec++; ret_value = 1; } } if (video_offset == 0 && audio_offset == 0 && text_offset == 0) { psptr->set_message("No playable codecs in mp4 file"); return -1; } if (cc_vft && cc_vft->media_list_query != NULL) { (cc_vft->media_list_query)(psptr, video_offset, vq, audio_offset, aq, text_offset, tq); } else { if (video_offset > 0) { vq[0].enabled = 1; } if (audio_offset > 0) { aq[0].enabled = 1; } if (text_offset > 0) { tq[0].enabled = 1; } } int vidret, audret, textret; uint start_desc = 1; vidret = create_video(psptr, vq, video_offset, start_desc); free(vq); if (vidret < 0) { free(aq); free(tq); return -1; } audret = create_audio(psptr, aq, audio_offset, start_desc); free(aq); textret = create_text(psptr, tq, text_offset, start_desc); free(tq); if (audret < 0 || textret < 0) ret_value = -1; char *name; verb = MP4GetVerbosity(m_mp4file); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); if (MP4GetMetadataName(m_mp4file, &name) && name != NULL) { psptr->set_session_desc(0, name); free(name); } MP4SetVerbosity(m_mp4file, verb); return (ret_value); }
AVCDescriptor* MP4Streamer::GetAVCDescriptor() { uint8_t **sequenceHeader; uint8_t **pictureHeader; uint32_t *pictureHeaderSize; uint32_t *sequenceHeaderSize; uint32_t i; uint8_t profile, level; uint32_t len; //Check video if (!video || video->codec!=VideoCodec::H264) //Nothing return NULL; //Create descriptor AVCDescriptor* desc = new AVCDescriptor(); //Set them desc->SetConfigurationVersion(0x01); desc->SetAVCProfileIndication(profile); desc->SetProfileCompatibility(0x00); desc->SetAVCLevelIndication(level); //Set nalu length MP4GetTrackH264LengthSize(video->mp4, video->track, &len); //Set it desc->SetNALUnitLength(len-1); // Get SEI informaMP4GetTrackH264SeqPictHeaderstion MP4GetTrackH264SeqPictHeaders(video->mp4, video->track, &sequenceHeader, &sequenceHeaderSize, &pictureHeader, &pictureHeaderSize); // Send sequence headers i=0; // Check we have sequence header if (sequenceHeader) { // Loop array while(sequenceHeader[i] && sequenceHeaderSize[i]) { //Append sequence desc->AddSequenceParameterSet(sequenceHeader[i],sequenceHeaderSize[i]); //Update values based on the ones in SQS desc->SetAVCProfileIndication(sequenceHeader[i][1]); desc->SetProfileCompatibility(sequenceHeader[i][2]); desc->SetAVCLevelIndication(sequenceHeader[i][3]); //inc i++; } } // Send picture headers i=0; // Check we have picture header if (pictureHeader) { // Loop array while(pictureHeader[i] && pictureHeaderSize[i]) { //Append sequence desc->AddPictureParameterSet(pictureHeader[i],pictureHeaderSize[i]); //inc i++; } } // Free data if (pictureHeader) free(pictureHeader); if (sequenceHeader) free(sequenceHeader); if (sequenceHeaderSize) free(sequenceHeaderSize); if (pictureHeaderSize) free(pictureHeaderSize); return desc; }
extern "C" MP4TrackId MP4AV_H264_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; // don't include mpeg4-esid MP4SetHintTrackRtpPayload(mp4File, hintTrackId, "H264", &payloadNumber, 0, NULL, true, false); /* get the mpeg4 video configuration */ u_int8_t **pSeq, **pPict ; u_int32_t *pSeqSize, *pPictSize; char *base64; uint32_t profile_level; char *sprop = NULL; uint32_t ix = 0; MP4GetTrackH264SeqPictHeaders(mp4File, mediaTrackId, &pSeq, &pSeqSize, &pPict, &pPictSize); if (pSeqSize && pSeqSize[0] != 0) { // we have valid sequence and picture headers uint8_t *p = pSeq[0]; if (*p == 0 && p[1] == 0 && (p[2] == 1 || (p[2] == 0 && p[3] == 0))) { if (p[2] == 0) p += 4; else p += 3; } profile_level = p[0] << 16 | p[1] << 8 | p[2]; while (pSeqSize[ix] != 0) { base64 = MP4BinaryToBase64(pSeq[ix], pSeqSize[ix]); if (sprop == NULL) { sprop = strdup(base64); } else { sprop = (char *)realloc(sprop, strlen(sprop) + strlen(base64) + 1 + 1); strcat(sprop, ","); strcat(sprop, base64); } free(base64); free(pSeq[ix]); ix++; } free(pSeq); free(pSeqSize); ix = 0; while (pPictSize[ix] != 0) { base64 = MP4BinaryToBase64(pPict[ix], pPictSize[ix]); sprop = (char *)realloc(sprop, strlen(sprop) + strlen(base64) + 1 + 1); strcat(sprop, ","); strcat(sprop, base64); free(base64); free(pPict[ix]); ix++; } free(pPict); free(pPictSize); /* create the appropriate SDP attribute */ char* sdpBuf = (char*)malloc(strlen(sprop) + 128); sprintf(sdpBuf, "a=fmtp:%u profile-level-id=%06x; sprop-parameter-sets=%s; packetization-mode=1\015\012", payloadNumber, profile_level, sprop); /* add this to the track's sdp */ MP4AppendHintTrackSdp(mp4File, hintTrackId, sdpBuf); free(sprop); free(sdpBuf); } return hintTrackId; }
int MP4RtpTrack::SendH263SEI(Listener *listener) { uint8_t **sequenceHeader; uint8_t **pictureHeader; uint32_t *pictureHeaderSize; uint32_t *sequenceHeaderSize; uint32_t i; uint8_t* data; uint32_t dataLen; //Not mark rtp.SetMark(false); // Get SEI information MP4GetTrackH264SeqPictHeaders(mp4, track, &sequenceHeader, &sequenceHeaderSize, &pictureHeader, &pictureHeaderSize); // Get data pointer data = rtp.GetMediaData(); // Reset length dataLen = 0; // Send sequence headers i=0; // Check we have sequence header if (sequenceHeader) // Loop array while(sequenceHeader[i] && sequenceHeaderSize[i]) { // Check if it can be handled in a single packeti if (sequenceHeaderSize[i]<1400) { // If there is not enought length if (dataLen+sequenceHeaderSize[i]>1400) { // Set data length rtp.SetMediaLength(dataLen); //Check listener if (listener) // Write frame listener->onRTPPacket(rtp); // Reset data dataLen = 0; } // Copy data memcpy(data+dataLen,sequenceHeader[i],sequenceHeaderSize[i]); // Increase pointer dataLen+=sequenceHeaderSize[i]; } // Free memory free(sequenceHeader[i]); // Next header i++; } // If there is still data if (dataLen>0) { // Set data length rtp.SetMediaLength(dataLen); //Check listener if (listener) // Write frame listener->onRTPPacket(rtp); // Reset data dataLen = 0; } // Send picture headers i=0; // Check we have picture header if (pictureHeader) // Loop array while(pictureHeader[i] && pictureHeaderSize[i]) { // Check if it can be handled in a single packeti if (pictureHeaderSize[i]<1400) { // If there is not enought length if (dataLen+pictureHeaderSize[i]>1400) { // Set data length rtp.SetMediaLength(dataLen); //Check listener if (listener) // Write frame listener->onRTPPacket(rtp); // Reset data dataLen = 0; } // Copy data memcpy(data+dataLen,pictureHeader[i],pictureHeaderSize[i]); // Increase pointer dataLen+=pictureHeaderSize[i]; } // Free memory free(pictureHeader[i]); // Next header i++; } // If there is still data if (dataLen>0) { // Set data length rtp.SetMediaLength(dataLen); //Check listener if (listener) // Write frame listener->onRTPPacket(rtp); // Reset data dataLen = 0; } // Free data if (pictureHeader) free(pictureHeader); if (sequenceHeader) free(sequenceHeader); if (sequenceHeaderSize) free(sequenceHeaderSize); if (pictureHeaderSize) free(pictureHeaderSize); }