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); }
static char* PrintVideoInfo( MP4FileHandle mp4File, MP4TrackId trackId) { static const u_int8_t mpegVideoTypes[] = { MP4_MPEG2_SIMPLE_VIDEO_TYPE, // 0x60 MP4_MPEG2_MAIN_VIDEO_TYPE, // 0x61 MP4_MPEG2_SNR_VIDEO_TYPE, // 0x62 MP4_MPEG2_SPATIAL_VIDEO_TYPE, // 0x63 MP4_MPEG2_HIGH_VIDEO_TYPE, // 0x64 MP4_MPEG2_442_VIDEO_TYPE, // 0x65 MP4_MPEG1_VIDEO_TYPE, // 0x6A MP4_JPEG_VIDEO_TYPE, // 0x6C MP4_YUV12_VIDEO_TYPE, MP4_H263_VIDEO_TYPE, MP4_H261_VIDEO_TYPE, }; static const char* mpegVideoNames[] = { "MPEG-2 Simple", "MPEG-2 Main", "MPEG-2 SNR", "MPEG-2 Spatial", "MPEG-2 High", "MPEG-2 4:2:2", "MPEG-1", "JPEG", "YUV12", "H.263", "H.261", }; u_int8_t numMpegVideoTypes = sizeof(mpegVideoTypes) / sizeof(u_int8_t); bool foundTypeName = false; const char* typeName = "Unknown"; const char *media_data_name; char originalFormat[8]; char oformatbuffer[32]; originalFormat[0] = 0; *oformatbuffer = 0; uint8_t type = 0; media_data_name = MP4GetTrackMediaDataName(mp4File, trackId); // encv 264b if (strcasecmp(media_data_name, "encv") == 0) { if (MP4GetTrackMediaDataOriginalFormat(mp4File, trackId, originalFormat, sizeof(originalFormat)) == false) media_data_name = NULL; } char typebuffer[80]; if (media_data_name == NULL) { typeName = "Unknown - no media data name"; foundTypeName = true; } else if ((strcasecmp(media_data_name, "avc1") == 0) || (strcasecmp(originalFormat, "264b") == 0)) { // avc uint8_t profile, level; char profileb[20], levelb[20]; if (MP4GetTrackH264ProfileLevel(mp4File, trackId, &profile, &level)) { if (profile == 66) { strcpy(profileb, "Baseline"); } else if (profile == 77) { strcpy(profileb, "Main"); } else if (profile == 88) { strcpy(profileb, "Extended"); } else if (profile == 100) { strcpy(profileb, "High"); } else if (profile == 110) { strcpy(profileb, "High 10"); } else if (profile == 122) { strcpy(profileb, "High 4:2:2"); } else if (profile == 144) { strcpy(profileb, "High 4:4:4"); } else { snprintf(profileb, 20, "Unknown Profile %x", profile); } switch (level) { case 10: case 20: case 30: case 40: case 50: snprintf(levelb, 20, "%u", level / 10); break; case 11: case 12: case 13: case 21: case 22: case 31: case 32: case 41: case 42: case 51: snprintf(levelb, 20, "%u.%u", level / 10, level % 10); break; default: snprintf(levelb, 20, "unknown level %x", level); break; } if (originalFormat != NULL && originalFormat[0] != '\0') snprintf(oformatbuffer, 32, "(%s) ", originalFormat); snprintf(typebuffer, sizeof(typebuffer), "H264 %s%s@%s", oformatbuffer, profileb, levelb); typeName = typebuffer; } else { typeName = "H.264 - profile/level error"; } foundTypeName = true; } else if (strcasecmp(media_data_name, "s263") == 0) { // 3gp h.263 typeName = "H.263"; foundTypeName = true; } else if ((strcasecmp(media_data_name, "mp4v") == 0) || (strcasecmp(media_data_name, "encv") == 0)) { // note encv might needs it's own field eventually. type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId); if (type == MP4_MPEG4_VIDEO_TYPE) { type = MP4GetVideoProfileLevel(mp4File, trackId); typeName = Mpeg4VisualProfileName(type); if (typeName == NULL) { typeName = "MPEG-4 Unknown Profile"; } else { foundTypeName = true; } } else { for (u_int8_t i = 0; i < numMpegVideoTypes; i++) { if (type == mpegVideoTypes[i]) { typeName = mpegVideoNames[i]; foundTypeName = true; break; } } } } else { typeName = media_data_name; foundTypeName = true; // we don't have a type value to display } MP4Duration trackDuration = MP4GetTrackDuration(mp4File, trackId); double msDuration = UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId, trackDuration, MP4_MSECS_TIME_SCALE)); u_int32_t avgBitRate = MP4GetTrackBitRate(mp4File, trackId); // Note not all mp4 implementations set width and height correctly // The real answer can be buried inside the ES configuration info u_int16_t width = MP4GetTrackVideoWidth(mp4File, trackId); u_int16_t height = MP4GetTrackVideoHeight(mp4File, trackId); double fps = MP4GetTrackVideoFrameRate(mp4File, trackId); char *sInfo = (char*)MP4Malloc(256); // type duration avgBitrate frameSize frameRate if (foundTypeName) { sprintf(sInfo, "%u\tvideo\t%s%s, %.3f secs, %u kbps, %ux%u @ %f fps\n", trackId, MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "encv - " : "", typeName, msDuration / 1000.0, (avgBitRate + 500) / 1000, width, height, fps ); } else { sprintf(sInfo, "%u\tvideo\t%s(%u), %.3f secs, %u kbps, %ux%u @ %f fps\n", trackId, typeName, type, msDuration / 1000.0, (avgBitRate + 500) / 1000, width, height, fps ); } return sInfo; }