| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / mp4box application | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "mp4box.h" | |
| #if defined(GPAC_DISABLE_ISOM) | |
| #error "Cannot compile MP4Box if GPAC is not built with ISO File Format support" | |
| #else | |
| #ifndef GPAC_DISABLE_X3D | |
| #include <gpac/nodes_x3d.h> | |
| #endif | |
| #ifndef GPAC_DISABLE_BIFS | |
| #include <gpac/internal/bifs_dev.h> | |
| #endif | |
| #ifndef GPAC_DISABLE_VRML | |
| #include <gpac/nodes_mpeg4.h> | |
| #endif | |
| #include <gpac/constants.h> | |
| #include <gpac/avparse.h> | |
| #include <gpac/internal/media_dev.h> | |
| /*ISO 639 languages*/ | |
| #include <gpac/iso639.h> | |
| #include <gpac/mpegts.h> | |
| #ifndef GPAC_DISABLE_SMGR | |
| #include <gpac/scene_manager.h> | |
| #endif | |
| #include <gpac/internal/media_dev.h> | |
| #include <gpac/media_tools.h> | |
| /*for built-in box printing*/ | |
| #include <gpac/internal/isomedia_dev.h> | |
| #include <gpac/download.h> | |
| #include <gpac/mpd.h> | |
| #ifndef GPAC_DISABLE_SWF_IMPORT | |
| extern u32 swf_flags; | |
| #endif | |
| extern Double swf_flatten_angle; | |
| extern GF_FileType get_file_type_by_ext(char *inName); | |
| extern u32 fs_dump_flags; | |
| extern Bool dump_check_xml; | |
| void scene_coding_log(void *cbk, GF_LOG_Level log_level, GF_LOG_Tool log_tool, const char *fmt, va_list vlist); | |
| #ifdef GPAC_DISABLE_LOG | |
| void mp4box_log(const char *fmt, ...) | |
| { | |
| va_list vl; | |
| va_start(vl, fmt); | |
| vfprintf(stderr, fmt, vl); | |
| fflush(stderr); | |
| va_end(vl); | |
| } | |
| #endif | |
| u32 PrintLanguages(char *val, u32 opt) | |
| { | |
| u32 i=0, count = gf_lang_get_count(); | |
| fprintf(stderr, "Supported ISO 639 languages and codes:\n\n"); | |
| for (i=0; i<count; i++) { | |
| if (gf_lang_get_2cc(i)) { | |
| fprintf(stderr, "%s (%s - %s)\n", gf_lang_get_name(i), gf_lang_get_3cc(i), gf_lang_get_2cc(i)); | |
| } | |
| } | |
| return 1; | |
| } | |
| static const char *GetLanguage(char *lcode) | |
| { | |
| s32 idx = gf_lang_find(lcode); | |
| if (idx>=0) return gf_lang_get_name(idx); | |
| return lcode; | |
| } | |
| GF_Err dump_isom_cover_art(GF_ISOFile *file, char *inName, Bool is_final_name) | |
| { | |
| const u8 *tag; | |
| FILE *t; | |
| u32 tag_len; | |
| GF_Err e = gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_COVER_ART, &tag, &tag_len); | |
| if (e!=GF_OK) { | |
| if (e==GF_URL_ERROR) { | |
| M4_LOG(GF_LOG_WARNING, ("No cover art found\n")); | |
| return GF_OK; | |
| } | |
| return e; | |
| } | |
| if (inName) { | |
| char *szName=NULL; | |
| gf_dynstrcat(&szName, inName, NULL); | |
| if (!is_final_name) { | |
| gf_dynstrcat(&szName, (tag_len>>31) ? ".png" : ".jpg", NULL); | |
| } | |
| if (!szName) return GF_OUT_OF_MEM; | |
| t = gf_fopen(szName, "wb"); | |
| if (!t) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szName)); | |
| gf_free(szName); | |
| return GF_IO_ERR; | |
| } | |
| gf_free(szName); | |
| } else { | |
| t = stdout; | |
| } | |
| tag_len &= 0x7FFFFFFF; | |
| if (gf_fwrite(tag, tag_len, t) != tag_len) | |
| e = GF_IO_ERR; | |
| if (inName) gf_fclose(t); | |
| return e; | |
| } | |
| #ifndef GPAC_DISABLE_SCENE_DUMP | |
| GF_Err dump_isom_scene(char *file, char *inName, Bool is_final_name, GF_SceneDumpFormat dump_mode, Bool do_log, Bool no_odf_conv) | |
| { | |
| GF_Err e; | |
| GF_SceneManager *ctx; | |
| GF_SceneGraph *sg; | |
| GF_SceneLoader load; | |
| GF_FileType ftype; | |
| gf_log_cbk prev_logs = NULL; | |
| FILE *logs = NULL; | |
| sg = gf_sg_new(); | |
| ctx = gf_sm_new(sg); | |
| memset(&load, 0, sizeof(GF_SceneLoader)); | |
| load.fileName = file; | |
| load.ctx = ctx; | |
| #ifndef GPAC_DISABLE_SWF_IMPORT | |
| load.swf_import_flags = swf_flags; | |
| #endif | |
| if (dump_mode == GF_SM_DUMP_SVG) { | |
| load.swf_import_flags |= GF_SM_SWF_USE_SVG; | |
| load.svgOutFile = inName; | |
| } | |
| load.swf_flatten_limit = (Float) swf_flatten_angle; | |
| ftype = get_file_type_by_ext(file); | |
| if (ftype == GF_FILE_TYPE_ISO_MEDIA) { | |
| load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL); | |
| if (!load.isom) { | |
| e = gf_isom_last_error(NULL); | |
| M4_LOG(GF_LOG_ERROR, ("Error opening file: %s\n", gf_error_to_string(e))); | |
| gf_sm_del(ctx); | |
| gf_sg_del(sg); | |
| return e; | |
| } | |
| if (no_odf_conv) | |
| gf_isom_disable_odf_conversion(load.isom, GF_TRUE); | |
| } else if (ftype==GF_FILE_TYPE_LSR_SAF) { | |
| load.isom = gf_isom_open("saf_conv", GF_ISOM_WRITE_EDIT, NULL); | |
| #ifndef GPAC_DISABLE_MEDIA_IMPORT | |
| if (load.isom) { | |
| GF_Fraction _frac = {0,0}; | |
| e = import_file(load.isom, file, 0, _frac, 0, NULL, NULL, NULL, 0); | |
| } else | |
| #else | |
| M4_LOG(GF_LOG_WARNING, ("Warning: GPAC was compiled without Media Import support\n")); | |
| #endif | |
| e = gf_isom_last_error(NULL); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("Error importing file: %s\n", gf_error_to_string(e))); | |
| gf_sm_del(ctx); | |
| gf_sg_del(sg); | |
| if (load.isom) gf_isom_delete(load.isom); | |
| return e; | |
| } | |
| } | |
| if (do_log) { | |
| char szLog[GF_MAX_PATH]; | |
| snprintf(szLog, GF_ARRAY_LENGTH(szLog), "%s_dec.logs", inName); | |
| logs = gf_fopen(szLog, "wt"); | |
| if (logs) { | |
| gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_DEBUG); | |
| prev_logs = gf_log_set_callback(logs, scene_coding_log); | |
| } | |
| } | |
| e = gf_sm_load_init(&load); | |
| if (!e) e = gf_sm_load_run(&load); | |
| gf_sm_load_done(&load); | |
| if (logs) { | |
| gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_ERROR); | |
| gf_log_set_callback(NULL, prev_logs); | |
| gf_fclose(logs); | |
| } | |
| if (!e && dump_mode != GF_SM_DUMP_SVG) { | |
| u32 count = gf_list_count(ctx->streams); | |
| if (count) | |
| fprintf(stderr, "Scene loaded - dumping %d systems streams\n", count); | |
| else | |
| fprintf(stderr, "Scene loaded - dumping root scene\n"); | |
| e = gf_sm_dump(ctx, inName, is_final_name, dump_mode); | |
| } | |
| gf_sm_del(ctx); | |
| gf_sg_del(sg); | |
| if (e) M4_LOG(GF_LOG_ERROR, ("Error loading scene: %s\n", gf_error_to_string(e))); | |
| if (load.isom) gf_isom_delete(load.isom); | |
| return e; | |
| } | |
| #endif | |
| #ifndef GPAC_DISABLE_SCENE_STATS | |
| static void dump_stats(FILE *dump, const GF_SceneStatistics *stats) | |
| { | |
| u32 i; | |
| s32 created, count, draw_created, draw_count, deleted, draw_deleted; | |
| created = count = draw_created = draw_count = deleted = draw_deleted = 0; | |
| fprintf(dump, "<NodeStatistics>\n"); | |
| fprintf(dump, "<General NumberOfNodeTypes=\"%d\"/>\n", gf_list_count(stats->node_stats)); | |
| for (i=0; i<gf_list_count(stats->node_stats); i++) { | |
| GF_NodeStats *ptr = gf_list_get(stats->node_stats, i); | |
| fprintf(dump, "<NodeStat NodeName=\"%s\">\n", ptr->name); | |
| switch (ptr->tag) { | |
| #ifndef GPAC_DISABLE_VRML | |
| case TAG_MPEG4_Bitmap: | |
| case TAG_MPEG4_Background2D: | |
| case TAG_MPEG4_Background: | |
| case TAG_MPEG4_Box: | |
| case TAG_MPEG4_Circle: | |
| case TAG_MPEG4_CompositeTexture2D: | |
| case TAG_MPEG4_CompositeTexture3D: | |
| case TAG_MPEG4_Cylinder: | |
| case TAG_MPEG4_Cone: | |
| case TAG_MPEG4_Curve2D: | |
| case TAG_MPEG4_Extrusion: | |
| case TAG_MPEG4_ElevationGrid: | |
| case TAG_MPEG4_IndexedFaceSet2D: | |
| case TAG_MPEG4_IndexedFaceSet: | |
| case TAG_MPEG4_IndexedLineSet2D: | |
| case TAG_MPEG4_IndexedLineSet: | |
| case TAG_MPEG4_PointSet2D: | |
| case TAG_MPEG4_PointSet: | |
| case TAG_MPEG4_Rectangle: | |
| case TAG_MPEG4_Sphere: | |
| case TAG_MPEG4_Text: | |
| case TAG_MPEG4_Ellipse: | |
| case TAG_MPEG4_XCurve2D: | |
| draw_count += ptr->nb_created + ptr->nb_used - ptr->nb_del; | |
| draw_deleted += ptr->nb_del; | |
| draw_created += ptr->nb_created; | |
| break; | |
| #endif /*GPAC_DISABLE_VRML*/ | |
| } | |
| fprintf(dump, "<Instanciation NbObjects=\"%d\" NbUse=\"%d\" NbDestroy=\"%d\"/>\n", ptr->nb_created, ptr->nb_used, ptr->nb_del); | |
| count += ptr->nb_created + ptr->nb_used; | |
| deleted += ptr->nb_del; | |
| created += ptr->nb_created; | |
| fprintf(dump, "</NodeStat>\n"); | |
| } | |
| if (i) { | |
| fprintf(dump, "<CumulatedStat TotalNumberOfNodes=\"%d\" ReallyAllocatedNodes=\"%d\" DeletedNodes=\"%d\" NumberOfAttributes=\"%d\"/>\n", count, created, deleted, stats->nb_svg_attributes); | |
| fprintf(dump, "<DrawableNodesCumulatedStat TotalNumberOfNodes=\"%d\" ReallyAllocatedNodes=\"%d\" DeletedNodes=\"%d\"/>\n", draw_count, draw_created, draw_deleted); | |
| } | |
| fprintf(dump, "</NodeStatistics>\n"); | |
| created = count = deleted = 0; | |
| if (gf_list_count(stats->proto_stats)) { | |
| fprintf(dump, "<ProtoStatistics NumberOfProtoUsed=\"%d\">\n", gf_list_count(stats->proto_stats)); | |
| for (i=0; i<gf_list_count(stats->proto_stats); i++) { | |
| GF_NodeStats *ptr = gf_list_get(stats->proto_stats, i); | |
| fprintf(dump, "<ProtoStat ProtoName=\"%s\">\n", ptr->name); | |
| fprintf(dump, "<Instanciation NbObjects=\"%d\" NbUse=\"%d\" NbDestroy=\"%d\"/>\n", ptr->nb_created, ptr->nb_used, ptr->nb_del); | |
| count += ptr->nb_created + ptr->nb_used; | |
| deleted += ptr->nb_del; | |
| created += ptr->nb_created; | |
| fprintf(dump, "</ProtoStat>\n"); | |
| } | |
| if (i) fprintf(dump, "<CumulatedStat TotalNumberOfProtos=\"%d\" ReallyAllocatedProtos=\"%d\" DeletedProtos=\"%d\"/>\n", count, created, deleted); | |
| fprintf(dump, "</ProtoStatistics>\n"); | |
| } | |
| fprintf(dump, "<FixedValues min=\"%f\" max=\"%f\">\n", FIX2FLT( stats->min_fixed) , FIX2FLT( stats->max_fixed )); | |
| fprintf(dump, "<Resolutions scaleIntegerPart=\"%d\" scaleFracPart=\"%d\" coordIntegerPart=\"%d\" coordFracPart=\"%d\"/>\n", stats->scale_int_res_2d, stats->scale_frac_res_2d, stats->int_res_2d, stats->frac_res_2d); | |
| fprintf(dump, "</FixedValues>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"MFVec2f\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>\n", stats->count_2d, stats->rem_2d); | |
| if (stats->count_2d) { | |
| fprintf(dump, "<ExtendInfo MinVec2f=\"%f %f\" MaxVec2f=\"%f %f\"/>\n", FIX2FLT( stats->min_2d.x) , FIX2FLT( stats->min_2d.y ), FIX2FLT( stats->max_2d.x ), FIX2FLT( stats->max_2d.y ) ); | |
| } | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"MFVec3f\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_3d, stats->rem_3d); | |
| if (stats->count_3d) { | |
| fprintf(dump, "<ExtendInfo MinVec3f=\"%f %f %f\" MaxVec3f=\"%f %f %f\"/>\n", FIX2FLT( stats->min_3d.x ), FIX2FLT( stats->min_3d.y ), FIX2FLT( stats->min_3d.z ), FIX2FLT( stats->max_3d.x ), FIX2FLT( stats->max_3d.y ), FIX2FLT( stats->max_3d.z ) ); | |
| } | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"MF/SFColor\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_color, stats->rem_color); | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"MF/SFFloat\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_float, stats->rem_float); | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"SFVec2f\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\"/>", stats->count_2f); | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| fprintf(dump, "<FieldStatistic FieldType=\"SFVec3f\">\n"); | |
| fprintf(dump, "<ParsingInfo NumParsed=\"%d\"/>", stats->count_3f); | |
| fprintf(dump, "</FieldStatistic>\n"); | |
| } | |
| static void ReorderAU(GF_List *sample_list, GF_AUContext *au) | |
| { | |
| u32 i; | |
| for (i=0; i<gf_list_count(sample_list); i++) { | |
| GF_AUContext *ptr = gf_list_get(sample_list, i); | |
| if ( | |
| /*time ordered*/ | |
| (ptr->timing_sec > au->timing_sec) | |
| /*set bifs first*/ | |
| || ((ptr->timing_sec == au->timing_sec) && (ptr->owner->streamType < au->owner->streamType)) | |
| ) { | |
| gf_list_insert(sample_list, au, i); | |
| return; | |
| } | |
| } | |
| gf_list_add(sample_list, au); | |
| } | |
| void dump_isom_scene_stats(char *file, char *inName, Bool is_final_name, u32 stat_level) | |
| { | |
| GF_Err e; | |
| FILE *dump; | |
| Bool close; | |
| u32 i, j, count; | |
| char szBuf[1024]; | |
| GF_SceneManager *ctx; | |
| GF_SceneLoader load; | |
| GF_StatManager *sm; | |
| GF_List *sample_list; | |
| GF_SceneGraph *scene_graph; | |
| dump = NULL; | |
| sm = NULL; | |
| sample_list = NULL; | |
| e = GF_OK; | |
| close = 0; | |
| scene_graph = gf_sg_new(); | |
| ctx = gf_sm_new(scene_graph); | |
| memset(&load, 0, sizeof(GF_SceneLoader)); | |
| load.fileName = file; | |
| load.ctx = ctx; | |
| if (get_file_type_by_ext(file) == GF_FILE_TYPE_ISO_MEDIA) { | |
| load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL); | |
| if (!load.isom) { | |
| M4_LOG(GF_LOG_ERROR, ("Cannot open file: %s\n", gf_error_to_string(gf_isom_last_error(NULL)))); | |
| gf_sm_del(ctx); | |
| gf_sg_del(scene_graph); | |
| return; | |
| } | |
| } | |
| e = gf_sm_load_init(&load); | |
| if (!e) e = gf_sm_load_run(&load); | |
| gf_sm_load_done(&load); | |
| if (e<0) goto exit; | |
| if (inName) { | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) gf_strcat(szBuf, "_stat.xml"); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| goto exit; | |
| } | |
| close = 1; | |
| } else { | |
| dump = stdout; | |
| close = 0; | |
| } | |
| fprintf(stderr, "Analysing Scene\n"); | |
| fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); | |
| fprintf(dump, "<!-- Scene Graph Statistics Generated by MP4Box - GPAC "); | |
| if (! gf_sys_is_test_mode()) | |
| fprintf(dump, "%s ", gf_gpac_version()); | |
| fprintf(dump, "-->\n"); | |
| fprintf(dump, "<SceneStatistics file=\"%s\" DumpType=\"%s\">\n", gf_file_basename(file), (stat_level==1) ? "full scene" : ((stat_level==2) ? "AccessUnit based" : "SceneGraph after each AU")); | |
| sm = gf_sm_stats_new(); | |
| /*stat level 1: complete scene stat*/ | |
| if (stat_level == 1) { | |
| e = gf_sm_stats_for_scene(sm, ctx); | |
| if (!e) dump_stats(dump, gf_sm_stats_get(sm) ); | |
| goto exit; | |
| } | |
| /*re_order all BIFS-AUs*/ | |
| sample_list = gf_list_new(); | |
| /*configure all systems streams we're dumping*/ | |
| for (i=0; i<gf_list_count(ctx->streams); i++) { | |
| GF_StreamContext *sc = gf_list_get(ctx->streams, i); | |
| if (sc->streamType != GF_STREAM_SCENE) continue; | |
| for (j=0; j<gf_list_count(sc->AUs); j++) { | |
| GF_AUContext *au = gf_list_get(sc->AUs, j); | |
| ReorderAU(sample_list, au); | |
| } | |
| } | |
| count = gf_list_count(sample_list); | |
| for (i=0; i<count; i++) { | |
| GF_AUContext *au = gf_list_get(sample_list, i); | |
| for (j=0; j<gf_list_count(au->commands); j++) { | |
| GF_Command *com = gf_list_get(au->commands, j); | |
| /*stat level 2 - get command stats*/ | |
| if (stat_level==2) { | |
| e = gf_sm_stats_for_command(sm, com); | |
| if (e) goto exit; | |
| } | |
| /*stat level 3 - apply command*/ | |
| if (stat_level==3) gf_sg_command_apply(scene_graph, com, 0); | |
| } | |
| /*stat level 3: get graph stat*/ | |
| if (stat_level==3) { | |
| e = gf_sm_stats_for_graph(sm, scene_graph); | |
| if (e) goto exit; | |
| } | |
| if (stat_level==2) { | |
| fprintf(dump, "<AUStatistics StreamID=\"%d\" AUTime=\""LLD"\">\n", au->owner->ESID, au->timing); | |
| } else { | |
| fprintf(dump, "<GraphStatistics StreamID=\"%d\" AUTime=\""LLD"\">\n", au->owner->ESID, au->timing); | |
| } | |
| /*dump stats*/ | |
| dump_stats(dump, gf_sm_stats_get(sm) ); | |
| /*reset stats*/ | |
| gf_sm_stats_reset(sm); | |
| if (stat_level==2) { | |
| fprintf(dump, "</AUStatistics>\n"); | |
| } else { | |
| fprintf(dump, "</GraphStatistics>\n"); | |
| } | |
| gf_set_progress("Analysing AU", i+1, count); | |
| } | |
| exit: | |
| if (sample_list) gf_list_del(sample_list); | |
| if (sm) gf_sm_stats_del(sm); | |
| gf_sm_del(ctx); | |
| gf_sg_del(scene_graph); | |
| if (load.isom) gf_isom_delete(load.isom); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("Stats error: %s\n", gf_error_to_string(e))); | |
| } else { | |
| fprintf(dump, "</SceneStatistics>\n"); | |
| } | |
| if (dump && close) gf_fclose(dump); | |
| fprintf(stderr, "done\n"); | |
| } | |
| #endif /*GPAC_DISABLE_SCENE_STATS*/ | |
| #ifndef GPAC_DISABLE_VRML | |
| static void PrintFixed(Fixed val, Bool add_space) | |
| { | |
| if (add_space) fprintf(stderr, " "); | |
| if (val==FIX_MIN) fprintf(stderr, "-I"); | |
| else if (val==FIX_MAX) fprintf(stderr, "+I"); | |
| else fprintf(stderr, "%g", FIX2FLT(val)); | |
| } | |
| static void PrintNodeSFField(u32 type, void *far_ptr) | |
| { | |
| if (!far_ptr) return; | |
| switch (type) { | |
| case GF_SG_VRML_SFBOOL: | |
| fprintf(stderr, "%s", (*(SFBool *)far_ptr) ? "TRUE" : "FALSE"); | |
| break; | |
| case GF_SG_VRML_SFINT32: | |
| fprintf(stderr, "%d", (*(SFInt32 *)far_ptr)); | |
| break; | |
| case GF_SG_VRML_SFFLOAT: | |
| PrintFixed((*(SFFloat *)far_ptr), 0); | |
| break; | |
| case GF_SG_VRML_SFTIME: | |
| fprintf(stderr, "%g", (*(SFTime *)far_ptr)); | |
| break; | |
| case GF_SG_VRML_SFVEC2F: | |
| PrintFixed(((SFVec2f *)far_ptr)->x, 0); | |
| PrintFixed(((SFVec2f *)far_ptr)->y, 1); | |
| break; | |
| case GF_SG_VRML_SFVEC3F: | |
| PrintFixed(((SFVec3f *)far_ptr)->x, 0); | |
| PrintFixed(((SFVec3f *)far_ptr)->y, 1); | |
| PrintFixed(((SFVec3f *)far_ptr)->z, 1); | |
| break; | |
| case GF_SG_VRML_SFROTATION: | |
| PrintFixed(((SFRotation *)far_ptr)->x, 0); | |
| PrintFixed(((SFRotation *)far_ptr)->y, 1); | |
| PrintFixed(((SFRotation *)far_ptr)->z, 1); | |
| PrintFixed(((SFRotation *)far_ptr)->q, 1); | |
| break; | |
| case GF_SG_VRML_SFCOLOR: | |
| PrintFixed(((SFColor *)far_ptr)->red, 0); | |
| PrintFixed(((SFColor *)far_ptr)->green, 1); | |
| PrintFixed(((SFColor *)far_ptr)->blue, 1); | |
| break; | |
| case GF_SG_VRML_SFSTRING: | |
| if (((SFString*)far_ptr)->buffer) | |
| fprintf(stderr, "\"%s\"", ((SFString*)far_ptr)->buffer); | |
| else | |
| fprintf(stderr, "NULL"); | |
| break; | |
| } | |
| } | |
| #endif | |
| #ifndef GPAC_DISABLE_VRML | |
| static void do_print_node(GF_Node *node, GF_SceneGraph *sg, const char *name, u32 graph_type, Bool is_nodefield, Bool do_cov) | |
| { | |
| u32 nbF, i; | |
| GF_FieldInfo f; | |
| #ifndef GPAC_DISABLE_BIFS | |
| u8 qt, at; | |
| Fixed bmin, bmax; | |
| u32 nbBits; | |
| #endif /*GPAC_DISABLE_BIFS*/ | |
| nbF = gf_node_get_field_count(node); | |
| if (is_nodefield) { | |
| char szField[1024]; | |
| u32 tfirst, tlast; | |
| if (gf_node_get_field_by_name(node, szField, &f) != GF_OK) { | |
| M4_LOG(GF_LOG_ERROR, ("Field %s is not a member of node %s\n", szField, name)); | |
| return; | |
| } | |
| fprintf(stderr, "Allowed nodes in %s.%s:\n", name, szField); | |
| if (graph_type==1) { | |
| tfirst = GF_NODE_RANGE_FIRST_X3D; | |
| tlast = GF_NODE_RANGE_LAST_X3D; | |
| } else { | |
| tfirst = GF_NODE_RANGE_FIRST_MPEG4; | |
| tlast = GF_NODE_RANGE_LAST_MPEG4; | |
| } | |
| for (i=tfirst; i<tlast; i++) { | |
| GF_Node *tmp = gf_node_new(sg, i); | |
| gf_node_register(tmp, NULL); | |
| if (gf_node_in_table_by_tag(i, f.NDTtype)) { | |
| const char *nname = gf_node_get_class_name(tmp); | |
| if (nname && strcmp(nname, "Unknown Node")) { | |
| fprintf(stderr, "\t%s\n", nname); | |
| } | |
| } | |
| gf_node_unregister(tmp, NULL); | |
| } | |
| return; | |
| } | |
| if (do_cov) { | |
| u32 ndt; | |
| if (graph_type==0) { | |
| u32 all; | |
| gf_node_mpeg4_type_by_class_name(name); | |
| #ifndef GPAC_DISABLE_BIFS | |
| gf_bifs_get_child_table(node); | |
| all = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); | |
| for (i=0; i<all; i++) { | |
| u32 res; | |
| gf_sg_script_get_field_index(node, i, GF_SG_FIELD_CODING_ALL, &res); | |
| } | |
| gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF); | |
| gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN); | |
| gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_OUT); | |
| gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DYN); | |
| #endif | |
| } | |
| #ifndef GPAC_DISABLE_X3D | |
| else if (graph_type==1) gf_node_x3d_type_by_class_name(name); | |
| #endif | |
| for (ndt=NDT_SFWorldNode; ndt<NDT_LAST; ndt++) { | |
| gf_node_in_table_by_tag(gf_node_get_tag(node), ndt); | |
| } | |
| } | |
| fprintf(stderr, "%s {\n", name); | |
| for (i=0; i<nbF; i++) { | |
| gf_node_get_field(node, i, &f); | |
| if (graph_type==2) { | |
| fprintf(stderr, "\t%s=\"...\"\n", f.name); | |
| continue; | |
| } | |
| fprintf(stderr, "\t%s %s %s", gf_sg_vrml_get_event_type_name(f.eventType, 0), gf_sg_vrml_get_field_type_name(f.fieldType), f.name); | |
| if (f.fieldType==GF_SG_VRML_SFNODE) fprintf(stderr, " NULL"); | |
| else if (f.fieldType==GF_SG_VRML_MFNODE) fprintf(stderr, " []"); | |
| else if (gf_sg_vrml_is_sf_field(f.fieldType)) { | |
| fprintf(stderr, " "); | |
| PrintNodeSFField(f.fieldType, f.far_ptr); | |
| } else { | |
| void *ptr; | |
| u32 j, sftype; | |
| GenMFField *mffield = (GenMFField *) f.far_ptr; | |
| fprintf(stderr, " ["); | |
| sftype = gf_sg_vrml_get_sf_type(f.fieldType); | |
| for (j=0; j<mffield->count; j++) { | |
| if (j) fprintf(stderr, " "); | |
| gf_sg_vrml_mf_get_item(f.far_ptr, f.fieldType, &ptr, j); | |
| PrintNodeSFField(sftype, ptr); | |
| } | |
| fprintf(stderr, "]"); | |
| } | |
| #ifndef GPAC_DISABLE_BIFS | |
| if (gf_bifs_get_aq_info(node, i, &qt, &at, &bmin, &bmax, &nbBits)) { | |
| if (qt) { | |
| fprintf(stderr, " #QP=%d", qt); | |
| if (qt==13) fprintf(stderr, " NbBits=%d", nbBits); | |
| if (bmin && bmax) { | |
| fprintf(stderr, " Bounds=["); | |
| PrintFixed(bmin, 0); | |
| fprintf(stderr, ","); | |
| PrintFixed(bmax, 0); | |
| fprintf(stderr, "]"); | |
| } | |
| } | |
| } | |
| #endif /*GPAC_DISABLE_BIFS*/ | |
| fprintf(stderr, "\n"); | |
| if (do_cov) { | |
| gf_node_get_field_by_name(node, (char *) f.name, &f); | |
| } | |
| } | |
| fprintf(stderr, "}\n\n"); | |
| } | |
| #endif | |
| u32 PrintNode(const char *name, u32 graph_type) | |
| { | |
| #ifdef GPAC_DISABLE_VRML | |
| M4_LOG(GF_LOG_ERROR, ("VRML/MPEG-4/X3D scene graph is disabled in this build of GPAC\n")); | |
| return 2; | |
| #else | |
| const char *std_name; | |
| GF_Node *node; | |
| GF_SceneGraph *sg; | |
| u32 tag; | |
| #ifndef GPAC_DISABLE_BIFS | |
| #endif /*GPAC_DISABLE_BIFS*/ | |
| Bool is_nodefield = 0; | |
| char *sep = strchr(name, '.'); | |
| if (sep) { | |
| sep[0] = 0; | |
| is_nodefield = 1; | |
| } | |
| if (graph_type==1) { | |
| #ifndef GPAC_DISABLE_X3D | |
| tag = gf_node_x3d_type_by_class_name(name); | |
| std_name = "X3D"; | |
| #else | |
| M4_LOG(GF_LOG_ERROR, ("X3D node printing is not supported (X3D support disabled)\n")); | |
| return 2; | |
| #endif | |
| } else { | |
| tag = gf_node_mpeg4_type_by_class_name(name); | |
| std_name = "MPEG4"; | |
| } | |
| if (!tag) { | |
| M4_LOG(GF_LOG_ERROR, ("Unknown %s node %s\n", std_name, name)); | |
| return 2; | |
| } | |
| sg = gf_sg_new(); | |
| node = gf_node_new(sg, tag); | |
| gf_node_register(node, NULL); | |
| name = gf_node_get_class_name(node); | |
| if (!node) { | |
| M4_LOG(GF_LOG_ERROR, ("Node %s not supported in current built\n", name)); | |
| return 2; | |
| } | |
| do_print_node(node, sg, name, graph_type, is_nodefield, GF_FALSE); | |
| gf_node_unregister(node, NULL); | |
| gf_sg_del(sg); | |
| #endif /*GPAC_DISABLE_VRML*/ | |
| return 1; | |
| } | |
| u32 PrintBuiltInNodes(char *arg_val, u32 dump_type) | |
| { | |
| #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_X3D) && !defined(GPAC_DISABLE_SVG) | |
| GF_SceneGraph *sg; | |
| u32 i, nb_in, nb_not_in, start_tag, end_tag; | |
| u32 graph_type; | |
| Bool dump_nodes = ((dump_type==1) || (dump_type==3)) ? 1 : 0; | |
| if (dump_type==4) graph_type = 2; | |
| else if ((dump_type==2) || (dump_type==3)) graph_type = 1; | |
| else graph_type = 0; | |
| if (graph_type==1) { | |
| #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_X3D) | |
| start_tag = GF_NODE_RANGE_FIRST_X3D; | |
| end_tag = TAG_LastImplementedX3D; | |
| #else | |
| M4_LOG(GF_LOG_ERROR, ("X3D scene graph disabled in this build of GPAC\n")); | |
| return 2; | |
| #endif | |
| } else if (graph_type==2) { | |
| #ifdef GPAC_DISABLE_SVG | |
| M4_LOG(GF_LOG_ERROR, ("SVG scene graph disabled in this build of GPAC\n")); | |
| return 2; | |
| #else | |
| start_tag = GF_NODE_RANGE_FIRST_SVG; | |
| end_tag = GF_NODE_RANGE_LAST_SVG; | |
| #endif | |
| } else { | |
| #ifdef GPAC_DISABLE_VRML | |
| M4_LOG(GF_LOG_ERROR, ("VRML/MPEG-4 scene graph disabled in this build of GPAC\n")); | |
| return 2; | |
| #else | |
| start_tag = GF_NODE_RANGE_FIRST_MPEG4; | |
| end_tag = TAG_LastImplementedMPEG4; | |
| #endif | |
| } | |
| nb_in = nb_not_in = 0; | |
| sg = gf_sg_new(); | |
| if (graph_type==1) { | |
| fprintf(stderr, "Available X3D nodes in this build (dumping):\n"); | |
| } else if (graph_type==2) { | |
| fprintf(stderr, "Available SVG nodes in this build (dumping and LASeR coding):\n"); | |
| } else { | |
| fprintf(stderr, "Available MPEG-4 nodes in this build (encoding/decoding/dumping):\n"); | |
| } | |
| for (i=start_tag; i<end_tag; i++) { | |
| GF_Node *node = gf_node_new(sg, i); | |
| if (node) { | |
| gf_node_register(node, NULL); | |
| if (dump_nodes) { | |
| do_print_node(node, sg, gf_node_get_class_name(node), graph_type, GF_FALSE, GF_TRUE); | |
| } else { | |
| fprintf(stderr, " %s\n", gf_node_get_class_name(node)); | |
| } | |
| gf_node_unregister(node, NULL); | |
| nb_in++; | |
| } else { | |
| if (graph_type==2) | |
| break; | |
| nb_not_in++; | |
| } | |
| } | |
| gf_sg_del(sg); | |
| if (graph_type==2) { | |
| fprintf(stderr, "\n%d nodes supported\n", nb_in); | |
| } else { | |
| fprintf(stderr, "\n%d nodes supported - %d nodes not supported\n", nb_in, nb_not_in); | |
| } | |
| //coverage | |
| if (dump_nodes) { | |
| for (i=GF_SG_VRML_SFBOOL; i<GF_SG_VRML_SCRIPT_FUNCTION; i++) { | |
| void *fp = gf_sg_vrml_field_pointer_new(i); | |
| if (fp) { | |
| if (i==GF_SG_VRML_SFSCRIPT) gf_free(fp); | |
| else gf_sg_vrml_field_pointer_del(fp, i); | |
| } | |
| } | |
| } | |
| #else | |
| M4_LOG(GF_LOG_ERROR, ("No scene graph enabled in this MP4Box build\n")); | |
| #endif | |
| return 1; | |
| } | |
| u32 PrintBuiltInBoxes(char *argval, u32 do_cov) | |
| { | |
| u32 i, count=gf_isom_get_num_supported_boxes(); | |
| fprintf(stdout, "<Boxes>\n"); | |
| //index 0 is our internal unknown box handler | |
| for (i=1; i<count; i++) { | |
| #ifndef GPAC_DISABLE_ISOM_DUMP | |
| gf_isom_dump_supported_box(i, stdout); | |
| #endif | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| if (do_cov) { | |
| u32 btype = gf_isom_get_supported_box_type(i); | |
| GF_Box *b=gf_isom_box_new(btype); | |
| if (b) { | |
| GF_Box *c=NULL; | |
| gf_isom_clone_box(b, &c); | |
| if (c) gf_isom_box_del(c); | |
| gf_isom_box_del(b); | |
| } | |
| } | |
| #endif | |
| } | |
| fprintf(stdout, "</Boxes>\n"); | |
| if (do_cov) { | |
| i=0; | |
| //check all tags are registered in box factory | |
| while (1) { | |
| s32 res = gf_itags_get_type(i); | |
| if (res==-1) break; | |
| u32 itype = gf_itags_get_itag(i); | |
| if (!itype) { | |
| i++; | |
| continue; | |
| } | |
| GF_Box *b=gf_isom_box_new(itype); | |
| Bool valid = (!b || (b->type==GF_ISOM_BOX_TYPE_UNKNOWN)) ? GF_FALSE : GF_TRUE; | |
| if (b) gf_isom_box_del(b); | |
| if (!valid) { | |
| M4_LOG(GF_LOG_ERROR, ("Tag %s not registered in box factory\n", gf_4cc_to_str(itype) )); | |
| return 2; | |
| } | |
| i++; | |
| } | |
| } | |
| return 1; | |
| } | |
| #if !defined(GPAC_DISABLE_ISOM_HINTING) && !defined(GPAC_DISABLE_ISOM_DUMP) | |
| void dump_isom_rtp(GF_ISOFile *file, char *inName, Bool is_final_name) | |
| { | |
| u32 i, j, size; | |
| FILE *dump; | |
| const char *sdp; | |
| if (inName) { | |
| char szBuf[1024]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) gf_strcat(szBuf, "_rtp.xml"); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); | |
| fprintf(dump, "<!-- MP4Box RTP trace -->\n"); | |
| fprintf(dump, "<RTPFile>\n"); | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| if (gf_isom_get_media_type(file, i+1) != GF_ISOM_MEDIA_HINT) continue; | |
| fprintf(dump, "<RTPHintTrack trackID=\"%d\">\n", gf_isom_get_track_id(file, i+1)); | |
| gf_isom_sdp_track_get(file, i+1, &sdp, &size); | |
| if (sdp && size) | |
| fprintf(dump, "<SDPInfo>%s</SDPInfo>", sdp); | |
| #ifndef GPAC_DISABLE_ISOM_HINTING | |
| for (j=0; j<gf_isom_get_sample_count(file, i+1); j++) { | |
| gf_isom_dump_hint_sample(file, i+1, j+1, dump); | |
| } | |
| #endif | |
| fprintf(dump, "</RTPHintTrack>\n"); | |
| } | |
| fprintf(dump, "</RTPFile>\n"); | |
| if (inName) gf_fclose(dump); | |
| } | |
| #endif | |
| void dump_isom_timestamps(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_mode) | |
| { | |
| u32 i, j, k, count; | |
| Bool has_ctts_error, is_fragmented=GF_FALSE; | |
| FILE *dump; | |
| Bool skip_offset = ((dump_mode==2) || (dump_mode==4)) ? GF_TRUE : GF_FALSE; | |
| Bool check_ts = ((dump_mode==3) || (dump_mode==4)) ? GF_TRUE : GF_FALSE; | |
| struct _ts_info { | |
| u64 dts; | |
| s64 cts; | |
| }; | |
| struct _ts_info *timings = NULL; | |
| u32 nb_timings=0, nb_timings_alloc = 0; | |
| if (inName) { | |
| char szBuf[1024]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) gf_strcat(szBuf, "_ts.txt"); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| if (gf_isom_is_fragmented(file)) | |
| is_fragmented = GF_TRUE; | |
| has_ctts_error = GF_FALSE; | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| s64 cts_dts_shift = gf_isom_get_cts_to_dts_shift(file, i+1); | |
| u32 has_cts_offset = gf_isom_has_time_offset(file, i+1); | |
| fprintf(dump, "#dumping track ID %d timing:\n", gf_isom_get_track_id(file, i + 1)); | |
| fprintf(dump, "Num\tDTS\tCTS\tSize\tRAP%s\tisLeading\tDependsOn\tDependedOn\tRedundant\tRAP-SampleGroup\tRoll-SampleGroup\tRoll-Distance", skip_offset ? "" : "\tOffset"); | |
| if (is_fragmented) { | |
| fprintf(dump, "\tfrag_start"); | |
| } | |
| fprintf(dump, "\n"); | |
| count = gf_isom_get_sample_count(file, i+1); | |
| if (has_cts_offset && check_ts) { | |
| if (nb_timings_alloc<count) { | |
| nb_timings_alloc = count; | |
| timings = gf_realloc(timings, sizeof (struct _ts_info) * count); | |
| } | |
| nb_timings = 0; | |
| } | |
| for (j=0; j<count; j++) { | |
| s64 cts; | |
| u64 dts, offset; | |
| u32 isLeading, dependsOn, dependedOn, redundant; | |
| Bool is_rap; | |
| GF_ISOSampleRollType roll_type; | |
| s32 roll_distance; | |
| u32 index; | |
| GF_ISOSample *samp = gf_isom_get_sample_info(file, i+1, j+1, &index, &offset); | |
| if (!samp) { | |
| fprintf(dump, " SAMPLE #%d IN TRACK #%d NOT THERE !!!\n", j+1, i+1); | |
| continue; | |
| } | |
| gf_isom_get_sample_flags(file, i+1, j+1, &isLeading, &dependsOn, &dependedOn, &redundant); | |
| gf_isom_get_sample_rap_roll_info(file, i+1, j+1, &is_rap, &roll_type, &roll_distance); | |
| dts = samp->DTS; | |
| cts = dts + (s32) samp->CTS_Offset; | |
| fprintf(dump, "Sample %d\tDTS "LLU"\tCTS "LLD"\t%d\t%d", j+1, dts, cts, samp->dataLength, samp->IsRAP); | |
| if (!skip_offset) | |
| fprintf(dump, "\t"LLU, offset); | |
| fprintf(dump, "\t%d\t%d\t%d\t%d\t%d\t%d\t%d", isLeading, dependsOn, dependedOn, redundant, is_rap, roll_type, roll_distance); | |
| if (cts< (s64) dts) { | |
| if (has_cts_offset==2) { | |
| if (cts_dts_shift && (cts+cts_dts_shift < (s64) dts)) { | |
| fprintf(dump, " #NEGATIVE CTS OFFSET!!!"); | |
| has_ctts_error = 1; | |
| } else if (!cts_dts_shift) { | |
| fprintf(dump, " #possible negative CTS offset (no cslg in file)"); | |
| } | |
| } else { | |
| fprintf(dump, " #NEGATIVE CTS OFFSET!!!"); | |
| has_ctts_error = 1; | |
| } | |
| } | |
| if (has_cts_offset && check_ts) { | |
| for (k=0; k<nb_timings; k++) { | |
| if (timings[k].dts==dts) { | |
| fprintf(dump, " #SAME DTS USED!!!"); | |
| has_ctts_error = 1; | |
| } | |
| if (timings[k].cts==cts) { | |
| fprintf(dump, " #SAME CTS USED!!! "); | |
| has_ctts_error = 1; | |
| } | |
| } | |
| timings[nb_timings].dts = dts; | |
| timings[nb_timings].cts = cts; | |
| nb_timings++; | |
| } | |
| gf_isom_sample_del(&samp); | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| if (is_fragmented) { | |
| fprintf(dump, "\t%d", gf_isom_sample_is_fragment_start(file, i+1, j+1, NULL) ); | |
| } | |
| #endif | |
| fprintf(dump, "\n"); | |
| gf_set_progress("Dumping track timing", j+1, count); | |
| } | |
| fprintf(dump, "\n\n"); | |
| gf_set_progress("Dumping track timing", count, count); | |
| } | |
| if (timings) gf_free(timings); | |
| if (inName) gf_fclose(dump); | |
| if (has_ctts_error) { | |
| M4_LOG(GF_LOG_ERROR, ("\tFile has CTTS table errors\n")); | |
| } | |
| } | |
| static u32 read_nal_size_hdr(u8 *ptr, u32 nalh_size) | |
| { | |
| u32 nal_size=0; | |
| u32 v = nalh_size; | |
| while (v) { | |
| nal_size |= (u8) *ptr; | |
| ptr++; | |
| v-=1; | |
| if (v) nal_size <<= 8; | |
| } | |
| return nal_size; | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| void gf_inspect_dump_nalu(FILE *dump, u8 *ptr, u32 ptr_size, Bool is_svc, HEVCState *hevc, AVCState *avc, VVCState *vvc, u32 nalh_size, Bool dump_crc, Bool is_encrypted, u8 *sai_buffer, u32 sai_buffer_size, u32 offset_in_sample); | |
| #endif | |
| static GF_Err dump_isom_nal_ex(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, u32 dump_flags) | |
| { | |
| u32 i, j, count, nb_descs, track, nalh_size, timescale, cur_extract_mode; | |
| GF_Err e=GF_OK; | |
| s32 countRef; | |
| Bool is_adobe_protected; | |
| Bool is_cenc_protected; | |
| Bool is_hevc = GF_FALSE; | |
| Bool is_vvc = GF_FALSE; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| AVCState *avc_state = NULL; | |
| HEVCState *hevc_state = NULL; | |
| VVCState *vvc_state = NULL; | |
| #endif | |
| GF_AVCConfig *avccfg, *svccfg; | |
| GF_HEVCConfig *hevccfg, *lhvccfg; | |
| GF_VVCConfig *vvccfg; | |
| GF_NALUFFParam *slc; | |
| Bool has_svcc = GF_FALSE; | |
| track = gf_isom_get_track_by_id(file, trackID); | |
| count = gf_isom_get_sample_count(file, track); | |
| timescale = gf_isom_get_media_timescale(file, track); | |
| cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track); | |
| nb_descs = gf_isom_get_sample_description_count(file, track); | |
| if (!nb_descs) { | |
| M4_LOG(GF_LOG_ERROR, ("Error: Track #%d has no sample description so is likely not NALU-based!\n", trackID)); | |
| return GF_BAD_PARAM; | |
| } | |
| fprintf(dump, "<NALUTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| #define DUMP_ARRAY(arr, name, loc, _is_svc)\ | |
| if (arr) {\ | |
| fprintf(dump, " <%sArray location=\"%s\">\n", name, loc);\ | |
| for (i=0; i<gf_list_count(arr); i++) {\ | |
| slc = gf_list_get(arr, i);\ | |
| fprintf(dump, " <NALU size=\"%d\" ", slc->size);\ | |
| gf_inspect_dump_nalu(dump, (u8 *) slc->data, slc->size, _is_svc, is_hevc ? hevc_state : NULL, avc_state, is_vvc ? vvc_state : NULL, nalh_size, (dump_flags&1) ? GF_TRUE : GF_FALSE, GF_FALSE, NULL, 0, 0);\ | |
| }\ | |
| fprintf(dump, " </%sArray>\n", name);\ | |
| }\ | |
| #else | |
| #define DUMP_ARRAY(arr, name, loc, _is_svc)\ | |
| if (arr) {\ | |
| fprintf(dump, " <%sArray location=\"%s\">\n", name, loc);\ | |
| for (i=0; i<gf_list_count(arr); i++) {\ | |
| slc = gf_list_get(arr, i);\ | |
| fprintf(dump, " <NALU size=\"%d\" ", slc->size);\ | |
| fprintf(dump, "/>\n");\ | |
| }\ | |
| fprintf(dump, " </%sArray>\n", name);\ | |
| }\ | |
| #endif | |
| nalh_size = 0; | |
| for (j=0; j<nb_descs; j++) { | |
| GF_AVCConfig *mvccfg; | |
| Bool is_svc; | |
| avccfg = gf_isom_avc_config_get(file, track, j+1); | |
| svccfg = gf_isom_svc_config_get(file, track, j+1); | |
| mvccfg = gf_isom_mvc_config_get(file, track, j+1); | |
| hevccfg = gf_isom_hevc_config_get(file, track, j+1); | |
| lhvccfg = gf_isom_lhvc_config_get(file, track, j+1); | |
| vvccfg = gf_isom_vvc_config_get(file, track, j+1); | |
| is_svc = (svccfg!=NULL) ? 1:0; | |
| if (hevccfg || lhvccfg) { | |
| is_hevc = 1; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_SAFEALLOC(hevc_state, HEVCState) | |
| #endif | |
| } else if (vvccfg) { | |
| is_vvc = 1; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_SAFEALLOC(vvc_state, VVCState) | |
| #endif | |
| } else if (avccfg || svccfg || mvccfg) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_SAFEALLOC(avc_state, AVCState) | |
| #endif | |
| } | |
| //for tile tracks the hvcC is stored in the 'tbas' track | |
| if (!hevccfg && gf_isom_get_reference_count(file, track, GF_ISOM_REF_TBAS)) { | |
| u32 tk = 0; | |
| gf_isom_get_reference(file, track, GF_ISOM_REF_TBAS, 1, &tk); | |
| hevccfg = gf_isom_hevc_config_get(file, tk, 1); | |
| } | |
| fprintf(dump, " <NALUConfig>\n"); | |
| if (!avccfg && !svccfg && !hevccfg && !lhvccfg && !vvccfg) { | |
| M4_LOG(GF_LOG_ERROR, ("Error: Track #%d is not NALU or OBU based!\n", trackID)); | |
| return GF_BAD_PARAM; | |
| } | |
| if (avccfg) { | |
| nalh_size = avccfg->nal_unit_size; | |
| DUMP_ARRAY(avccfg->sequenceParameterSets, "AVCSPS", "avcC", is_svc); | |
| DUMP_ARRAY(avccfg->pictureParameterSets, "AVCPPS", "avcC", is_svc) | |
| DUMP_ARRAY(avccfg->sequenceParameterSetExtensions, "AVCSPSEx", "avcC", is_svc) | |
| } | |
| if (is_svc) { | |
| if (!nalh_size) nalh_size = svccfg->nal_unit_size; | |
| DUMP_ARRAY(svccfg->sequenceParameterSets, "SVCSPS", "svcC", is_svc) | |
| DUMP_ARRAY(svccfg->pictureParameterSets, "SVCPPS", "svcC", is_svc) | |
| } | |
| if (mvccfg) { | |
| if (!nalh_size) nalh_size = mvccfg->nal_unit_size; | |
| DUMP_ARRAY(mvccfg->sequenceParameterSets, "SVCSPS", "mvcC", is_svc) | |
| DUMP_ARRAY(mvccfg->pictureParameterSets, "SVCPPS", "mvcC", is_svc) | |
| } | |
| if (hevccfg) { | |
| u32 idx; | |
| nalh_size = hevccfg->nal_unit_size; | |
| for (idx=0; idx<gf_list_count(hevccfg->param_array); idx++) { | |
| GF_NALUFFParamArray *ar = gf_list_get(hevccfg->param_array, idx); | |
| if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCSPS", "hvcC", 0) | |
| } else if (ar->type==GF_HEVC_NALU_PIC_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCPPS", "hvcC", 0) | |
| } else if (ar->type==GF_HEVC_NALU_VID_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCVPS", "hvcC", 0) | |
| } else { | |
| DUMP_ARRAY(ar->nalus, "HEVCUnknownPS", "hvcC", 0) | |
| } | |
| } | |
| } | |
| if (vvccfg) { | |
| u32 idx; | |
| nalh_size = vvccfg->nal_unit_size; | |
| for (idx=0; idx<gf_list_count(vvccfg->param_array); idx++) { | |
| GF_NALUFFParamArray *ar = gf_list_get(vvccfg->param_array, idx); | |
| if (ar->type==GF_VVC_NALU_SEQ_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "VVCSPS", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_PIC_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "VVCPPS", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_VID_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "VVCVPS", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_DEC_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "VVCDecParam", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_OPI) { | |
| DUMP_ARRAY(ar->nalus, "VVCOPI", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_APS_PREFIX) { | |
| DUMP_ARRAY(ar->nalus, "VVCAPSPrefix", "vvcC", 0) | |
| } else if (ar->type==GF_VVC_NALU_SEI_PREFIX) { | |
| DUMP_ARRAY(ar->nalus, "VVCSEIPrefix", "vvcC", 0) | |
| } else { | |
| DUMP_ARRAY(ar->nalus, "VVCUnknownPS", "vvcC", 0) | |
| } | |
| } | |
| } | |
| if (lhvccfg) { | |
| u32 idx; | |
| nalh_size = lhvccfg->nal_unit_size; | |
| for (idx=0; idx<gf_list_count(lhvccfg->param_array); idx++) { | |
| GF_NALUFFParamArray *ar = gf_list_get(lhvccfg->param_array, idx); | |
| if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCSPS", "lhcC", 0) | |
| } else if (ar->type==GF_HEVC_NALU_PIC_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCPPS", "lhcC", 0) | |
| } else if (ar->type==GF_HEVC_NALU_VID_PARAM) { | |
| DUMP_ARRAY(ar->nalus, "HEVCVPS", "lhcC", 0) | |
| } else { | |
| DUMP_ARRAY(ar->nalus, "HEVCUnknownPS", "lhcC", 0) | |
| } | |
| } | |
| } | |
| fprintf(dump, " </NALUConfig>\n"); | |
| if (avccfg) gf_odf_avc_cfg_del(avccfg); | |
| if (svccfg) { | |
| gf_odf_avc_cfg_del(svccfg); | |
| has_svcc = GF_TRUE; | |
| } | |
| if (hevccfg) gf_odf_hevc_cfg_del(hevccfg); | |
| if (vvccfg) gf_odf_vvc_cfg_del(vvccfg); | |
| if (lhvccfg) gf_odf_hevc_cfg_del(lhvccfg); | |
| } | |
| /*fixme: for dumping encrypted track: we don't have neither avccfg nor svccfg*/ | |
| if (!nalh_size) nalh_size = 4; | |
| /*for testing dependency*/ | |
| countRef = gf_isom_get_reference_count(file, track, GF_ISOM_REF_SCAL); | |
| if (countRef > 0) | |
| { | |
| GF_ISOTrackID refTrackID; | |
| fprintf(dump, " <SCALReferences>\n"); | |
| for (i = 1; i <= (u32) countRef; i++) | |
| { | |
| gf_isom_get_reference_ID(file, track, GF_ISOM_REF_SCAL, i, &refTrackID); | |
| fprintf(dump, " <SCALReference number=\"%d\" refTrackID=\"%d\"/>\n", i, refTrackID); | |
| } | |
| fprintf(dump, " </SCALReferences>\n"); | |
| } | |
| u32 sai_buffer_size=0, sai_buffer_alloc=0; | |
| u8 *sai_buffer = NULL; | |
| fprintf(dump, " <NALUSamples>\n"); | |
| gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT); | |
| for (i=0; i<count; i++) { | |
| u64 dts, cts; | |
| Bool is_rap; | |
| u32 size, nal_size, idx, di; | |
| u8 *ptr; | |
| GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, &di); | |
| if (!samp) { | |
| e = gf_isom_last_error(file); | |
| break; | |
| } | |
| is_adobe_protected = gf_isom_is_adobe_protection_media(file, track, di); | |
| is_cenc_protected = gf_isom_is_cenc_media(file, track, di); | |
| dts = samp->DTS; | |
| cts = dts + (s32) samp->CTS_Offset; | |
| is_rap = samp->IsRAP ? GF_TRUE : GF_FALSE; | |
| if (!is_rap) gf_isom_get_sample_rap_roll_info(file, track, i+1, &is_rap, NULL, NULL); | |
| if (dump_flags&2) { | |
| fprintf(dump, " <Sample size=\"%d\" RAP=\"%d\"", samp->dataLength, is_rap); | |
| } else { | |
| fprintf(dump, " <Sample DTS=\""LLD"\" CTS=\""LLD"\" size=\"%d\" RAP=\"%d\"", dts, cts, samp->dataLength, is_rap); | |
| } | |
| if (nb_descs>1) | |
| fprintf(dump, " sample_description=\"%d\"", di); | |
| fprintf(dump, " >\n"); | |
| if (cts<dts) fprintf(dump, "<!-- NEGATIVE CTS OFFSET! -->\n"); | |
| idx = 1; | |
| ptr = samp->data; | |
| size = samp->dataLength; | |
| if (is_adobe_protected) { | |
| u8 encrypted_au = ptr[0]; | |
| if (encrypted_au) { | |
| fprintf(dump, " <!-- Sample number %d is an Adobe's protected sample: can not be dumped -->\n", i+1); | |
| fprintf(dump, " </Sample>\n\n"); | |
| continue; | |
| } | |
| else { | |
| ptr++; | |
| size--; | |
| } | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| u32 sample_offset=0; | |
| Bool is_encrypted = 0; | |
| if (is_cenc_protected) { | |
| e = gf_isom_get_sample_cenc_info(file, track, i + 1, &is_encrypted, NULL, NULL, NULL, NULL); | |
| if (e != GF_OK) { | |
| fprintf(dump, "dump_msg=\"Error %s while fetching encryption info for sample, assuming sample is encrypted\" ", gf_error_to_string(e) ); | |
| is_encrypted = GF_TRUE; | |
| } | |
| sai_buffer_size = sai_buffer_alloc; | |
| e = gf_isom_cenc_get_sample_aux_info(file, track, i+1, di, NULL, &sai_buffer, &sai_buffer_size); | |
| if (sai_buffer_size > sai_buffer_alloc) | |
| sai_buffer_alloc = sai_buffer_size; | |
| } | |
| #endif | |
| while (size) { | |
| if (size<nalh_size) { | |
| fprintf(dump, " <!-- NALU number %d is corrupted: length field is %d but only %d remains -->\n", idx, nalh_size, size); | |
| break; | |
| } | |
| nal_size = read_nal_size_hdr(ptr, nalh_size); | |
| ptr += nalh_size; | |
| if (nal_size >= UINT_MAX-nalh_size || nalh_size + nal_size > size) { | |
| fprintf(dump, " <!-- NALU number %d is corrupted: size is %d but only %d remains -->\n", idx, nal_size, size); | |
| break; | |
| } else { | |
| fprintf(dump, " <NALU size=\"%d\" ", nal_size); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| gf_inspect_dump_nalu(dump, ptr, nal_size, has_svcc ? 1 : 0, hevc_state, avc_state, vvc_state, nalh_size, dump_flags, is_encrypted, sai_buffer, sai_buffer_size, sample_offset); | |
| sample_offset += nal_size + nalh_size; | |
| #else | |
| fprintf(dump, "/>\n"); | |
| #endif | |
| } | |
| idx++; | |
| ptr+=nal_size; | |
| size -= nal_size + nalh_size; | |
| } | |
| fprintf(dump, " </Sample>\n"); | |
| gf_isom_sample_del(&samp); | |
| fprintf(dump, "\n"); | |
| gf_set_progress("Analysing Track NALUs", i+1, count); | |
| } | |
| fprintf(dump, " </NALUSamples>\n"); | |
| fprintf(dump, "</NALUTrack>\n"); | |
| if (sai_buffer) gf_free(sai_buffer); | |
| gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| if (hevc_state) gf_free(hevc_state); | |
| if (vvc_state) gf_free(vvc_state); | |
| if (avc_state) gf_free(avc_state); | |
| #endif | |
| return e; | |
| } | |
| static GF_Err dump_isom_obu(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc); | |
| static GF_Err dump_qt_prores(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc); | |
| static GF_Err dump_isom_opus(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc); | |
| GF_Err dump_isom_nal(GF_ISOFile *file, GF_ISOTrackID trackID, char *inName, Bool is_final_name, u32 dump_flags) | |
| { | |
| char szFileName[GF_MAX_PATH]; | |
| Bool is_av1 = GF_FALSE; | |
| Bool is_prores = GF_FALSE; | |
| Bool is_opus = GF_FALSE; | |
| u32 track = gf_isom_get_track_by_id(file, trackID); | |
| GF_ESD* esd = gf_isom_get_esd(file, track, 1); | |
| if (!esd || !esd->decoderConfig) { | |
| switch (gf_isom_get_media_subtype(file, track, 1)) { | |
| case GF_ISOM_SUBTYPE_AV01: | |
| is_av1 = GF_TRUE; | |
| break; | |
| case GF_QT_SUBTYPE_APCH: | |
| case GF_QT_SUBTYPE_APCO: | |
| case GF_QT_SUBTYPE_APCN: | |
| case GF_QT_SUBTYPE_APCS: | |
| case GF_QT_SUBTYPE_AP4X: | |
| case GF_QT_SUBTYPE_AP4H: | |
| is_prores = GF_TRUE; | |
| break; | |
| case GF_ISOM_SUBTYPE_OPUS: | |
| is_opus = GF_TRUE; | |
| break; | |
| } | |
| } | |
| else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AV1) { | |
| is_av1 = GF_TRUE; | |
| } else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_OPUS) { | |
| is_opus = GF_TRUE; | |
| } | |
| if (esd) gf_odf_desc_del((GF_Descriptor*)esd); | |
| FILE *dump; | |
| if (inName) { | |
| if (is_final_name) { | |
| gf_strcpy(szFileName, inName); | |
| } else { | |
| snprintf(szFileName, sizeof(szFileName), "%s_%d_%s.xml", inName, trackID, is_av1 ? "obu" : "nalu"); | |
| } | |
| dump = gf_fopen(szFileName, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szFileName)); | |
| return GF_URL_ERROR; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| GF_Err e = GF_OK; | |
| if (is_av1) | |
| e = dump_isom_obu(file, trackID, dump, dump_flags); | |
| else if (is_prores) | |
| e = dump_qt_prores(file, trackID, dump, dump_flags); | |
| else if (is_opus) | |
| e = dump_isom_opus(file, trackID, dump, dump_flags); | |
| else | |
| e = dump_isom_nal_ex(file, trackID, dump, dump_flags); | |
| if (inName) gf_fclose(dump); | |
| if (!e && dump_check_xml) { | |
| if (inName) { | |
| GF_DOMParser *xml_parser = gf_xml_dom_new(); | |
| e = gf_xml_dom_parse(xml_parser, szFileName, NULL, NULL); | |
| if (e) { | |
| fprintf(stderr, "Failed to parse XML dump %s: line %d: %s\n", szFileName, gf_xml_dom_get_line(xml_parser), gf_xml_dom_get_error(xml_parser) ); | |
| } | |
| gf_xml_dom_del(xml_parser); | |
| } else { | |
| fprintf(stderr, "Cannot check XML for dump on stdout\n"); | |
| } | |
| } | |
| return e; | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| void gf_inspect_dump_opus(FILE *dump, u8 *ptr, u64 size, u32 channel_count, Bool dump_crc); | |
| #endif | |
| static GF_Err dump_isom_opus(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc) | |
| { | |
| GF_Err e = GF_OK; | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| u32 i, count, track, timescale, channel_count; | |
| GF_OpusConfig opcfg; | |
| track = gf_isom_get_track_by_id(file, trackID); | |
| e = gf_isom_opus_config_get_desc(file, track, 1, &opcfg); | |
| if (e != GF_OK) { | |
| M4_LOG(GF_LOG_ERROR, ("Error: Track #%d is not Opus!\n", trackID)); | |
| return GF_ISOM_INVALID_FILE; | |
| } | |
| count = gf_isom_get_sample_count(file, track); | |
| timescale = gf_isom_get_media_timescale(file, track); | |
| fprintf(dump, "<OpusTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale); | |
| fprintf(dump, " <OpusConfig version=\"%d\" OutputChannelCount=\"%d\" PreSkip=\"%d\" InputSampleRate=\"%d\" OutputGain=\"%d\" ChannelMappingFamily=\"%d\"", | |
| opcfg.version, opcfg.OutputChannelCount, opcfg.PreSkip, opcfg.InputSampleRate, opcfg.OutputGain, opcfg.ChannelMappingFamily); | |
| if (opcfg.ChannelMappingFamily) { | |
| fprintf(dump, " StreamCount=\"%d\" CoupledStreamCount=\"%d\" channelMapping=\"", opcfg.StreamCount, opcfg.CoupledCount); | |
| for (i=0; i<opcfg.OutputChannelCount; i++) { | |
| fprintf(dump, "%s%d", i ? " " : "", opcfg.ChannelMapping[i]); | |
| } | |
| fprintf(dump, "\""); | |
| } | |
| fprintf(dump, "/>\n"); | |
| channel_count = (opcfg.StreamCount ? opcfg.StreamCount : 1); | |
| fprintf(dump, " <OpusSamples>\n"); | |
| e = GF_OK; | |
| for (i=0; i<count; i++) { | |
| u64 dts, cts; | |
| GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, NULL); | |
| if (!samp) { | |
| e = gf_isom_last_error(file); | |
| break; | |
| } | |
| dts = samp->DTS; | |
| cts = dts + (s32) samp->CTS_Offset; | |
| fprintf(dump, " <Sample number=\"%d\" DTS=\""LLD"\" CTS=\""LLD"\" size=\"%d\" RAP=\"%d\" >\n", i+1, dts, cts, samp->dataLength, samp->IsRAP); | |
| if (cts<dts) fprintf(dump, "<!-- NEGATIVE CTS OFFSET! -->\n"); | |
| gf_inspect_dump_opus(dump, samp->data, samp->dataLength, channel_count, dump_crc); | |
| fprintf(dump, " </Sample>\n"); | |
| gf_isom_sample_del(&samp); | |
| fprintf(dump, "\n"); | |
| if (e<GF_OK) break; | |
| gf_set_progress("Analysing Track Opus Sample", i+1, count); | |
| } | |
| fprintf(dump, " </OpusSamples>\n"); | |
| fprintf(dump, "</OpusTrack>\n"); | |
| #endif | |
| return e; | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| void gf_inspect_dump_obu(FILE *dump, AV1State *av1, u8 *obu, u64 obu_length, ObuType obu_type, u64 obu_size, u32 hdr_size, Bool dump_crc, u8 *sai_buffer, u32 sai_buffer_size, u32 offset_in_sample); | |
| #endif | |
| static GF_Err dump_isom_obu(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc) | |
| { | |
| GF_Err e = GF_OK; | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| u32 i, count, track, timescale; | |
| AV1State *av1_state; | |
| ObuType obu_type = 0; | |
| u64 obu_size = 0; | |
| u32 hdr_size = 0; | |
| GF_BitStream *bs; | |
| u32 idx; | |
| track = gf_isom_get_track_by_id(file, trackID); | |
| GF_SAFEALLOC(av1_state, AV1State); | |
| if (!av1_state) return GF_OUT_OF_MEM; | |
| gf_av1_init_state(av1_state); | |
| av1_state->config = gf_isom_av1_config_get(file, track, 1); | |
| if (!av1_state->config) { | |
| M4_LOG(GF_LOG_ERROR, ("Error: Track #%d is not AV1!\n", trackID)); | |
| gf_free(av1_state); | |
| return GF_ISOM_INVALID_FILE; | |
| } | |
| count = gf_isom_get_sample_count(file, track); | |
| timescale = gf_isom_get_media_timescale(file, track); | |
| fprintf(dump, "<OBUTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale); | |
| fprintf(dump, " <OBUConfig>\n"); | |
| for (i=0; i<gf_list_count(av1_state->config->obu_array); i++) { | |
| GF_AV1_OBUArrayEntry *obu = gf_list_get(av1_state->config->obu_array, i); | |
| bs = gf_bs_new(obu->obu, (u32) obu->obu_length, GF_BITSTREAM_READ); | |
| e = gf_av1_parse_obu(bs, &obu_type, &obu_size, &hdr_size, av1_state); | |
| if (e<GF_OK) { | |
| if (av1_state->config) gf_odf_av1_cfg_del(av1_state->config); | |
| gf_av1_reset_state(av1_state, GF_TRUE); | |
| gf_free(av1_state); | |
| return e; | |
| } | |
| gf_inspect_dump_obu(dump, av1_state, obu->obu, obu->obu_length, obu_type, obu_size, hdr_size, dump_crc, NULL, 0, 0); | |
| gf_bs_del(bs); | |
| } | |
| fprintf(dump, " </OBUConfig>\n"); | |
| fprintf(dump, " <OBUSamples>\n"); | |
| u8 *sai_buffer = NULL; | |
| u32 sai_buffer_size=0, sai_buffer_alloc=0; | |
| e = GF_OK; | |
| for (i=0; i<count; i++) { | |
| u64 dts, cts; | |
| u32 size, di; | |
| u8 *ptr; | |
| GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, &di); | |
| if (!samp) { | |
| e = gf_isom_last_error(file); | |
| break; | |
| } | |
| dts = samp->DTS; | |
| cts = dts + (s32) samp->CTS_Offset; | |
| fprintf(dump, " <Sample number=\"%d\" DTS=\""LLD"\" CTS=\""LLD"\" size=\"%d\" RAP=\"%d\" >\n", i+1, dts, cts, samp->dataLength, samp->IsRAP); | |
| if (cts<dts) fprintf(dump, "<!-- NEGATIVE CTS OFFSET! -->\n"); | |
| u32 sample_offset=0; | |
| Bool is_encrypted = 0; | |
| if (gf_isom_is_cenc_media(file, track, di)) { | |
| e = gf_isom_get_sample_cenc_info(file, track, i + 1, &is_encrypted, NULL, NULL, NULL, NULL); | |
| if (e != GF_OK) { | |
| fprintf(dump, "dump_msg=\"Error %s while fetching encryption info for sample, assuming sample is encrypted\" ", gf_error_to_string(e) ); | |
| is_encrypted = GF_TRUE; | |
| } | |
| sai_buffer_size = sai_buffer_alloc; | |
| e = gf_isom_cenc_get_sample_aux_info(file, track, i+1, di, NULL, &sai_buffer, &sai_buffer_size); | |
| if (sai_buffer_size > sai_buffer_alloc) | |
| sai_buffer_alloc = sai_buffer_size; | |
| } | |
| idx = 1; | |
| ptr = samp->data; | |
| size = samp->dataLength; | |
| bs = gf_bs_new(ptr, size, GF_BITSTREAM_READ); | |
| while (size) { | |
| e = gf_av1_parse_obu(bs, &obu_type, &obu_size, &hdr_size, av1_state); | |
| if (e<GF_OK) break; | |
| if (obu_size > size) { | |
| fprintf(dump, " <!-- OBU number %d is corrupted: size is %d but only %d remains -->\n", idx, (u32) obu_size, size); | |
| break; | |
| } | |
| gf_inspect_dump_obu(dump, av1_state, ptr, obu_size, obu_type, obu_size, hdr_size, dump_crc, sai_buffer, sai_buffer_size, sample_offset); | |
| ptr += obu_size; | |
| size -= (u32)obu_size; | |
| idx++; | |
| sample_offset+=obu_size; | |
| } | |
| gf_bs_del(bs); | |
| fprintf(dump, " </Sample>\n"); | |
| gf_isom_sample_del(&samp); | |
| fprintf(dump, "\n"); | |
| if (e<GF_OK) break; | |
| gf_set_progress("Analysing Track OBUs", i+1, count); | |
| } | |
| fprintf(dump, " </OBUSamples>\n"); | |
| fprintf(dump, "</OBUTrack>\n"); | |
| if (sai_buffer) gf_free(sai_buffer); | |
| if (av1_state->config) gf_odf_av1_cfg_del(av1_state->config); | |
| gf_av1_reset_state(av1_state, GF_TRUE); | |
| gf_free(av1_state); | |
| #endif | |
| return e; | |
| } | |
| static GF_Err dump_qt_prores(GF_ISOFile *file, u32 trackID, FILE *dump, Bool dump_crc) | |
| { | |
| GF_Err e = GF_OK; | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_INSPECT) | |
| u32 i, count, track, timescale; | |
| track = gf_isom_get_track_by_id(file, trackID); | |
| count = gf_isom_get_sample_count(file, track); | |
| timescale = gf_isom_get_media_timescale(file, track); | |
| fprintf(dump, "<ProResTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale); | |
| for (i=0; i<count; i++) { | |
| void gf_inspect_dump_prores(FILE *dump, u8 *ptr, u64 frame_size, Bool dump_crc); | |
| u64 dts, cts; | |
| GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, NULL); | |
| if (!samp) { | |
| e = gf_isom_last_error(file); | |
| break; | |
| } | |
| dts = samp->DTS; | |
| cts = dts + (s32) samp->CTS_Offset; | |
| if (cts!=dts) fprintf(dump, "<!-- Wrong timing info (CTS "LLD" vs DTS "LLD") ! -->\n", cts, dts); | |
| if (!samp->IsRAP) fprintf(dump, "<!-- Wrong sync sample info, sample is not SAP1 ! -->\n"); | |
| fprintf(dump, " <Sample number=\"%d\" CTS=\""LLD"\" size=\"%d\">\n", i+1, cts, samp->dataLength); | |
| gf_inspect_dump_prores(dump, samp->data, samp->dataLength, dump_crc); | |
| fprintf(dump, " </Sample>\n"); | |
| gf_isom_sample_del(&samp); | |
| fprintf(dump, "\n"); | |
| gf_set_progress("Analysing ProRes Track", i+1, count); | |
| } | |
| fprintf(dump, "</ProResTrack>\n"); | |
| #endif | |
| return e; | |
| } | |
| void dump_isom_saps(GF_ISOFile *file, GF_ISOTrackID trackID, u32 dump_saps_mode, char *inName, Bool is_final_name) | |
| { | |
| FILE *dump; | |
| u32 i, count; | |
| s64 media_offset=0; | |
| u32 track = gf_isom_get_track_by_id(file, trackID); | |
| if (inName) { | |
| char szBuf[GF_MAX_PATH]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) snprintf(szBuf, sizeof(szBuf), "%s_%d_cues.xml", inName, trackID); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | |
| fprintf(dump, "<DASHCues xmlns=\"urn:gpac:dash:schema:cues:2018\">\n"); | |
| fprintf(dump, "<Stream id=\"%d\" timescale=\"%d\"", trackID, gf_isom_get_media_timescale(file, track) ); | |
| if (dump_saps_mode==4) { | |
| fprintf(dump, " mode=\"edit\""); | |
| gf_isom_get_edit_list_type(file, track, &media_offset); | |
| } | |
| fprintf(dump, ">\n"); | |
| count = gf_isom_get_sample_count(file, track); | |
| for (i=0; i<count; i++) { | |
| s64 cts, dts; | |
| u32 di; | |
| Bool traf_start = 0; | |
| u32 sap_type = 0; | |
| u64 doffset; | |
| GF_ISOSample *samp = gf_isom_get_sample_info(file, track, i+1, &di, &doffset); | |
| if (!samp) | |
| continue; | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| traf_start = gf_isom_sample_is_fragment_start(file, track, i+1, NULL); | |
| #endif | |
| sap_type = samp->IsRAP; | |
| if (!sap_type) { | |
| Bool is_rap; | |
| GF_ISOSampleRollType roll_type; | |
| s32 roll_dist; | |
| gf_isom_get_sample_rap_roll_info(file, track, i+1, &is_rap, &roll_type, &roll_dist); | |
| if (roll_type) sap_type = SAP_TYPE_4; | |
| else if (is_rap) sap_type = SAP_TYPE_3; | |
| } | |
| if (!sap_type) { | |
| gf_isom_sample_del(&samp); | |
| continue; | |
| } | |
| dts = cts = samp->DTS; | |
| cts += samp->CTS_Offset; | |
| fprintf(dump, "<Cue sap=\"%d\"", sap_type); | |
| if (dump_saps_mode==4) { | |
| cts += media_offset; | |
| fprintf(dump, " cts=\""LLD"\"", cts); | |
| } else { | |
| if (!dump_saps_mode || (dump_saps_mode==1)) fprintf(dump, " sample=\"%d\"", i+1); | |
| if (!dump_saps_mode || (dump_saps_mode==2)) fprintf(dump, " cts=\""LLD"\"", cts); | |
| if (!dump_saps_mode || (dump_saps_mode==3)) fprintf(dump, " dts=\""LLD"\"", dts); | |
| } | |
| if (traf_start) | |
| fprintf(dump, " wasFragStart=\"yes\""); | |
| fprintf(dump, "/>\n"); | |
| gf_isom_sample_del(&samp); | |
| } | |
| fprintf(dump, "</Stream>\n"); | |
| fprintf(dump, "</DASHCues>\n"); | |
| if (inName) gf_fclose(dump); | |
| } | |
| typedef struct | |
| { | |
| u32 track_num; | |
| u32 chunk_num; | |
| u32 first_sample_num; | |
| u32 timescale; | |
| u32 sample_per_chunk; | |
| u64 chunk_offset; | |
| u64 size; | |
| } ChunkInfo; | |
| static s32 sort_chunk(const void* e1, const void* e2) | |
| { | |
| const ChunkInfo *p1 = e1; | |
| const ChunkInfo *p2 = e2; | |
| if (p1->chunk_offset < p2->chunk_offset) return -1; | |
| if (p1->chunk_offset > p2->chunk_offset) return 1; | |
| return 0; | |
| } | |
| void dump_isom_chunks(GF_ISOFile *file, char *inName, Bool is_final_name) | |
| { | |
| u32 i, count, nb_chunks_total, cur; | |
| FILE *dump; | |
| Bool dump_stsd = 0; | |
| u32 dump_hm = 0; | |
| u64 prev_time = 0; | |
| u64 unused_space = 0; | |
| u64 prev_chunk_end = 0; | |
| Bool do_prog=GF_TRUE; | |
| ChunkInfo *all_chunks; | |
| if (inName) { | |
| char szBuf[1024]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) gf_strcat(szBuf, "_chunks.txt"); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| do_prog = GF_FALSE; | |
| } | |
| count = gf_isom_get_track_count(file); | |
| nb_chunks_total = 0; | |
| for (i=0; i<count; i++) { | |
| nb_chunks_total += gf_isom_get_chunk_count(file, i+1); | |
| } | |
| all_chunks = gf_malloc(sizeof(ChunkInfo) * nb_chunks_total); | |
| memset(all_chunks, 0, sizeof(ChunkInfo) * nb_chunks_total); | |
| cur = 0; | |
| for (i=0; i<count; i++) { | |
| u32 csize = 0; | |
| u32 c1, c2; | |
| u32 j, nb_chunks = gf_isom_get_chunk_count(file, i+1); | |
| u32 ts = gf_isom_get_media_timescale(file, i+1); | |
| u64 dur = gf_isom_get_media_duration(file, i+1); | |
| if (dur > 3600 * ts) dump_hm = 2; | |
| else if (dur > 60 * ts) dump_hm = 1; | |
| if (nb_chunks>1) { | |
| csize = gf_isom_get_constant_sample_size(file, i+1); | |
| if (csize) { | |
| gf_isom_enable_raw_pack(file, i+1, 1024); | |
| csize = gf_isom_get_constant_sample_size(file, i+1); | |
| } | |
| } | |
| c1 = c2 = 0; | |
| for (j=0; j<nb_chunks; j++) { | |
| ChunkInfo *ci; | |
| u32 k; | |
| u32 sample_num, spc, di; | |
| u64 offset; | |
| gf_isom_get_chunk_info(file, i+1,j+1, &offset, &sample_num, &spc, &di, &c1, &c2); | |
| ci = &all_chunks[cur]; | |
| ci->track_num = i+1; | |
| ci->chunk_num = j+1; | |
| ci->chunk_offset = offset; | |
| ci->first_sample_num = sample_num; | |
| ci->timescale = ts; | |
| ci->sample_per_chunk = spc; | |
| if (csize) { | |
| ci->size += csize * spc; | |
| } else { | |
| for (k=0; k<spc; k++) { | |
| ci->size += gf_isom_get_sample_size(file, i+1, sample_num+k); | |
| } | |
| } | |
| if (di>1) dump_stsd=1; | |
| if (do_prog) | |
| gf_set_progress("Analysing chunks", cur+1, nb_chunks_total); | |
| cur++; | |
| } | |
| } | |
| qsort(all_chunks, nb_chunks_total, sizeof(ChunkInfo), sort_chunk); | |
| fprintf(stderr, "Dumping chunk info - diff_prev_ms is the diff in ms between start time of current and prev chunk\n"); | |
| if (dump_stsd) | |
| fprintf(dump, "ChunkNum\tOffset\tTkNum\tChkNbInTk\tChkSize\tSamp/Chunk\tFirstSample\tDescIdx\tSAP\tTime\tCTS\tdiff_prev_ms\n"); | |
| else | |
| fprintf(dump, "ChunkNum\tOffset\tTkNum\tChkNbInTk\tChkSize\tSamp/Chunk\tFirstSample\tSAP\tTime\tCTS\tdiff_prev_ms\n"); | |
| for (i=0; i<nb_chunks_total; i++) { | |
| ChunkInfo *ci = &all_chunks[i]; | |
| u32 di; | |
| GF_ISOSample *samp = gf_isom_get_sample_info(file, ci->track_num, ci->first_sample_num, &di, NULL); | |
| if (samp) { | |
| u32 h, m, s, ms, secs; | |
| u64 time = samp->DTS+samp->CTS_Offset; | |
| s64 diff; | |
| time *= 1000; | |
| time /= ci->timescale; | |
| secs = (u32) (time/1000); | |
| h = secs / 3600; | |
| m = (secs - 3600 * h) / 60; | |
| s = (secs - 3600 * h - 60 * m); | |
| ms = (u32) (time - secs*1000); | |
| diff = time; | |
| diff -= prev_time; | |
| prev_time = time; | |
| fprintf(dump, "%d\t"LLU"\t%d\t%d\t"LLU"\t%d\t%d\t", i+1, ci->chunk_offset, ci->track_num, ci->chunk_num, ci->size, ci->sample_per_chunk, ci->first_sample_num); | |
| if (dump_stsd) fprintf(dump, "%d\t", di); | |
| fprintf(dump, "%d\t", samp->IsRAP); | |
| if (dump_hm==2) | |
| fprintf(dump, "%02d:", h); | |
| if (dump_hm>=1) | |
| fprintf(dump, "%02d:", m); | |
| fprintf(dump, "%02d.%03d\t"LLD"\t"LLD"\n", s, ms, samp->DTS+samp->CTS_Offset, diff); | |
| gf_isom_sample_del(&samp); | |
| } else { | |
| fprintf(dump, "%d\t"LLU"\t%d\t%d\t%d\tN/A\tN/A\tN/A\tN/A\n", i+1, ci->chunk_offset, ci->track_num, ci->chunk_num, ci->first_sample_num); | |
| } | |
| //we assume single mdat ... | |
| if (!prev_chunk_end) { | |
| prev_chunk_end = gf_isom_get_first_mdat_start(file); | |
| } | |
| if (ci->chunk_offset > prev_chunk_end) | |
| unused_space += ci->chunk_offset - prev_chunk_end; | |
| prev_chunk_end = ci->chunk_offset + ci->size; | |
| if (do_prog) | |
| gf_set_progress("Dumping chunks", i+1, nb_chunks_total); | |
| } | |
| gf_free(all_chunks); | |
| if (inName) | |
| gf_fclose(dump); | |
| prev_chunk_end = gf_isom_get_unused_box_bytes(file); | |
| if (prev_chunk_end) | |
| fprintf(stderr, "Unused bytes in box structure: "LLU"\n", prev_chunk_end); | |
| if (unused_space) | |
| fprintf(stderr, "Unused bytes in mdat: "LLU"\n", unused_space); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_DUMP | |
| void dump_isom_ismacryp(GF_ISOFile *file, char *inName, Bool is_final_name) | |
| { | |
| u32 i, j; | |
| FILE *dump; | |
| if (inName) { | |
| char szBuf[1024]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) gf_strcat(szBuf, "_ismacryp.xml"); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); | |
| fprintf(dump, "<!-- MP4Box ISMACryp trace -->\n"); | |
| fprintf(dump, "<ISMACrypFile>\n"); | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| if (gf_isom_get_media_subtype(file, i+1, 1) != GF_ISOM_SUBTYPE_MPEG4_CRYP) continue; | |
| gf_isom_dump_ismacryp_protection(file, i+1, dump); | |
| fprintf(dump, "<ISMACrypTrack trackID=\"%d\">\n", gf_isom_get_track_id(file, i+1)); | |
| for (j=0; j<gf_isom_get_sample_count(file, i+1); j++) { | |
| gf_isom_dump_ismacryp_sample(file, i+1, j+1, dump); | |
| } | |
| fprintf(dump, "</ISMACrypTrack >\n"); | |
| } | |
| fprintf(dump, "</ISMACrypFile>\n"); | |
| if (inName) gf_fclose(dump); | |
| } | |
| void dump_isom_timed_text(GF_ISOFile *file, GF_ISOTrackID trackID, char *inName, Bool is_final_name, Bool is_convert, GF_TextDumpType dump_type) | |
| { | |
| FILE *dump; | |
| GF_Err e; | |
| u32 track; | |
| track = gf_isom_get_track_by_id(file, trackID); | |
| if (!track) { | |
| M4_LOG(GF_LOG_ERROR, ("Cannot find track ID %d\n", trackID)); | |
| return; | |
| } | |
| switch (gf_isom_get_media_type(file, track)) { | |
| case GF_ISOM_MEDIA_TEXT: | |
| case GF_ISOM_MEDIA_SUBT: | |
| break; | |
| default: | |
| M4_LOG(GF_LOG_ERROR, ("Track ID %d is not a 3GPP text track\n", trackID)); | |
| return; | |
| } | |
| if (inName) { | |
| char szBuf[1024]; | |
| char *ext; | |
| ext = ((dump_type==GF_TEXTDUMPTYPE_SVG) ? "svg" : ((dump_type==GF_TEXTDUMPTYPE_SRT) ? "srt" : "ttxt")); | |
| if (is_final_name) { | |
| gf_strcpy(szBuf, inName) ; | |
| } else if (is_convert) | |
| snprintf(szBuf, sizeof(szBuf), "%s.%s", inName, ext) ; | |
| else | |
| snprintf(szBuf, sizeof(szBuf), "%s_%d_text.%s", inName, trackID, ext); | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| } | |
| e = gf_isom_text_dump(file, track, dump, dump_type); | |
| if (inName) gf_fclose(dump); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("Conversion failed (%s)\n", gf_error_to_string(e))); | |
| } else { | |
| fprintf(stderr, "Conversion done\n"); | |
| } | |
| } | |
| #endif /*GPAC_DISABLE_ISOM_DUMP*/ | |
| #ifndef GPAC_DISABLE_ISOM_HINTING | |
| void dump_isom_sdp(GF_ISOFile *file, char *inName, Bool is_final_name) | |
| { | |
| const char *sdp; | |
| u32 size, i; | |
| FILE *dump; | |
| if (inName) { | |
| char szBuf[1024]; | |
| gf_strcpy(szBuf, inName); | |
| if (!is_final_name) { | |
| char *ext = strchr(szBuf, '.'); | |
| if (ext) ext[0] = 0; | |
| gf_strcat(szBuf, "_sdp.txt"); | |
| } | |
| dump = gf_fopen(szBuf, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf)); | |
| return; | |
| } | |
| } else { | |
| dump = stdout; | |
| fprintf(dump, "# File SDP content \n\n"); | |
| } | |
| //get the movie SDP | |
| gf_isom_sdp_get(file, &sdp, &size); | |
| if (sdp && size) | |
| fprintf(dump, "%s", sdp); | |
| fprintf(dump, "\r\n"); | |
| //then tracks | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| if (gf_isom_get_media_type(file, i+1) != GF_ISOM_MEDIA_HINT) continue; | |
| gf_isom_sdp_track_get(file, i+1, &sdp, &size); | |
| if (sdp && size) | |
| fprintf(dump, "%s", sdp); | |
| } | |
| fprintf(dump, "\n\n"); | |
| if (inName) gf_fclose(dump); | |
| } | |
| #endif | |
| #ifndef GPAC_DISABLE_ISOM_DUMP | |
| GF_Err dump_isom_xml(GF_ISOFile *file, char *inName, Bool is_final_name, Bool do_track_dump, Bool merge_vtt_cues, const char *init_seg, Bool skip_samples) | |
| { | |
| char szFileName[1024]; | |
| GF_Err e; | |
| FILE *dump = stdout; | |
| Bool do_close = GF_FALSE; | |
| Bool skip_init = init_seg ? GF_TRUE : GF_FALSE; | |
| if (!file) return GF_ISOM_INVALID_FILE; | |
| if (inName) { | |
| gf_strcpy(szFileName, inName); | |
| if (!is_final_name) { | |
| gf_strcat(szFileName, do_track_dump ? "_dump.xml" : "_info.xml"); | |
| } | |
| dump = gf_fopen(szFileName, "wt"); | |
| if (!dump) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szFileName)); | |
| return GF_IO_ERR; | |
| } | |
| do_close=GF_TRUE; | |
| } | |
| fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | |
| if (do_track_dump) { | |
| fprintf(dump, "<ISOBaseMediaFileTrace>\n"); | |
| } | |
| e = gf_isom_dump(file, dump, skip_init, skip_samples); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("Error dumping ISO structure\n")); | |
| } | |
| if (do_track_dump) { | |
| #ifndef GPAC_DISABLE_MEDIA_EXPORT | |
| u32 i; | |
| //because of dump mode we need to reopen in regular read mode to avoid mem leaks | |
| GF_ISOFile *the_file = gf_isom_open(gf_isom_get_filename(file), GF_ISOM_OPEN_READ, NULL); | |
| if (skip_init) { | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| gf_isom_open_segment(the_file, init_seg, 0, 0, 0); | |
| #endif | |
| } | |
| u32 tcount = gf_isom_get_track_count(the_file); | |
| fprintf(dump, "<Tracks>\n"); | |
| for (i=0; i<tcount; i++) { | |
| GF_MediaExporter dumper; | |
| GF_ISOTrackID trackID = gf_isom_get_track_id(the_file, i+1); | |
| u32 mtype = gf_isom_get_media_type(the_file, i+1); | |
| u32 msubtype = gf_isom_get_media_subtype(the_file, i+1, 1); | |
| Bool fmt_handled = GF_FALSE; | |
| memset(&dumper, 0, sizeof(GF_MediaExporter)); | |
| dumper.file = the_file; | |
| dumper.trackID = trackID; | |
| dumper.dump_file = dump; | |
| if (msubtype == GF_ISOM_SUBTYPE_MPEG4_CRYP) | |
| gf_isom_get_original_format_type(the_file, i+1, 1, &msubtype); | |
| e = GF_OK; | |
| if (mtype == GF_ISOM_MEDIA_HINT) { | |
| #ifndef GPAC_DISABLE_ISOM_HINTING | |
| char *name=NULL; | |
| if (msubtype==GF_ISOM_SUBTYPE_RTP) name = "RTPHintTrack"; | |
| else if (msubtype==GF_ISOM_SUBTYPE_SRTP) name = "SRTPHintTrack"; | |
| else if (msubtype==GF_ISOM_SUBTYPE_RRTP) name = "RTPReceptionHintTrack"; | |
| else if (msubtype==GF_ISOM_SUBTYPE_RTCP) name = "RTCPReceptionHintTrack"; | |
| else if (msubtype==GF_ISOM_SUBTYPE_FLUTE) name = "FLUTEReceptionHintTrack"; | |
| else name = "UnknownHintTrack"; | |
| fprintf(dump, "<%s trackID=\"%d\">\n", name, trackID); | |
| #ifndef GPAC_DISABLE_ISOM_HINTING | |
| u32 j, scount=gf_isom_get_sample_count(the_file, i+1); | |
| for (j=0; j<scount; j++) { | |
| gf_isom_dump_hint_sample(the_file, i+1, j+1, dump); | |
| } | |
| #endif | |
| fprintf(dump, "</%s>\n", name); | |
| fmt_handled = GF_TRUE; | |
| #endif /*GPAC_DISABLE_ISOM_HINTING*/ | |
| } | |
| else if (gf_isom_get_avc_svc_type(the_file, i+1, 1) | |
| || gf_isom_get_hevc_lhvc_type(the_file, i+1, 1) | |
| || gf_isom_get_vvc_type(the_file, i+1, 1) | |
| ) { | |
| e = dump_isom_nal_ex(the_file, trackID, dump, GF_FALSE); | |
| fmt_handled = GF_TRUE; | |
| } | |
| else if (msubtype == GF_ISOM_SUBTYPE_AV01) { | |
| e = dump_isom_obu(the_file, trackID, dump, GF_FALSE); | |
| fmt_handled = GF_TRUE; | |
| } | |
| else if ((msubtype == GF_QT_SUBTYPE_APCH) || (msubtype == GF_QT_SUBTYPE_APCO) | |
| || (msubtype == GF_QT_SUBTYPE_APCN) || (msubtype == GF_QT_SUBTYPE_APCS) | |
| || (msubtype == GF_QT_SUBTYPE_AP4X) || (msubtype == GF_QT_SUBTYPE_AP4H) | |
| ) { | |
| e = dump_qt_prores(the_file, trackID, dump, GF_FALSE); | |
| fmt_handled = GF_TRUE; | |
| } | |
| else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT) ) { | |
| if (msubtype==GF_ISOM_SUBTYPE_WVTT) { | |
| #ifndef GPAC_DISABLE_VTT | |
| e = gf_webvtt_dump_iso_track(&dumper, i+1, merge_vtt_cues, GF_TRUE); | |
| fmt_handled = GF_TRUE; | |
| #endif | |
| } else if ((msubtype==GF_ISOM_SUBTYPE_TX3G) || (msubtype==GF_ISOM_SUBTYPE_TEXT)) { | |
| e = gf_isom_text_dump(the_file, i+1, dump, GF_TEXTDUMPTYPE_TTXT_BOXES); | |
| fmt_handled = GF_TRUE; | |
| } | |
| } | |
| else if (msubtype == GF_ISOM_SUBTYPE_OPUS) { | |
| e = dump_isom_opus(the_file, trackID, dump, GF_FALSE); | |
| fmt_handled = GF_TRUE; | |
| } | |
| if (!fmt_handled) { | |
| dumper.flags = GF_EXPORT_NHML | GF_EXPORT_NHML_FULL; | |
| dumper.print_stats_graph = fs_dump_flags; | |
| e = gf_media_export(&dumper); | |
| } | |
| if (e) break; | |
| } | |
| gf_isom_delete(the_file); | |
| #endif /*GPAC_DISABLE_MEDIA_EXPORT*/ | |
| fprintf(dump, "</Tracks>\n"); | |
| fprintf(dump, "</ISOBaseMediaFileTrace>\n"); | |
| } | |
| if (do_close) gf_fclose(dump); | |
| if (!e && dump_check_xml) { | |
| if (do_close) { | |
| GF_DOMParser *xml_parser = gf_xml_dom_new(); | |
| e = gf_xml_dom_parse(xml_parser, szFileName, NULL, NULL); | |
| if (e) { | |
| fprintf(stderr, "Failed to parse XML dump %s: line %d: %s\n", szFileName, gf_xml_dom_get_line(xml_parser), gf_xml_dom_get_error(xml_parser) ); | |
| } | |
| gf_xml_dom_del(xml_parser); | |
| } else { | |
| fprintf(stderr, "Cannot check XML for dump on stdout\n"); | |
| } | |
| } | |
| return e; | |
| } | |
| #endif | |
| static const char *format_duration(u64 dur, u32 timescale, char szDur[100]) | |
| { | |
| if ((dur==(u64) -1) || (dur==(u32) -1)) { | |
| gf_strlcpy(szDur, "Unknown", 100); | |
| return szDur; | |
| } | |
| return gf_format_duration(dur, timescale, szDur); | |
| } | |
| static char *format_date(u64 time, char szTime[100]) | |
| { | |
| time_t now; | |
| if (!time) { | |
| gf_strlcpy(szTime, "UNKNOWN DATE", 100); | |
| } else { | |
| time -= 2082844800; | |
| now = (u32) time; | |
| sprintf(szTime, "GMT %s", asctime(gf_gmtime(&now)) ); | |
| } | |
| return szTime; | |
| } | |
| void print_udta(GF_ISOFile *file, u32 track_number, Bool has_meta_tags) | |
| { | |
| u32 i, count; | |
| count = gf_isom_get_udta_count(file, track_number); | |
| if (!count) return; | |
| if (has_meta_tags) { | |
| for (i=0; i<count; i++) { | |
| u32 type; | |
| bin128 uuid; | |
| gf_isom_get_udta_type(file, track_number, i+1, &type, &uuid); | |
| if ((type == GF_ISOM_BOX_TYPE_META) | |
| || (type==GF_ISOM_UDTA_GPAC_SRD) | |
| || (type==GF_ISOM_BOX_TYPE_KIND) | |
| ) { | |
| count--; | |
| } | |
| } | |
| if (!count) return; | |
| } | |
| fprintf(stderr, "%d UDTA types: ", count); | |
| for (i=0; i<count; i++) { | |
| u32 j, type, nb_items; | |
| bin128 uuid; | |
| gf_isom_get_udta_type(file, track_number, i+1, &type, &uuid); | |
| if (type==GF_ISOM_UDTA_GPAC_SRD) continue; | |
| nb_items = gf_isom_get_user_data_count(file, track_number, type, uuid); | |
| if (!nb_items) continue; | |
| s32 itag = gf_itags_find_by_itag(type); | |
| fprintf(stderr, "\n\t%s: ", (itag<0) ? gf_4cc_to_str(type) : gf_itags_get_name(itag)); | |
| for (j=0; j<nb_items; j++) { | |
| u8 *udta=NULL, *str; | |
| u32 udta_size; | |
| gf_isom_get_user_data(file, track_number, type, uuid, j+1, &udta, &udta_size); | |
| if (!udta) continue; | |
| if (j) fprintf(stderr, ", "); | |
| str = udta; | |
| if (itag>=0) { | |
| u32 s = udta[0]; | |
| s <<= 8; | |
| s |= udta[1]; | |
| if (s+4 == udta_size) { | |
| udta_size = s; | |
| str = udta+4; | |
| } | |
| } | |
| if (udta_size && gf_utf8_is_legal(str, udta_size)) { | |
| u32 idx; | |
| for (idx=0; idx<udta_size; idx++) { | |
| if (!str[idx]) break; | |
| fprintf(stderr, "%c", str[idx]); | |
| } | |
| } else { | |
| fprintf(stderr, " unknown type (%d bytes)", udta_size); | |
| } | |
| gf_free(udta); | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| GF_Err dump_isom_udta(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_udta_type, u32 dump_udta_track) | |
| { | |
| u8 *data; | |
| FILE *t; | |
| bin128 uuid; | |
| u32 count, res; | |
| GF_Err e; | |
| memset(uuid, 0, 16); | |
| count = gf_isom_get_user_data_count(file, dump_udta_track, dump_udta_type, uuid); | |
| if (!count) { | |
| M4_LOG(GF_LOG_ERROR, ("No UDTA for type %s found\n", gf_4cc_to_str(dump_udta_type) )); | |
| return GF_NOT_FOUND; | |
| } | |
| data = NULL; | |
| count = 0; | |
| e = gf_isom_get_user_data(file, dump_udta_track, dump_udta_type, uuid, 0, &data, &count); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("Error dumping UDTA %s: %s\n", gf_4cc_to_str(dump_udta_type), gf_error_to_string(e) )); | |
| return e; | |
| } | |
| if (inName) { | |
| char szName[1024]; | |
| if (is_final_name) | |
| gf_strcpy(szName, inName); | |
| else | |
| sprintf(szName, "%s_%s.udta", inName, gf_4cc_to_str(dump_udta_type) ); | |
| t = gf_fopen(szName, "wb"); | |
| if (!t) { | |
| gf_free(data); | |
| M4_LOG(GF_LOG_ERROR, ("Cannot open file %s\n", szName )); | |
| return GF_IO_ERR; | |
| } | |
| } else { | |
| t = stdout; | |
| } | |
| res = (u32) gf_fwrite(data+8, count-8, t); | |
| if (inName) gf_fclose(t); | |
| gf_free(data); | |
| if (count-8 != res) { | |
| M4_LOG(GF_LOG_ERROR, ("Error writing udta to file\n")); | |
| return GF_IO_ERR; | |
| } | |
| return GF_OK; | |
| } | |
| GF_Err dump_isom_chapters(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_mode) | |
| { | |
| #ifdef GPAC_DISABLE_ISOM_DUMP | |
| return GF_NOT_SUPPORTED; | |
| #else | |
| FILE *t; | |
| u32 i, count; | |
| u32 chap_tk = 0; | |
| count = gf_isom_get_chapter_count(file, 0); | |
| if (dump_mode==2) dump_mode = GF_TEXTDUMPTYPE_OGG_CHAP; | |
| else if (dump_mode==3) dump_mode = GF_TEXTDUMPTYPE_ZOOM_CHAP; | |
| else dump_mode = GF_TEXTDUMPTYPE_TTXT_CHAP; | |
| if (!count) { | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| if (gf_isom_get_reference_count(file, i+1, GF_ISOM_REF_CHAP)) { | |
| GF_Err e = gf_isom_get_reference(file, i+1, GF_ISOM_REF_CHAP, 1, &chap_tk); | |
| if (!e) break; | |
| } | |
| } | |
| if (!chap_tk) { | |
| M4_LOG(GF_LOG_WARNING, ("No chapters or chapters track found in file\n")); | |
| return GF_OK; | |
| } | |
| fprintf(stderr, "Dumping chapter track %d\n", chap_tk); | |
| dump_isom_timed_text(file, gf_isom_get_track_id(file, chap_tk), inName, is_final_name, GF_FALSE, dump_mode); | |
| return GF_OK; | |
| } | |
| if (inName) { | |
| char szName[1024]; | |
| gf_strcpy(szName, inName); | |
| if (!is_final_name) { | |
| if (dump_mode==GF_TEXTDUMPTYPE_OGG_CHAP) { | |
| gf_strcat(szName, ".txt"); | |
| } else if (dump_mode==GF_TEXTDUMPTYPE_ZOOM_CHAP) { | |
| gf_strcat(szName, ".txt"); | |
| } else { | |
| gf_strcat(szName, ".ttxt"); | |
| } | |
| } | |
| t = gf_fopen(szName, "wt"); | |
| if (!t) return GF_IO_ERR; | |
| } else { | |
| t = stdout; | |
| } | |
| if (dump_mode==GF_TEXTDUMPTYPE_TTXT_CHAP) { | |
| fprintf(t, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); | |
| fprintf(t, "<TextStream version=\"1.1\">\n"); | |
| fprintf(t, "<TextStreamHeader width=\"0\" height=\"0\" layer=\"0\" translation_x=\"0\" translation_y=\"0\">\n"); | |
| fprintf(t, "<TextSampleDescription horizontalJustification=\"left\" backColor=\"0 0 0\" scroll=\"None\"/>\n"); | |
| fprintf(t, "</TextStreamHeader>\n"); | |
| } | |
| for (i=0; i<count; i++) { | |
| char szDur[100]; | |
| u64 chapter_time; | |
| const char *name; | |
| gf_isom_get_chapter(file, 0, i+1, &chapter_time, &name); | |
| if (dump_mode==GF_TEXTDUMPTYPE_OGG_CHAP) { | |
| fprintf(t, "CHAPTER%02d=%s\n", i+1, format_duration(chapter_time, 1000, szDur)); | |
| fprintf(t, "CHAPTER%02dNAME=%s\n", i+1, name); | |
| } else if (dump_mode==GF_TEXTDUMPTYPE_ZOOM_CHAP) { | |
| chapter_time /= 1000; | |
| fprintf(t, "AddChapterBySecond("LLD",%s)\n", chapter_time, name); | |
| } else { | |
| fprintf(t, "<TextSample sampleTime=\"%s\" sampleDescriptionIndex=\"1\" xml:space=\"preserve\">%s</TextSample>\n" | |
| , format_duration(chapter_time, 1000, szDur), name); | |
| } | |
| } | |
| if (dump_mode==GF_TEXTDUMPTYPE_TTXT_CHAP) { | |
| fprintf(t, "</TextStream>\n"); | |
| } | |
| if (inName) gf_fclose(t); | |
| return GF_OK; | |
| #endif /*GPAC_DISABLE_ISOM_DUMP*/ | |
| } | |
| static void dump_key_info(const u8 *key_info, u32 key_info_size, Bool is_protected) | |
| { | |
| if (!key_info) return; | |
| u32 j, k, kpos=3; | |
| u32 nb_keys = 1; | |
| if (key_info[0]) { | |
| nb_keys = key_info[1]; | |
| nb_keys <<= 8; | |
| nb_keys |= key_info[2]; | |
| } | |
| for (k=0; k<nb_keys; k++) { | |
| u8 constant_iv_size=0; | |
| u8 iv_size=key_info[kpos+1]; | |
| fprintf(stderr, "\t\tKID"); | |
| if (nb_keys>1) | |
| fprintf(stderr, "%d", k+1); | |
| fprintf(stderr, " "); | |
| for (j=0; j<16; j++) fprintf(stderr, "%02X", key_info[kpos+1+j]); | |
| kpos+=17; | |
| if (!iv_size && is_protected) { | |
| constant_iv_size = key_info[1]; | |
| kpos += 1 + constant_iv_size; | |
| } | |
| fprintf(stderr, " - %sIV size %d \n", constant_iv_size ? "const " : "", constant_iv_size ? constant_iv_size : iv_size); | |
| } | |
| } | |
| static void DumpMetaItem(GF_ISOFile *file, Bool root_meta, u32 tk_num, char *name) | |
| { | |
| char szInd[2]; | |
| u32 i, count, primary_id; | |
| u32 meta_type = gf_isom_get_meta_type(file, root_meta, tk_num); | |
| if (name[0]=='\t') { | |
| szInd[0] = '\t'; | |
| szInd[1] = 0; | |
| } else { | |
| szInd[0] = 0; | |
| } | |
| count = gf_isom_get_meta_item_count(file, root_meta, tk_num); | |
| if (!count && !meta_type) return; | |
| primary_id = gf_isom_get_meta_primary_item_id(file, root_meta, tk_num); | |
| fprintf(stderr, "%s type: \"%s\" - %d resource item(s)\n", name, meta_type ? gf_4cc_to_str(meta_type) : "undefined", (count+(primary_id>0))); | |
| switch (gf_isom_has_meta_xml(file, root_meta, tk_num)) { | |
| case 1: | |
| fprintf(stderr, "%sMeta has XML resource\n", szInd); | |
| break; | |
| case 2: | |
| fprintf(stderr, "%sMeta has BinaryXML resource\n", szInd); | |
| break; | |
| } | |
| if (primary_id) { | |
| fprintf(stderr, "%sPrimary Item - ID %d\n", szInd, primary_id); | |
| } | |
| for (i=0; i<count; i++) { | |
| const char *it_name, *mime, *enc, *url, *urn; | |
| Bool self_ref; | |
| u32 ID; | |
| u32 it_type, cenc_scheme, cenc_version; | |
| GF_Err e = gf_isom_get_meta_item_info(file, root_meta, tk_num, i+1, &ID, &it_type, &cenc_scheme, &cenc_version, &self_ref, &it_name, &mime, &enc, &url, &urn); | |
| if (e) { | |
| fprintf(stderr, "%sItem #%d fetch info error: %s\n", szInd, i+1, gf_error_to_string(e) ); | |
| continue; | |
| } | |
| fprintf(stderr, "%sItem #%d: ID %d type %s", szInd, i+1, ID, gf_4cc_to_str(it_type)); | |
| if (self_ref) fprintf(stderr, " Self-Reference"); | |
| else if (it_name && it_name[0]) fprintf(stderr, " Name \"%s\"", it_name); | |
| if (mime) fprintf(stderr, " MIME: \"%s\"", mime); | |
| if (enc) fprintf(stderr, " ContentEncoding: \"%s\"", enc); | |
| if (meta_type == GF_META_ITEM_TYPE_PICT) { | |
| GF_ImageItemProperties img_props; | |
| e = gf_isom_get_meta_image_props(file, root_meta, tk_num, ID, &img_props, NULL); | |
| if (e) { | |
| fprintf(stderr, " invalid image properties !"); | |
| } else { | |
| u32 j; | |
| Bool chan_diff = 0; | |
| if (img_props.width && img_props.height) { | |
| fprintf(stderr, " size %ux%u", img_props.width, img_props.height); | |
| } | |
| if (img_props.hSpacing && img_props.vSpacing) { | |
| fprintf(stderr, " SAR %u/%u", img_props.hSpacing, img_props.vSpacing); | |
| } | |
| if (img_props.num_channels) { | |
| fprintf(stderr, " %d channel%s (", img_props.num_channels, (img_props.num_channels>1) ? "s" : ""); | |
| for (j=1; j<img_props.num_channels; j++) { | |
| if (img_props.bits_per_channel[0] != img_props.bits_per_channel[j]) | |
| chan_diff = 1; | |
| } | |
| if (chan_diff) { | |
| for (j=0; j<img_props.num_channels; j++) { | |
| if (j) fprintf(stderr, ","); | |
| fprintf(stderr, "%d", img_props.bits_per_channel[j]); | |
| } | |
| } else { | |
| fprintf(stderr, "%d", img_props.bits_per_channel[0]); | |
| } | |
| fprintf(stderr, " bpc)"); | |
| } | |
| if (img_props.hOffset || img_props.vOffset || (it_type==GF_ISOM_SUBTYPE_HVT1)) | |
| fprintf(stderr, " Offset %ux%u", img_props.hOffset, img_props.vOffset); | |
| if (img_props.alpha) fprintf(stderr, " Alpha"); | |
| if (img_props.hidden) fprintf(stderr, " Hidden"); | |
| if (img_props.angle) fprintf(stderr, " Rotate %d", img_props.angle); | |
| if (img_props.mirror) fprintf(stderr, " Mirror %d", img_props.mirror); | |
| if (img_props.clap_hden || img_props.clap_wden) | |
| fprintf(stderr, " Clap %d/%d,%d/%d,%d/%d,%d/%d", img_props.clap_wnum, img_props.clap_wden, img_props.clap_hnum, img_props.clap_hden, img_props.clap_honum, img_props.clap_hoden, img_props.clap_vonum, img_props.clap_voden); | |
| } | |
| } | |
| if (cenc_scheme) { | |
| Bool is_protected; | |
| u32 skip_byte_block, crypt_byte_block; | |
| const u8 *key_info; | |
| u32 key_info_size; | |
| fprintf(stderr, " - Protection scheme: %s v0x%08X", gf_4cc_to_str(cenc_scheme), cenc_version); | |
| gf_isom_extract_meta_item_get_cenc_info(file, root_meta, tk_num, ID, &is_protected, &skip_byte_block, &crypt_byte_block, &key_info, &key_info_size, NULL, NULL, NULL, NULL); | |
| if (skip_byte_block && crypt_byte_block) | |
| fprintf(stderr, " - Pattern %d:%d", skip_byte_block, crypt_byte_block); | |
| fprintf(stderr, "\n"); | |
| dump_key_info(key_info, key_info_size, is_protected); | |
| } | |
| fprintf(stderr, "\n"); | |
| if (url) fprintf(stderr, "%sURL: %s\n", szInd, url); | |
| if (urn) fprintf(stderr, "%sURN: %s\n", szInd, urn); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| static void print_config_hash(GF_List *xps_array, char *szName) | |
| { | |
| u32 i, j; | |
| u8 hash[20]; | |
| for (i=0; i<gf_list_count(xps_array); i++) { | |
| GF_NALUFFParam *slc = gf_list_get(xps_array, i); | |
| gf_sha1_csum((u8 *) slc->data, slc->size, hash); | |
| fprintf(stderr, "\t%s#%d hash: ", szName, i+1); | |
| for (j=0; j<20; j++) fprintf(stderr, "%02X", hash[j]); | |
| fprintf(stderr, "\n"); | |
| } | |
| } | |
| void dump_hevc_track_info(GF_ISOFile *file, u32 trackNum, GF_HEVCConfig *hevccfg | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| , HEVCState *hevc_state | |
| #endif /*GPAC_DISABLE_AV_PARSERS*/ | |
| ) | |
| { | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| u32 idx; | |
| #endif | |
| u32 k; | |
| Bool non_hevc_base_layer=GF_FALSE; | |
| fprintf(stderr, "\t%s Info:", hevccfg->is_lhvc ? "LHVC" : "HEVC"); | |
| if (!hevccfg->is_lhvc) | |
| fprintf(stderr, " Profile %s @ Level %g - Chroma Format %s\n", gf_hevc_get_profile_name(hevccfg->profile_idc), ((Double)hevccfg->level_idc) / 30.0, gf_avc_hevc_get_chroma_format_name(hevccfg->chromaFormat)); | |
| fprintf(stderr, "\n"); | |
| fprintf(stderr, "\tNAL Unit length bits: %d", 8*hevccfg->nal_unit_size); | |
| if (!hevccfg->is_lhvc) | |
| fprintf(stderr, " - general profile compatibility 0x%08X\n", hevccfg->general_profile_compatibility_flags); | |
| fprintf(stderr, "\n"); | |
| fprintf(stderr, "\tParameter Sets: "); | |
| for (k=0; k<gf_list_count(hevccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k); | |
| if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { | |
| fprintf(stderr, "%d SPS ", gf_list_count(ar->nalus)); | |
| } | |
| if (ar->type==GF_HEVC_NALU_PIC_PARAM) { | |
| fprintf(stderr, "%d PPS ", gf_list_count(ar->nalus)); | |
| } | |
| if (ar->type==GF_HEVC_NALU_VID_PARAM) { | |
| fprintf(stderr, "%d VPS ", gf_list_count(ar->nalus)); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| for (idx=0; idx<gf_list_count(ar->nalus); idx++) { | |
| GF_NALUFFParam *vps = gf_list_get(ar->nalus, idx); | |
| s32 ps_idx=gf_hevc_read_vps(vps->data, vps->size, hevc_state); | |
| if (hevccfg->is_lhvc && (ps_idx>=0)) { | |
| non_hevc_base_layer = ! hevc_state->vps[ps_idx].base_layer_internal_flag; | |
| } | |
| } | |
| #endif | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| for (k=0; k<gf_list_count(hevccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k); | |
| u32 width, height; | |
| s32 par_n, par_d; | |
| if (ar->type !=GF_HEVC_NALU_SEQ_PARAM) continue; | |
| for (idx=0; idx<gf_list_count(ar->nalus); idx++) { | |
| GF_Err e; | |
| GF_NALUFFParam *sps = gf_list_get(ar->nalus, idx); | |
| par_n = par_d = -1; | |
| e = gf_hevc_get_sps_info_with_state(hevc_state, sps->data, sps->size, NULL, &width, &height, &par_n, &par_d); | |
| if (e==GF_OK) { | |
| fprintf(stderr, "\tSPS resolution %dx%d", width, height); | |
| if ((par_n>0) && (par_d>0)) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, " - Pixel Aspect Ratio %d:%d - Indicated track size %d x %d", par_n, par_d, tw, th); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to read SPS: %s\n\n", gf_error_to_string(e) )); | |
| } | |
| } | |
| } | |
| #endif | |
| if (!hevccfg->is_lhvc) | |
| fprintf(stderr, "\tBit Depth luma %d - Chroma %d - %d temporal layers\n", hevccfg->luma_bit_depth, hevccfg->chroma_bit_depth, hevccfg->numTemporalLayers); | |
| else | |
| fprintf(stderr, "\t%d temporal layers\n", hevccfg->numTemporalLayers); | |
| if (hevccfg->is_lhvc) { | |
| fprintf(stderr, "\t%sHEVC base layer - Complete representation %d\n", non_hevc_base_layer ? "Non-" : "", hevccfg->complete_representation); | |
| } | |
| for (k=0; k<gf_list_count(hevccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k); | |
| if (ar->type==GF_HEVC_NALU_SEQ_PARAM) print_config_hash(ar->nalus, "SPS"); | |
| else if (ar->type==GF_HEVC_NALU_PIC_PARAM) print_config_hash(ar->nalus, "PPS"); | |
| else if (ar->type==GF_HEVC_NALU_VID_PARAM) print_config_hash(ar->nalus, "VPS"); | |
| } | |
| } | |
| void dump_vvc_track_info(GF_ISOFile *file, u32 trackNum, GF_VVCConfig *vvccfg | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| , VVCState *vvc_state | |
| #endif /*GPAC_DISABLE_AV_PARSERS*/ | |
| ) | |
| { | |
| u32 idx, k; | |
| fprintf(stderr, "\tNAL Unit length bits: %d", 8*vvccfg->nal_unit_size); | |
| if (vvccfg->ptl_present) { | |
| fprintf(stderr, " Profile %d @ Level %d - Chroma Format %s", vvccfg->general_profile_idc, vvccfg->general_level_idc, gf_avc_hevc_get_chroma_format_name(vvccfg->chroma_format)); | |
| if (vvccfg->general_constraint_info && vvccfg->num_constraint_info && vvccfg->general_constraint_info[0]) { | |
| fprintf(stderr, " - general constraint info 0x"); | |
| for (idx=0; idx<vvccfg->num_constraint_info; idx++) { | |
| fprintf(stderr, "%02X", vvccfg->general_constraint_info[idx]); | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| fprintf(stderr, "\tBit Depth %d - %d temporal layers\n", vvccfg->bit_depth, vvccfg->numTemporalLayers); | |
| } | |
| fprintf(stderr, "\tParameter Sets: "); | |
| for (k=0; k<gf_list_count(vvccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k); | |
| if (ar->type==GF_VVC_NALU_SEQ_PARAM) { | |
| fprintf(stderr, "%d SPS ", gf_list_count(ar->nalus)); | |
| } | |
| if (ar->type==GF_VVC_NALU_PIC_PARAM) { | |
| fprintf(stderr, "%d PPS ", gf_list_count(ar->nalus)); | |
| } | |
| if (ar->type==GF_VVC_NALU_VID_PARAM) { | |
| fprintf(stderr, "%d VPS ", gf_list_count(ar->nalus)); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) && 0 //TODO | |
| for (idx=0; idx<gf_list_count(ar->nalus); idx++) { | |
| GF_NALUFFParam *vps = gf_list_get(ar->nalus, idx); | |
| s32 ps_idx=gf_hevc_read_vps(vps->data, vps->size, hevc_state); | |
| if (hevccfg->is_lhvc && (ps_idx>=0)) { | |
| non_hevc_base_layer = ! hevc_state->vps[ps_idx].base_layer_internal_flag; | |
| } | |
| } | |
| #endif | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| for (k=0; k<gf_list_count(vvccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k); | |
| u32 width, height; | |
| s32 par_n, par_d; | |
| if (ar->type !=GF_VVC_NALU_SEQ_PARAM) continue; | |
| for (idx=0; idx<gf_list_count(ar->nalus); idx++) { | |
| GF_Err e; | |
| GF_NALUFFParam *sps = gf_list_get(ar->nalus, idx); | |
| par_n = par_d = -1; | |
| e = gf_vvc_get_sps_info(sps->data, sps->size, NULL, &width, &height, &par_n, &par_d); | |
| if (e==GF_OK) { | |
| fprintf(stderr, "\tSPS resolution %dx%d", width, height); | |
| if ((par_n>0) && (par_d>0)) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, " - Pixel Aspect Ratio %d:%d - Indicated track size %d x %d", par_n, par_d, tw, th); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else { | |
| M4_LOG(GF_LOG_ERROR, ("\nFailed to read SPS: %s\n\n", gf_error_to_string(e) )); | |
| } | |
| } | |
| } | |
| #endif | |
| for (k=0; k<gf_list_count(vvccfg->param_array); k++) { | |
| GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k); | |
| if (ar->type==GF_VVC_NALU_SEQ_PARAM) print_config_hash(ar->nalus, "SPS"); | |
| else if (ar->type==GF_VVC_NALU_PIC_PARAM) print_config_hash(ar->nalus, "PPS"); | |
| else if (ar->type==GF_VVC_NALU_VID_PARAM) print_config_hash(ar->nalus, "VPS"); | |
| } | |
| } | |
| #if !defined(GPAC_DISABLE_INSPECT) | |
| void gf_inspect_format_timecode(const u8 *data, u32 size, u32 tmcd_flags, u32 tc_num, u32 tc_den, u32 tmcd_fpt, char szFmt[100]); | |
| #endif | |
| static void DumpStsdInfo(GF_ISOFile *file, u32 trackNum, Bool full_dump, Bool dump_m4sys, u32 mtype, u32 stsd_idx, Bool *is_od_track) | |
| { | |
| char szCodec[RFC6381_CODEC_NAME_SIZE_MAX]; | |
| u32 i, j, msub_type, sr, nb_ch, count, bps, pfmt, codecid, w_dsisize=0; | |
| GF_ESD *esd; | |
| msub_type = gf_isom_get_media_subtype(file, trackNum, stsd_idx); | |
| if (msub_type==GF_ISOM_SUBTYPE_MPEG4_CRYP) | |
| gf_isom_get_original_format_type(file, trackNum, stsd_idx, &msub_type); | |
| pfmt = gf_pixel_fmt_from_qt_type(msub_type); | |
| codecid = gf_codec_id_from_isobmf(msub_type); | |
| u32 stsd_type = msub_type; | |
| if (msub_type==GF_ISOM_SUBTYPE_MPEG4) | |
| stsd_type = gf_isom_get_mpeg4_subtype(file, trackNum, stsd_idx); | |
| if (full_dump) { | |
| if (stsd_idx>1) fprintf(stderr, "\n"); | |
| fprintf(stderr, "Sample Description #%u - %s:%s\n", stsd_idx, gf_4cc_to_str(mtype), gf_4cc_to_str(stsd_type)); | |
| } else { | |
| if (stsd_type == GF_4CC('G','M','C','W')) { | |
| u32 w_codec_id=0; | |
| char *w_codec_str = NULL; | |
| GF_GenericSampleDescription *gdesc = gf_isom_get_generic_sample_description(file, trackNum, stsd_idx); | |
| if (gdesc) { | |
| if (gdesc->extension_buf_size>16) { | |
| GF_BitStream *bs = gf_bs_new(gdesc->extension_buf, gdesc->extension_buf_size, GF_BITSTREAM_READ); | |
| gf_bs_read_u32(bs); | |
| gf_bs_read_u32(bs); | |
| gf_bs_read_u32(bs); | |
| w_codec_id = gf_bs_read_u32(bs); | |
| w_codec_str = gf_bs_read_utf8(bs); | |
| gf_bs_read_u32(bs); | |
| w_dsisize = (u32) gf_bs_available(bs); | |
| gf_bs_del(bs); | |
| } | |
| if (gdesc->extension_buf) gf_free(gdesc->extension_buf); | |
| gf_free(gdesc); | |
| } | |
| fprintf(stderr, "Media Type: %s:%s (GPAC wrapper for %s)\n", gf_4cc_to_str(mtype), gf_4cc_to_str(stsd_type), w_codec_str ? w_codec_str : gf_4cc_to_str(w_codec_id) ); | |
| if (w_codec_str) gf_free(w_codec_str); | |
| } else { | |
| fprintf(stderr, "Media Type: %s:%s\n", gf_4cc_to_str(mtype), gf_4cc_to_str(stsd_type)); | |
| } | |
| } | |
| if (!gf_isom_is_self_contained(file, trackNum, stsd_idx)) { | |
| const char *url, *urn; | |
| gf_isom_get_data_reference(file, trackNum, stsd_idx, &url, &urn); | |
| fprintf(stderr, "Media Data Location: %s\n", url ? url : urn); | |
| } | |
| if (gf_isom_is_video_handler_type(mtype) ) { | |
| u32 w, h; | |
| u16 bit_depth; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| gf_isom_get_visual_bit_depth(file, trackNum, stsd_idx, &bit_depth); | |
| fprintf(stderr, "\tVisual Sample Entry Info: width=%d height=%d (depth=%d bits)\n", w, h, (int)bit_depth); | |
| GF_DOVIDecoderConfigurationRecord *dovi = gf_isom_dovi_config_get(file, trackNum, stsd_idx); | |
| if (dovi) { | |
| fprintf(stderr, "\tDolbyVision version %d.%d profile %d level %d (RPU: %d Base Layer: %d Enhancement Layer: %d Compatibility: %d)\n", | |
| dovi->dv_version_major, dovi->dv_version_minor, dovi->dv_profile, dovi->dv_level, | |
| dovi->rpu_present_flag, dovi->bl_present_flag, dovi->el_present_flag, dovi->dv_bl_signal_compatibility_id); | |
| gf_odf_dovi_cfg_del(dovi); | |
| } | |
| } | |
| gf_isom_get_audio_info(file, trackNum, stsd_idx, &sr, &nb_ch, &bps); | |
| gf_isom_set_nalu_extract_mode(file, trackNum, GF_ISOM_NALU_EXTRACT_INSPECT); | |
| if (stsd_type == GF_ISOM_SUBTYPE_RESV) { | |
| gf_isom_get_original_format_type(file, trackNum, stsd_idx, &msub_type); | |
| } | |
| if ((msub_type==GF_ISOM_SUBTYPE_MPEG4) | |
| || (msub_type==GF_ISOM_SUBTYPE_AVC_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_AVC2_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_AVC3_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_AVC4_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_SVC_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_MVC_H264) | |
| || (msub_type==GF_ISOM_SUBTYPE_LSR1) | |
| || (msub_type==GF_ISOM_SUBTYPE_HVC1) | |
| || (msub_type==GF_ISOM_SUBTYPE_HEV1) | |
| || (msub_type==GF_ISOM_SUBTYPE_HVC2) | |
| || (msub_type==GF_ISOM_SUBTYPE_HEV2) | |
| || (msub_type==GF_ISOM_SUBTYPE_LHV1) | |
| || (msub_type==GF_ISOM_SUBTYPE_LHE1) | |
| || (msub_type==GF_ISOM_SUBTYPE_HVT1) | |
| || (msub_type==GF_ISOM_SUBTYPE_DVHE) | |
| || (msub_type==GF_ISOM_SUBTYPE_DVH1) | |
| || (msub_type==GF_ISOM_SUBTYPE_DVA1) | |
| || (msub_type==GF_ISOM_SUBTYPE_DVAV) | |
| ) { | |
| esd = gf_isom_get_esd(file, trackNum, stsd_idx); | |
| if (!esd || !esd->decoderConfig) { | |
| M4_LOG(GF_LOG_WARNING, ("\tWARNING: Broken MPEG-4 Track\n")); | |
| if (esd) gf_odf_desc_del((GF_Descriptor *)esd); | |
| } else { | |
| const char *st = gf_stream_type_name(esd->decoderConfig->streamType); | |
| if (dump_m4sys) { | |
| if (st) { | |
| fprintf(stderr, "\tMPEG-4 Config%s%s Stream - ObjectTypeIndication 0x%02x\n", | |
| full_dump ? "\n\t" : ": ", st, esd->decoderConfig->objectTypeIndication); | |
| } else { | |
| fprintf(stderr, "\tMPEG-4 Config%sStream Type 0x%02x - ObjectTypeIndication 0x%02x\n", | |
| full_dump ? "\n\t" : ": ", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); | |
| } | |
| } | |
| if (esd->decoderConfig->streamType==GF_STREAM_OD) | |
| *is_od_track=1; | |
| if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) { | |
| u32 w, h; | |
| u16 rvc_predef; | |
| w = h = 0; | |
| if (esd->decoderConfig->objectTypeIndication==GF_CODECID_MPEG4_PART2) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| if (!esd->decoderConfig->decoderSpecificInfo) { | |
| #else | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\tMPEG-4 Visual Size %d x %d\n", w, h); | |
| #endif | |
| M4_LOG(GF_LOG_WARNING, ("Non-compliant MPEG-4 Visual track: video_object_layer infos not found in sample description\n")); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| } else { | |
| GF_M4VDecSpecInfo dsi; | |
| gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| w = dsi.width; | |
| h = dsi.height; | |
| fprintf(stderr, "\tMPEG-4 Visual Size %d x %d - %s\n", w, h, gf_m4v_get_profile_name(dsi.VideoPL)); | |
| if (dsi.par_den && dsi.par_num) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", dsi.par_num, dsi.par_den, tw, th); | |
| } | |
| } | |
| #endif | |
| } else if (gf_isom_get_avc_svc_type(file, trackNum, stsd_idx) != GF_ISOM_AVCTYPE_NONE) { | |
| GF_AVCConfig *avccfg, *svccfg, *mvccfg; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\tAVC/H264 Video - Visual Size %d x %d\n", w, h); | |
| avccfg = gf_isom_avc_config_get(file, trackNum, stsd_idx); | |
| svccfg = gf_isom_svc_config_get(file, trackNum, stsd_idx); | |
| mvccfg = gf_isom_mvc_config_get(file, trackNum, stsd_idx); | |
| if (!avccfg && !svccfg && !mvccfg) { | |
| M4_LOG(GF_LOG_ERROR, ("\tNon-compliant AVC track: SPS/PPS not found in sample description\n")); | |
| } else if (avccfg) { | |
| fprintf(stderr, "\tAVC Info: %d SPS - %d PPS", gf_list_count(avccfg->sequenceParameterSets) , gf_list_count(avccfg->pictureParameterSets) ); | |
| fprintf(stderr, " - Profile %s @ Level %g\n", gf_avc_get_profile_name(avccfg->AVCProfileIndication), ((Double)avccfg->AVCLevelIndication)/10.0 ); | |
| fprintf(stderr, "\tNAL Unit length bits: %d\n", 8*avccfg->nal_unit_size); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| for (i=0; i<gf_list_count(avccfg->sequenceParameterSets); i++) { | |
| s32 par_n, par_d; | |
| GF_NALUFFParam *slc = gf_list_get(avccfg->sequenceParameterSets, i); | |
| gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, NULL, &par_n, &par_d); | |
| if ((par_n>0) && (par_d>0)) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); | |
| } | |
| if (!full_dump) break; | |
| } | |
| #endif | |
| if (avccfg->chroma_bit_depth) { | |
| fprintf(stderr, "\tChroma format %s - Luma bit depth %d - chroma bit depth %d\n", gf_avc_hevc_get_chroma_format_name(avccfg->chroma_format), avccfg->luma_bit_depth, avccfg->chroma_bit_depth); | |
| } | |
| print_config_hash(avccfg->sequenceParameterSets, "SPS"); | |
| print_config_hash(avccfg->pictureParameterSets, "PPS"); | |
| gf_odf_avc_cfg_del(avccfg); | |
| } | |
| if (svccfg) { | |
| fprintf(stderr, "\n\tSVC Info: %d SPS - %d PPS - Profile %s @ Level %g\n", gf_list_count(svccfg->sequenceParameterSets) , gf_list_count(svccfg->pictureParameterSets), gf_avc_get_profile_name(svccfg->AVCProfileIndication), ((Double)svccfg->AVCLevelIndication)/10.0 ); | |
| fprintf(stderr, "\tSVC NAL Unit length bits: %d\n", 8*svccfg->nal_unit_size); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| for (i=0; i<gf_list_count(svccfg->sequenceParameterSets); i++) { | |
| GF_NALUFFParam *slc = gf_list_get(svccfg->sequenceParameterSets, i); | |
| if (slc) { | |
| s32 par_n, par_d; | |
| u32 s_w, s_h, sps_id; | |
| gf_avc_get_sps_info(slc->data, slc->size, &sps_id, &s_w, &s_h, &par_n, &par_d); | |
| fprintf(stderr, "\t\tSPS ID %d - Visual Size %d x %d\n", sps_id, s_w, s_h); | |
| if ((par_n>0) && (par_d>0)) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); | |
| } | |
| } | |
| } | |
| #endif | |
| print_config_hash(svccfg->sequenceParameterSets, "SPS"); | |
| print_config_hash(svccfg->pictureParameterSets, "PPS"); | |
| print_config_hash(svccfg->sequenceParameterSetExtensions, "SPSEx"); | |
| gf_odf_avc_cfg_del(svccfg); | |
| } | |
| if (mvccfg) { | |
| fprintf(stderr, "\n\tMVC Info: %d SPS - %d PPS - Profile %s @ Level %g\n", gf_list_count(mvccfg->sequenceParameterSets) , gf_list_count(mvccfg->pictureParameterSets), gf_avc_get_profile_name(mvccfg->AVCProfileIndication), ((Double)mvccfg->AVCLevelIndication)/10.0 ); | |
| fprintf(stderr, "\tMVC NAL Unit length bits: %d\n", 8*mvccfg->nal_unit_size); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| for (i=0; i<gf_list_count(mvccfg->sequenceParameterSets); i++) { | |
| GF_NALUFFParam *slc = gf_list_get(mvccfg->sequenceParameterSets, i); | |
| if (slc) { | |
| u32 s_w, s_h, sps_id; | |
| s32 par_n, par_d; | |
| gf_avc_get_sps_info(slc->data, slc->size, &sps_id, &s_w, &s_h, &par_n, &par_d); | |
| fprintf(stderr, "\t\tSPS ID %d - Visual Size %d x %d\n", sps_id, s_w, s_h); | |
| if ((par_n>0) && (par_d>0)) { | |
| u32 tw, th; | |
| gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); | |
| fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); | |
| } | |
| } | |
| } | |
| #endif | |
| print_config_hash(mvccfg->sequenceParameterSets, "SPS"); | |
| print_config_hash(mvccfg->pictureParameterSets, "PPS"); | |
| gf_odf_avc_cfg_del(mvccfg); | |
| } | |
| } else if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_HEVC) | |
| || (esd->decoderConfig->objectTypeIndication==GF_CODECID_LHVC) | |
| ) { | |
| GF_HEVCConfig *hevccfg, *lhvccfg; | |
| GF_OperatingPointsInformation *oinf; | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| HEVCState *hvc_state; | |
| GF_SAFEALLOC(hvc_state, HEVCState); | |
| if (!hvc_state) return; | |
| hvc_state->sps_active_idx = -1; | |
| #endif | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\tHEVC Video - Visual Size %d x %d\n", w, h); | |
| hevccfg = gf_isom_hevc_config_get(file, trackNum, stsd_idx); | |
| lhvccfg = gf_isom_lhvc_config_get(file, trackNum, stsd_idx); | |
| if (msub_type==GF_ISOM_SUBTYPE_HVT1) { | |
| const u8 *data; | |
| u32 tsize; | |
| u32 is_default, tx,ty,tw,th, id, independent; | |
| Bool full_frame; | |
| if (gf_isom_get_tile_info(file, trackNum, stsd_idx, &is_default, &id, &independent, &full_frame, &tx, &ty, &tw, &th)) { | |
| fprintf(stderr, "\tHEVC Tile - ID %d independent %d (x,y,w,h)=%d,%d,%d,%d \n", id, independent, tx, ty, tw, th); | |
| } else if (gf_isom_get_sample_group_info(file, trackNum, stsd_idx, GF_ISOM_SAMPLE_GROUP_TRIF, &is_default, &data, &tsize)) { | |
| fprintf(stderr, "\tHEVC Tile track containing a tile set\n"); | |
| } else { | |
| fprintf(stderr, "\tHEVC Tile track without tiling info\n"); | |
| } | |
| } else if (!hevccfg && !lhvccfg) { | |
| M4_LOG(GF_LOG_ERROR, ("\tNon-compliant HEVC track: No hvcC or shcC found in sample description\n")); | |
| } | |
| if (gf_isom_get_reference_count(file, trackNum, GF_ISOM_REF_SABT)) { | |
| fprintf(stderr, "\tHEVC Tile base track\n"); | |
| } | |
| if (hevccfg) { | |
| dump_hevc_track_info(file, trackNum, hevccfg | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| , hvc_state | |
| #endif | |
| ); | |
| gf_odf_hevc_cfg_del(hevccfg); | |
| fprintf(stderr, "\n"); | |
| } | |
| if (lhvccfg) { | |
| dump_hevc_track_info(file, trackNum, lhvccfg | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| , hvc_state | |
| #endif | |
| ); | |
| gf_odf_hevc_cfg_del(lhvccfg); | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| gf_free(hvc_state); | |
| #endif | |
| if (gf_isom_get_oinf_info(file, trackNum, &oinf)) { | |
| fprintf(stderr, "\n\tOperating Points Information -"); | |
| fprintf(stderr, " scalability_mask %d (", oinf->scalability_mask); | |
| switch (oinf->scalability_mask) { | |
| case 2: | |
| fprintf(stderr, "Multiview"); | |
| break; | |
| case 4: | |
| fprintf(stderr, "Spatial scalability"); | |
| break; | |
| case 8: | |
| fprintf(stderr, "Auxiliary"); | |
| break; | |
| default: | |
| fprintf(stderr, "unknown"); | |
| } | |
| //TODO: need to dump more info ? | |
| fprintf(stderr, ") num_profile_tier_level %d ", gf_list_count(oinf->profile_tier_levels) ); | |
| fprintf(stderr, " num_operating_points %d dependency layers %d \n", gf_list_count(oinf->operating_points), gf_list_count(oinf->dependency_layers) ); | |
| } | |
| } | |
| /*OGG media*/ | |
| else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_THEORA) { | |
| char *szName; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| szName = "Unknown"; | |
| if (esd->decoderConfig->decoderSpecificInfo | |
| && (esd->decoderConfig->decoderSpecificInfo->dataLength>=10) | |
| && !strnicmp((char *) &esd->decoderConfig->decoderSpecificInfo->data[3], "theora", 6) | |
| ) | |
| szName = "Theora"; | |
| fprintf(stderr, "\tOgg/%s video / GPAC Mux - Visual Size %d x %d\n", szName, w, h); | |
| } | |
| else { | |
| //check if we know this codec from its OTI | |
| u32 codec_id = gf_codecid_from_oti(GF_STREAM_VISUAL, esd->decoderConfig->objectTypeIndication); | |
| if (codec_id) { | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\t%s - Visual Size %d x %d\n", gf_codecid_name(codec_id), w, h); | |
| } | |
| } | |
| if (!w || !h) { | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\tVisual Size %d x %d\n", w, h); | |
| } | |
| if (gf_isom_get_rvc_config(file, trackNum, stsd_idx, &rvc_predef, NULL, NULL, NULL)==GF_OK) { | |
| fprintf(stderr, "\tHas RVC signaled - Predefined configuration %d\n", rvc_predef); | |
| } | |
| } else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_M4ADecSpecInfo a_cfg; | |
| GF_Err e; | |
| u32 oti; | |
| #endif | |
| u32 codec_id; | |
| Bool is_mp2 = GF_FALSE; | |
| switch (esd->decoderConfig->objectTypeIndication) { | |
| case GF_CODECID_AAC_MPEG2_MP: | |
| case GF_CODECID_AAC_MPEG2_LCP: | |
| case GF_CODECID_AAC_MPEG2_SSRP: | |
| is_mp2 = GF_TRUE; | |
| case GF_CODECID_AAC_MPEG4: | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| if (!esd->decoderConfig->decoderSpecificInfo) | |
| e = GF_NON_COMPLIANT_BITSTREAM; | |
| else | |
| e = gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| if (e) { | |
| M4_LOG(GF_LOG_ERROR, ("\tCorrupted AAC Config\n")); | |
| } else { | |
| char *signaling = "implicit"; | |
| char *heaac = ""; | |
| if (!is_mp2 && a_cfg.has_sbr) { | |
| if (a_cfg.has_ps) heaac = "(HE-AAC v2) "; | |
| else heaac = "(HE-AAC v1) "; | |
| } | |
| if (a_cfg.base_object_type==2) { | |
| if (a_cfg.has_ps || a_cfg.has_sbr) | |
| signaling = "backward compatible"; | |
| } else { | |
| signaling = "hierarchical"; | |
| } | |
| fprintf(stderr, "\t%s (AOT=%d %s) %s- %d Channel(s) - SampleRate %d", gf_m4a_object_type_name(a_cfg.base_object_type), a_cfg.base_object_type, signaling, heaac, a_cfg.nb_chan, a_cfg.base_sr); | |
| if (is_mp2) fprintf(stderr, " (MPEG-2 Signaling)"); | |
| if (a_cfg.has_sbr) fprintf(stderr, " - SBR: SampleRate %d Type %s", a_cfg.sbr_sr, gf_m4a_object_type_name(a_cfg.sbr_object_type)); | |
| if (a_cfg.has_ps) fprintf(stderr, " - PS"); | |
| fprintf(stderr, "\n"); | |
| } | |
| #else | |
| fprintf(stderr, "\tMPEG-2/4 Audio - %d Channels - SampleRate %d\n", nb_ch, sr); | |
| #endif | |
| break; | |
| case GF_CODECID_MPEG2_PART3: | |
| case GF_CODECID_MPEG_AUDIO: | |
| case GF_CODECID_MPEG_AUDIO_L1: | |
| if (msub_type == GF_ISOM_SUBTYPE_MPEG4_CRYP) { | |
| fprintf(stderr, "\tMPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr); | |
| } else { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, &oti); | |
| if (samp) { | |
| if (samp->data && (samp->dataLength>4)) { | |
| u32 mhdr = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\t%s Audio - %d Channel(s) - SampleRate %d - Layer %d\n", | |
| gf_mp3_version_name(mhdr), | |
| gf_mp3_num_channels(mhdr), | |
| gf_mp3_sampling_rate(mhdr), | |
| gf_mp3_layer(mhdr) | |
| ); | |
| } | |
| gf_isom_sample_del(&samp); | |
| } else { | |
| M4_LOG(GF_LOG_ERROR, ("Error fetching sample: %s\n", gf_error_to_string(gf_isom_last_error(file)) )); | |
| } | |
| #else | |
| fprintf(stderr, "\tMPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr); | |
| #endif | |
| } | |
| break; | |
| case GF_CODECID_EVRC: | |
| fprintf(stderr, "\tEVRC Audio - Sample Rate 8000 - 1 channel\n"); | |
| break; | |
| case GF_CODECID_SMV: | |
| fprintf(stderr, "\tSMV Audio - Sample Rate 8000 - 1 channel\n"); | |
| break; | |
| case GF_CODECID_QCELP: | |
| fprintf(stderr, "\tQCELP Audio - Sample Rate 8000 - 1 channel\n"); | |
| break; | |
| /*packetVideo hack for EVRC...*/ | |
| case GF_CODECID_EVRC_PV: | |
| if (esd->decoderConfig->decoderSpecificInfo && (esd->decoderConfig->decoderSpecificInfo->dataLength==8) | |
| && !strnicmp((char *)esd->decoderConfig->decoderSpecificInfo->data, "pvmm", 4)) { | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\tEVRC Audio (PacketVideo Mux) - Sample Rate 8000 - 1 channel\n"); | |
| } | |
| break; | |
| default: | |
| codec_id = gf_codecid_from_oti(GF_STREAM_AUDIO, esd->decoderConfig->objectTypeIndication); | |
| if (codec_id) { | |
| fprintf(stderr, "\t%s - Sample Rate %d - %d channel(s)\n", gf_codecid_name(codec_id), sr, nb_ch); | |
| } | |
| break; | |
| } | |
| } | |
| else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { | |
| if (esd->decoderConfig->objectTypeIndication<=4) { | |
| GF_BIFSConfig *b_cfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); | |
| if (b_cfg) { | |
| fprintf(stderr, "\tBIFS Scene description - %s stream\n", b_cfg->elementaryMasks ? "Animation" : "Command"); | |
| if (full_dump && !b_cfg->elementaryMasks) { | |
| fprintf(stderr, "\tWidth %d Height %d Pixel Metrics %s\n", b_cfg->pixelWidth, b_cfg->pixelHeight, b_cfg->pixelMetrics ? "yes" : "no"); | |
| } | |
| gf_odf_desc_del((GF_Descriptor *)b_cfg); | |
| } else { | |
| fprintf(stderr, "! Invalid BIFS configuration !\n"); | |
| } | |
| } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_AFX) { | |
| u8 tag = (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) ? esd->decoderConfig->decoderSpecificInfo->data[0] : 0xFF; | |
| const char *afxtype = gf_stream_type_afx_name(tag); | |
| fprintf(stderr, "\tAFX Stream - type %s (%d)\n", afxtype, tag); | |
| } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_FONT) { | |
| fprintf(stderr, "\tFont Data stream\n"); | |
| } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_LASER) { | |
| GF_LASERConfig l_cfg; | |
| gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &l_cfg); | |
| fprintf(stderr, "\tLASER Stream - %s\n", l_cfg.newSceneIndicator ? "Full Scene" : "Scene Segment"); | |
| } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_TEXT_MPEG4) { | |
| fprintf(stderr, "\tMPEG-4 Streaming Text stream\n"); | |
| } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_SYNTHESIZED_TEXTURE) { | |
| fprintf(stderr, "\tSynthetized Texture stream stream\n"); | |
| } else { | |
| M4_LOG(GF_LOG_WARNING, ("\tUnknown Systems stream OTI %d\n", esd->decoderConfig->objectTypeIndication)); | |
| } | |
| } | |
| /*sync is only valid if we open all tracks to take care of default MP4 sync..*/ | |
| if (!full_dump) { | |
| if (dump_m4sys) { | |
| if (!esd->OCRESID || (esd->OCRESID == esd->ESID)) | |
| fprintf(stderr, "\tSelf-synchronized\n"); | |
| else | |
| fprintf(stderr, "\tSynchronized on stream %d\n", esd->OCRESID); | |
| } | |
| } else { | |
| fprintf(stderr, "\tDecoding Buffer size %d - Bitrate: avg %d - max %d kbps\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate/1000, esd->decoderConfig->maxBitrate/1000); | |
| if (esd->dependsOnESID) | |
| fprintf(stderr, "\tDepends on stream %d for decoding\n", esd->dependsOnESID); | |
| else | |
| fprintf(stderr, "\tNo stream dependencies for decoding\n"); | |
| fprintf(stderr, "\tStreamPriority %d\n", esd->streamPriority); | |
| if (esd->URLString) fprintf(stderr, "\tRemote Data Source %s\n", esd->URLString); | |
| } | |
| gf_odf_desc_del((GF_Descriptor *) esd); | |
| } | |
| } else if (msub_type == GF_ISOM_SUBTYPE_AV01) { | |
| GF_AV1Config *av1c; | |
| u32 w, h; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\tAOM AV1 stream - Resolution %d x %d\n", w, h); | |
| av1c = gf_isom_av1_config_get(file, trackNum, stsd_idx); | |
| if (!av1c) { | |
| fprintf(stderr, "\tCorrupted av1 config\n"); | |
| } else { | |
| fprintf(stderr, "\tversion=%u, profile=%u, level_idx0=%u, tier=%u\n", (u32)av1c->version, (u32)av1c->seq_profile, (u32)av1c->seq_level_idx_0, (u32)av1c->seq_tier_0); | |
| fprintf(stderr, "\thigh_bitdepth=%u, twelve_bit=%u, monochrome=%u\n", (u32)av1c->high_bitdepth, (u32)av1c->twelve_bit, (u32)av1c->monochrome); | |
| fprintf(stderr, "\tchroma: subsampling_x=%u, subsampling_y=%u, sample_position=%u\n", (u32)av1c->chroma_subsampling_x, (u32)av1c->chroma_subsampling_y, (u32)av1c->chroma_sample_position); | |
| if (av1c->initial_presentation_delay_present) | |
| fprintf(stderr, "\tInitial presentation delay %u\n", (u32) av1c->initial_presentation_delay_minus_one+1); | |
| count = gf_list_count(av1c->obu_array); | |
| for (i=0; i<count; i++) { | |
| u8 hash[20]; | |
| GF_AV1_OBUArrayEntry *obu = gf_list_get(av1c->obu_array, i); | |
| gf_sha1_csum((u8*)obu->obu, (u32)obu->obu_length, hash); | |
| fprintf(stderr, "\tOBU#%d %s hash: ", i+1, gf_av1_get_obu_name(obu->obu_type) ); | |
| for (j=0; j<20; j++) fprintf(stderr, "%02X", hash[j]); | |
| fprintf(stderr, "\n"); | |
| } | |
| gf_odf_av1_cfg_del(av1c); | |
| } | |
| } else if (msub_type == GF_ISOM_SUBTYPE_3GP_H263) { | |
| u32 w, h; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\t3GPP H263 stream - Resolution %d x %d\n", w, h); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_MJP2) { | |
| u32 w, h; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\tMotionJPEG2000 stream - Resolution %d x %d\n", w, h); | |
| } else if ((msub_type == GF_ISOM_SUBTYPE_3GP_AMR) || (msub_type == GF_ISOM_SUBTYPE_3GP_AMR_WB)) { | |
| fprintf(stderr, "\t3GPP AMR%s stream - Sample Rate %d - %d channel(s) %d bps\n", (msub_type == GF_ISOM_SUBTYPE_3GP_AMR_WB) ? " Wide Band" : "", sr, nb_ch, (u32) bps); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_3GP_EVRC) { | |
| fprintf(stderr, "\t3GPP EVRC stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_3GP_QCELP) { | |
| fprintf(stderr, "\t3GPP QCELP stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_MP3) { | |
| fprintf(stderr, "\tMPEG 1/2 Audio stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps); | |
| } else if ((msub_type == GF_ISOM_SUBTYPE_AC3) || (msub_type == GF_ISOM_SUBTYPE_EC3)) { | |
| u32 br = 0; | |
| const char *lfe = ""; | |
| Bool atmos=GF_FALSE; | |
| u32 complexity_index_type=0; | |
| Bool is_ec3 = (msub_type == GF_ISOM_SUBTYPE_EC3) ? GF_TRUE : GF_FALSE; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_AC3Config *ac3 = gf_isom_ac3_config_get(file, trackNum, stsd_idx); | |
| if (ac3) { | |
| nb_ch = gf_ac3_get_total_channels(ac3->streams[0].acmod); | |
| if (ac3->streams[0].nb_dep_sub) { | |
| nb_ch += gf_eac3_get_chan_loc_count(ac3->streams[0].chan_loc); | |
| } | |
| if (ac3->streams[0].lfon) lfe = ".1"; | |
| br = ac3->is_ec3 ? (ac3->brcode*1000) : gf_ac3_get_bitrate(ac3->brcode); | |
| is_ec3 = ac3->is_ec3; | |
| if (is_ec3) { | |
| atmos = ac3->atmos_ec3_ext; | |
| complexity_index_type = ac3->complexity_index_type; | |
| } | |
| gf_free(ac3); | |
| } | |
| #endif | |
| fprintf(stderr, "\t%s stream - Sample Rate %d - %d%s channel(s) - bitrate %d", is_ec3 ? "EC-3" : "AC-3", sr, nb_ch, lfe, br); | |
| if (atmos) { | |
| fprintf(stderr, " - ATMOS complexity index type %d", complexity_index_type); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_AC4) { | |
| u32 sr = 0; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_AC4Config *ac4 = gf_isom_ac4_config_get(file, trackNum, stsd_idx); | |
| if (ac4) { | |
| nb_ch = ac4->channel_count; | |
| sr = ac4->sample_rate; | |
| gf_odf_ac4_cfg_del(ac4); | |
| } | |
| #endif | |
| fprintf(stderr, "\tAC-4 stream - Sample Rate %d - %d channel(s)\n", sr, nb_ch); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_3GP_SMV) { | |
| fprintf(stderr, "\t3GPP SMV stream - Sample Rate %d - %d channel(s) %d bits per samples\n", sr, nb_ch, (u32) bps); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_3GP_DIMS) { | |
| u32 w, h; | |
| GF_DIMSDescription dims; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| gf_isom_get_dims_description(file, trackNum, stsd_idx, &dims); | |
| fprintf(stderr, "\t3GPP DIMS stream - size %d x %d - Profile %d - Level %d\n", w, h, dims.profile, dims.level); | |
| fprintf(stderr, "\tpathComponents: %d - useFullRequestHost: %s\n", dims.pathComponents, dims.fullRequestHost ? "yes" : "no"); | |
| fprintf(stderr, "\tstream type: %s - redundant: %s\n", dims.streamType ? "primary" : "secondary", (dims.containsRedundant==1) ? "main" : ((dims.containsRedundant==2) ? "redundant" : "main+redundant") ); | |
| if (dims.textEncoding && dims.textEncoding[0]) fprintf(stderr, "\ttext encoding %s\n", dims.textEncoding); | |
| if (dims.contentEncoding && dims.contentEncoding[0]) fprintf(stderr, "\tcontent encoding %s\n", dims.contentEncoding); | |
| if (dims.content_script_types && dims.content_script_types[0]) fprintf(stderr, "\tscript languages %s\n", dims.content_script_types); | |
| } else if (mtype==GF_ISOM_MEDIA_HINT) { | |
| u32 refTrack; | |
| s32 refCount = gf_isom_get_reference_count(file, trackNum, GF_ISOM_REF_HINT); | |
| if (refCount>0) { | |
| fprintf(stderr, "\tStreaming Hint Track for track%s ", (refCount>1) ? "s" :""); | |
| for (i=0; i<(u32) refCount; i++) { | |
| gf_isom_get_reference(file, trackNum, GF_ISOM_REF_HINT, i+1, &refTrack); | |
| if (i) fprintf(stderr, " - "); | |
| fprintf(stderr, "ID %d", gf_isom_get_track_id(file, refTrack)); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else { | |
| fprintf(stderr, "\tStreaming Hint Track (no refs)\n"); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_HINTING | |
| refCount = gf_isom_get_payt_count(file, trackNum); | |
| if (refCount>0) { | |
| for (i=0; i<(u32) refCount; i++) { | |
| const char *name = gf_isom_get_payt_info(file, trackNum, i+1, &refTrack); | |
| fprintf(stderr, "\tPayload ID %d: type %s\n", refTrack, name); | |
| } | |
| } | |
| #endif | |
| } else if (mtype==GF_ISOM_MEDIA_FLASH) { | |
| fprintf(stderr, "\tMacromedia Flash Movie\n"); | |
| } else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT) || (mtype==GF_ISOM_MEDIA_MPEG_SUBT)) { | |
| u32 w, h; | |
| s16 l; | |
| s32 tx, ty; | |
| Bool check_tx3g=GF_FALSE; | |
| const char *content_encoding = NULL; | |
| const char *mime = NULL; | |
| const char *config = NULL; | |
| const char *_namespace = NULL; | |
| const char *schema_loc = NULL; | |
| const char *auxiliary_mimes = NULL; | |
| gf_isom_get_track_layout_info(file, trackNum, &w, &h, &tx, &ty, &l); | |
| if (msub_type == GF_ISOM_SUBTYPE_SBTT) { | |
| gf_isom_stxt_get_description(file, trackNum, stsd_idx, &mime, &content_encoding, &config); | |
| fprintf(stderr, "\tTextual Subtitle Stream "); | |
| fprintf(stderr, "- mime %s", mime); | |
| if (content_encoding != NULL) { | |
| fprintf(stderr, " - encoding %s", content_encoding); | |
| } | |
| if (config != NULL) { | |
| fprintf(stderr, " - %d bytes config", (u32) strlen(config)); | |
| } | |
| } else if (msub_type == GF_ISOM_SUBTYPE_STXT) { | |
| gf_isom_stxt_get_description(file, trackNum, stsd_idx, &mime, &content_encoding, &config); | |
| fprintf(stderr, "\tSimple Timed Text Stream "); | |
| fprintf(stderr, "- mime %s", mime); | |
| if (content_encoding != NULL) { | |
| fprintf(stderr, " - encoding %s", content_encoding); | |
| } | |
| if (config != NULL) { | |
| fprintf(stderr, " - %d bytes config", (u32) strlen(config)); | |
| } | |
| } else if (msub_type == GF_ISOM_SUBTYPE_STPP) { | |
| gf_isom_xml_subtitle_get_description(file, trackNum, stsd_idx, &_namespace, &schema_loc, &auxiliary_mimes); | |
| fprintf(stderr, "\tXML Subtitle Stream "); | |
| fprintf(stderr, "- namespace %s", _namespace); | |
| if (schema_loc != NULL) { | |
| fprintf(stderr, " - schema-location %s", schema_loc); | |
| } | |
| if (auxiliary_mimes != NULL) { | |
| fprintf(stderr, " - auxiliary-mime-types %s", auxiliary_mimes); | |
| } | |
| } else if (mtype == GF_ISOM_MEDIA_SUBT) { | |
| fprintf(stderr, "\tQT/3GPP subtitle"); | |
| check_tx3g = GF_TRUE; | |
| } else if (mtype == GF_ISOM_MEDIA_TEXT) { | |
| fprintf(stderr, "\tQT/3GPP text"); | |
| check_tx3g = GF_TRUE; | |
| } else { | |
| fprintf(stderr, "\tUnknown Text Stream"); | |
| } | |
| if (check_tx3g) { | |
| GF_TextSampleDescriptor *txtcfg = NULL; | |
| gf_isom_get_text_description(file, trackNum, stsd_idx, &txtcfg); | |
| if (txtcfg) { | |
| if (txtcfg->displayFlags & GF_TXT_ALL_SAMPLES_FORCED) | |
| fprintf(stderr, " - all samples forced"); | |
| else if (txtcfg->displayFlags & GF_TXT_SOME_SAMPLES_FORCED) | |
| fprintf(stderr, " - forced samples"); | |
| gf_odf_desc_del((GF_Descriptor *) txtcfg); | |
| } | |
| } | |
| fprintf(stderr, "\n\tSize %d x %d - Translation X=%d Y=%d - Layer %d\n", w, h, tx, ty, l); | |
| } else if (mtype == GF_ISOM_MEDIA_META) { | |
| const char *content_encoding = NULL; | |
| if (msub_type == GF_ISOM_SUBTYPE_METT) { | |
| const char *mime = NULL; | |
| const char *config = NULL; | |
| gf_isom_stxt_get_description(file, trackNum, stsd_idx, &mime, &content_encoding, &config); | |
| fprintf(stderr, "\tTextual Metadata Stream - mime %s", mime); | |
| if (content_encoding != NULL) { | |
| fprintf(stderr, " - encoding %s", content_encoding); | |
| } | |
| if (config != NULL) { | |
| fprintf(stderr, " - %d bytes config", (u32) strlen(config)); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else if (msub_type == GF_ISOM_SUBTYPE_METX) { | |
| const char *_namespace = NULL; | |
| const char *schema_loc = NULL; | |
| gf_isom_get_xml_metadata_description(file, trackNum, stsd_idx, &_namespace, &schema_loc, &content_encoding); | |
| fprintf(stderr, "\tXML Metadata Stream - namespace %s", _namespace); | |
| if (content_encoding != NULL) { | |
| fprintf(stderr, " - encoding %s", content_encoding); | |
| } | |
| if (schema_loc != NULL) { | |
| fprintf(stderr, " - schema-location %s", schema_loc); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else { | |
| fprintf(stderr, "\tUnknown Metadata Stream\n"); | |
| } | |
| } else if ((msub_type==GF_ISOM_SUBTYPE_VVC1) || (msub_type==GF_ISOM_SUBTYPE_VVI1)) { | |
| GF_VVCConfig *vvccfg; | |
| u32 w, h; | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| VVCState *vvc_state; | |
| GF_SAFEALLOC(vvc_state, VVCState); | |
| if (vvc_state) vvc_state->sps_active_idx = -1; | |
| #endif | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| if (full_dump) fprintf(stderr, "\t"); | |
| fprintf(stderr, "\tVVC Video - Visual Size %d x %d\n", w, h); | |
| vvccfg = gf_isom_vvc_config_get(file, trackNum, stsd_idx); | |
| if (!vvccfg) { | |
| M4_LOG(GF_LOG_ERROR, ("Non-compliant VVC track: No vvcC found in sample description\n")); | |
| } else { | |
| dump_vvc_track_info(file, trackNum, vvccfg | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| , vvc_state | |
| #endif | |
| ); | |
| gf_odf_vvc_cfg_del(vvccfg); | |
| } | |
| #if !defined(GPAC_DISABLE_AV_PARSERS) | |
| if (vvc_state) gf_free(vvc_state); | |
| #endif | |
| } else if ((msub_type == GF_ISOM_SUBTYPE_MH3D_MHA1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHA2) | |
| || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM2) | |
| ) { | |
| const u8 *compat_profiles; | |
| u32 nb_compat_profiles; | |
| Bool valid = GF_FALSE; | |
| Bool allow_inband = GF_FALSE; | |
| if ( (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM2)) | |
| allow_inband = GF_TRUE; | |
| fprintf(stderr, "\tMPEG-H Audio stream - Sample Rate %d\n", sr); | |
| esd = gf_media_map_esd(file, trackNum, stsd_idx); | |
| if (!esd || !esd->decoderConfig || !esd->decoderConfig->decoderSpecificInfo | |
| || !esd->decoderConfig->decoderSpecificInfo->data | |
| ) { | |
| if (allow_inband) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, NULL); | |
| if (samp) { | |
| u64 ch_layout=0; | |
| s32 PL = gf_mpegh_get_mhas_pl(samp->data, samp->dataLength, &ch_layout); | |
| if (PL>=0) { | |
| fprintf(stderr, "\tProfileLevelIndication: 0x%02X", PL); | |
| if (ch_layout) | |
| fprintf(stderr, " - Reference Channel Layout %s", gf_audio_fmt_get_layout_name(ch_layout) ); | |
| fprintf(stderr, "\n"); | |
| } | |
| gf_isom_sample_del(&samp); | |
| } | |
| #endif | |
| valid = GF_TRUE; | |
| } | |
| } else if (esd->decoderConfig->decoderSpecificInfo->dataLength>=5) { | |
| fprintf(stderr, "\tProfileLevelIndication: 0x%02X - Reference Channel Layout %s\n", esd->decoderConfig->decoderSpecificInfo->data[1] | |
| , gf_audio_fmt_get_layout_name_from_cicp(esd->decoderConfig->decoderSpecificInfo->data[2]) | |
| ); | |
| valid = GF_TRUE; | |
| } | |
| if (!valid) { | |
| M4_LOG(GF_LOG_ERROR, ("Invalid MPEG-H audio config\n")); | |
| } | |
| if (esd) gf_odf_desc_del((GF_Descriptor *)esd); | |
| compat_profiles = gf_isom_get_mpegh_compatible_profiles(file, trackNum, stsd_idx, &nb_compat_profiles); | |
| for (i=0; i<nb_compat_profiles; i++) { | |
| if (!i) | |
| fprintf(stderr, "\tCompatible profiles:"); | |
| fprintf(stderr, " 0x%02X", compat_profiles[i]); | |
| } | |
| if (i) fprintf(stderr, "\n"); | |
| } else if (msub_type==GF_ISOM_SUBTYPE_MLPA) { | |
| u32 fmt, prate; | |
| if (gf_isom_truehd_config_get(file, trackNum, stsd_idx, &fmt, &prate) != GF_OK) { | |
| fprintf(stderr, "\tInvalid TrueHD audio config\n"); | |
| } | |
| fprintf(stderr, "\tTrueHD Audio stream - Sample Rate %u - channels %u - format %u peak rate %u\n", sr, nb_ch, fmt, prate); | |
| } else if (msub_type==GF_QT_SUBTYPE_LPCM) { | |
| Double pcm_sr; | |
| u32 pcm_flags, pcm_size; | |
| if (gf_isom_get_lpcm_config(file, trackNum, stsd_idx, &pcm_sr, &nb_ch, &pcm_flags, &pcm_size) == GF_OK) { | |
| fprintf(stderr, "\tPCM Audio - Sample Rate %f channels %u %u-bits %s %s-endian\n", pcm_sr, nb_ch, pcm_size, (pcm_flags & 1) ? "float" : "int", (pcm_flags & (1<<1)) ? "big" : "little"); | |
| } else { | |
| fprintf(stderr, "\tInvalid LPCM audio config\n"); | |
| } | |
| } else if (codecid) { | |
| if (gf_isom_is_video_handler_type(mtype) ) { | |
| u32 w, h; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\t%s - Resolution %d x %d\n", gf_codecid_name(codecid), w, h); | |
| } else if (mtype==GF_ISOM_MEDIA_AUDIO) { | |
| u32 size, flags; | |
| gf_isom_get_audio_info(file, trackNum, stsd_idx, &sr, &nb_ch, NULL); | |
| if (gf_isom_get_pcm_config(file, trackNum, stsd_idx, &flags, &size)==GF_OK) { | |
| fprintf(stderr, "\tPCM Audio Sample Rate %d %d channel(s) %u-bits %s-endian", sr, nb_ch, size, (flags&1) ? "little" : "big"); | |
| } else { | |
| fprintf(stderr, "\t%s - Sample Rate %d - %d channel(s)", gf_codecid_name(codecid), sr, nb_ch); | |
| } | |
| fprintf(stderr, "\n"); | |
| } else { | |
| fprintf(stderr, "\t%s\n", gf_codecid_name(codecid) ); | |
| } | |
| } else if (pfmt) { | |
| u32 w, h; | |
| gf_isom_get_visual_info(file, trackNum, stsd_idx, &w, &h); | |
| fprintf(stderr, "\tRaw video %s - Resolution %d x %d\n", gf_pixel_fmt_name(pfmt), w, h); | |
| } else if (msub_type==GF_QT_SUBTYPE_TMCD) { | |
| u32 a_stsd_idx; | |
| GF_ISOSample *sample = gf_isom_get_sample(file, trackNum, 1, &a_stsd_idx); | |
| fprintf(stderr, "\tTime Code stream\n"); | |
| if (sample) { | |
| u32 tmcd_flags, tmcd_num, tmcd_den, tmcd_fpt; | |
| gf_isom_get_tmcd_config(file, trackNum, a_stsd_idx, &tmcd_flags, &tmcd_num, &tmcd_den, &tmcd_fpt); | |
| #if !defined(GPAC_DISABLE_INSPECT) | |
| if (sample->data) { | |
| char szTimecode[100]; | |
| gf_inspect_format_timecode(sample->data, sample->dataLength, tmcd_flags, tmcd_num, tmcd_den, tmcd_fpt, szTimecode); | |
| fprintf(stderr, "\tFirst timecode: %s\n", szTimecode); | |
| } | |
| #endif | |
| gf_isom_sample_del(&sample); | |
| } | |
| } else if (msub_type==GF_ISOM_SUBTYPE_OPUS) { | |
| fprintf(stderr, "\tOpus Audio - Sample Rate %d ch %d\n", sr, nb_ch); | |
| } else { | |
| GF_GenericSampleDescription *udesc; | |
| udesc = gf_isom_get_generic_sample_description(file, trackNum, stsd_idx); | |
| if (udesc) { | |
| if (gf_isom_is_video_handler_type(mtype) ) { | |
| fprintf(stderr, "\t%s - Compressor \"%s\" - Resolution %d x %d\n", | |
| ( (mtype == GF_ISOM_MEDIA_VISUAL ? "Visual" : "Auxiliary Video") ), | |
| udesc->compressor_name, udesc->width, udesc->height); | |
| } else if (mtype==GF_ISOM_MEDIA_AUDIO) { | |
| fprintf(stderr, "\tAudio - Sample Rate %d - %d channel(s)\n", udesc->samplerate, udesc->nb_channels); | |
| } else { | |
| fprintf(stderr, "\tUnknown media type\n"); | |
| } | |
| if (udesc->vendor_code) | |
| fprintf(stderr, "\tVendor code \"%s\" - Version %d - revision %d\n", gf_4cc_to_str(udesc->vendor_code), udesc->version, udesc->revision); | |
| if (udesc->extension_buf) { | |
| fprintf(stderr, "\tCodec configuration data size: %d bytes\n", w_dsisize ? w_dsisize : udesc->extension_buf_size); | |
| gf_free(udesc->extension_buf); | |
| } | |
| gf_free(udesc); | |
| } else { | |
| fprintf(stderr, "\tUnknown media type\n"); | |
| } | |
| } | |
| /*Crypto info*/ | |
| if (gf_isom_is_media_encrypted(file, trackNum, stsd_idx)) { | |
| const char *scheme_URI, *KMS_URI; | |
| u32 scheme_type, version; | |
| u32 IV_size; | |
| Bool use_sel_enc; | |
| if (gf_isom_is_ismacryp_media(file, trackNum, stsd_idx)) { | |
| gf_isom_get_ismacryp_info(file, trackNum, stsd_idx, NULL, &scheme_type, &version, &scheme_URI, &KMS_URI, &use_sel_enc, &IV_size, NULL); | |
| fprintf(stderr, "\n\tProtected by ISMA E&A scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version); | |
| if (scheme_URI) fprintf(stderr, "scheme location: %s\n", scheme_URI); | |
| if (KMS_URI) { | |
| if (!strnicmp(KMS_URI, "(key)", 5)) fprintf(stderr, "\tKMS location: key in file\n"); | |
| else fprintf(stderr, "\tKMS location: %s\n", KMS_URI); | |
| } | |
| fprintf(stderr, "\tSelective Encryption: %s\n", use_sel_enc ? "Yes" : "No"); | |
| if (IV_size) fprintf(stderr, "\tInitialization Vector size: %d bits\n", IV_size*8); | |
| } else if (gf_isom_is_omadrm_media(file, trackNum, stsd_idx)) { | |
| const char *textHdrs; | |
| u32 enc_type, hdr_len; | |
| u64 orig_len; | |
| gf_isom_get_omadrm_info(file, trackNum, stsd_idx, NULL, &scheme_type, &version, &scheme_URI, &KMS_URI, &textHdrs, &hdr_len, &orig_len, &enc_type, &use_sel_enc, &IV_size, NULL); | |
| fprintf(stderr, "\n\tProtected by OMA DRM scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version); | |
| fprintf(stderr, "\tRights Issuer: %s\n", KMS_URI); | |
| fprintf(stderr, "\tContent ID: %s\n", scheme_URI); | |
| if (textHdrs) { | |
| u32 offset; | |
| const char *start = textHdrs; | |
| fprintf(stderr, "\tOMA Textual Headers:\n"); | |
| i=0; | |
| offset=0; | |
| while (i<hdr_len) { | |
| if (start[i]==0) { | |
| fprintf(stderr, "\t\t%s\n", start+offset); | |
| offset=i+1; | |
| } | |
| i++; | |
| } | |
| fprintf(stderr, "\\tt%s\n", start+offset); | |
| } | |
| if (orig_len) fprintf(stderr, "\tOriginal media size "LLD"\n", orig_len); | |
| fprintf(stderr, "\tEncryption algorithm %s\n", (enc_type==1) ? "AEA 128 CBC" : (enc_type ? "AEA 128 CTR" : "None")); | |
| fprintf(stderr, "\tSelective Encryption: %s\n", use_sel_enc ? "Yes" : "No"); | |
| if (IV_size) fprintf(stderr, "\tInitialization Vector size: %d bits\n", IV_size*8); | |
| } else if(gf_isom_is_cenc_media(file, trackNum, stsd_idx)) { | |
| const u8 *def_key; | |
| u32 def_key_size; | |
| Bool IsEncrypted; | |
| u32 crypt_byte_block, skip_byte_block; | |
| IV_size = 0; | |
| gf_isom_get_cenc_info(file, trackNum, stsd_idx, NULL, &scheme_type, &version); | |
| gf_isom_cenc_get_default_info(file, trackNum, stsd_idx, NULL, &IsEncrypted, &crypt_byte_block, &skip_byte_block, &def_key, &def_key_size); | |
| fprintf(stderr, "\n\tProtected by CENC scheme %s version 0x%08X", gf_4cc_to_str(scheme_type), version); | |
| if (crypt_byte_block && skip_byte_block) | |
| fprintf(stderr, " - Pattern %d:%d", (u32) skip_byte_block, (u32) crypt_byte_block); | |
| if (def_key && def_key[0]) | |
| fprintf(stderr, " - MultiKey"); | |
| fprintf(stderr, "\n"); | |
| dump_key_info(def_key, def_key_size, IsEncrypted); | |
| } else if(gf_isom_is_adobe_protection_media(file, trackNum, stsd_idx)) { | |
| gf_isom_get_adobe_protection_info(file, trackNum, stsd_idx, NULL, &scheme_type, &version, NULL); | |
| fprintf(stderr, "\nProtected by Adobe scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version); | |
| } else { | |
| fprintf(stderr, "\nProtected by unknown scheme %s\n", gf_4cc_to_str(gf_isom_is_media_encrypted(file, trackNum, 0) )); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| if (gf_media_get_rfc_6381_codec_name(file, trackNum, stsd_idx, szCodec, GF_FALSE, GF_FALSE) == GF_OK) { | |
| fprintf(stderr, "\tRFC6381 Codec Parameters: %s\n", szCodec); | |
| } | |
| } | |
| void DumpTrackInfo(GF_ISOFile *file, GF_ISOTrackID trackID, Bool full_dump, Bool is_track_num, Bool dump_m4sys) | |
| { | |
| Double scale, max_rate, rate; | |
| Bool is_od_track = 0; | |
| u32 trackNum, i, j, ts, mtype, msub_type, timescale, count, alt_group, nb_groups, nb_edits, cdur, csize; | |
| u64 time_slice, dur, size; | |
| s32 cts_shift; | |
| Bool is_extk; | |
| char szDur[100]; | |
| char *lang; | |
| GF_ISOTrackID extk_id; | |
| u32 extk_type, extk_flags; | |
| const char *extk_loc = NULL; | |
| if (!is_track_num) { | |
| trackNum = gf_isom_get_track_by_id(file, trackID); | |
| } else { | |
| trackNum = trackID; | |
| trackID = gf_isom_get_track_id(file, trackNum); | |
| } | |
| if (!trackNum) { | |
| M4_LOG(GF_LOG_ERROR, ("No track with ID %d found\n", trackID)); | |
| return; | |
| } | |
| is_extk = gf_isom_is_external_track(file, trackNum, &extk_id, &extk_type, &extk_flags, &extk_loc); | |
| if (is_extk) { | |
| fprintf(stderr, "# Track %d Info - ID %d\n", trackNum, trackID); | |
| fprintf(stderr, "External Track from %s trackID %d - type %s\n", extk_loc, extk_id, gf_4cc_to_str(extk_type)); | |
| dur = gf_isom_get_track_duration(file, trackNum); | |
| timescale = gf_isom_get_timescale(file); | |
| if (((s32)dur!=-1) || (extk_flags & 1)) | |
| fprintf(stderr, "Track Duration %s\n", format_duration(dur, timescale, szDur)); | |
| else | |
| fprintf(stderr, "Track Duration same as source track\n"); | |
| } else { | |
| timescale = gf_isom_get_media_timescale(file, trackNum); | |
| fprintf(stderr, "# Track %d Info - ID %d - TimeScale %d\n", trackNum, trackID, timescale); | |
| dur = gf_isom_get_media_original_duration(file, trackNum); | |
| size = gf_isom_get_media_duration(file, trackNum); | |
| fprintf(stderr, "Media Duration %s ", format_duration(dur, timescale, szDur)); | |
| if (dur != size) | |
| fprintf(stderr, " (recomputed %s)", format_duration(size, timescale, szDur)); | |
| fprintf(stderr, "\n"); | |
| if (gf_isom_check_data_reference(file, trackNum, 1) != GF_OK) { | |
| M4_LOG(GF_LOG_WARNING, ("Track uses external data reference not supported by GPAC!\n")); | |
| } | |
| } | |
| nb_edits = gf_isom_get_edits_count(file, trackNum); | |
| if (nb_edits) | |
| fprintf(stderr, "Track has %d edits: track duration is %s\n", nb_edits, format_duration(gf_isom_get_track_duration(file, trackNum), gf_isom_get_timescale(file), szDur)); | |
| cts_shift = gf_isom_get_composition_offset_shift(file, trackNum); | |
| if (cts_shift) | |
| fprintf(stderr, "Track composition offset shift (negative CTS offset): %d\n", cts_shift); | |
| if (gf_isom_is_track_in_root_od(file, trackNum) ) fprintf(stderr, "Track is present in Root OD\n"); | |
| u32 tk_flags = gf_isom_get_track_flags(file, trackNum); | |
| fprintf(stderr, "Track flags:"); | |
| if (tk_flags & GF_ISOM_TK_ENABLED) fprintf(stderr, " Enabled"); | |
| else fprintf(stderr, " Disabled"); | |
| if (tk_flags & GF_ISOM_TK_IN_MOVIE) fprintf(stderr, " In Movie"); | |
| if (tk_flags & GF_ISOM_TK_IN_PREVIEW) fprintf(stderr, " In Preview"); | |
| if (tk_flags & GF_ISOM_TK_SIZE_IS_AR) fprintf(stderr, " Size is AspectRatio"); | |
| fprintf(stderr, "\n"); | |
| gf_isom_get_media_language(file, trackNum, &lang); | |
| if (lang && strcmp(lang, "und")) | |
| fprintf(stderr, "Media Language: %s (%s)\n", GetLanguage(lang), lang ); | |
| if (lang) gf_free(lang); | |
| if (is_extk) { | |
| mtype = extk_type; | |
| msub_type = 0; | |
| } else { | |
| mtype = gf_isom_get_media_type(file, trackNum); | |
| msub_type = gf_isom_get_mpeg4_subtype(file, trackNum, 1); | |
| if (!msub_type) msub_type = gf_isom_get_media_subtype(file, trackNum, 1); | |
| } | |
| if (gf_isom_is_track_referenced(file, trackNum, GF_ISOM_REF_CHAP)) { | |
| if (gf_isom_is_video_handler_type(mtype)) fprintf(stderr, "Chapter Thumbnails\n"); | |
| else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT)) fprintf(stderr, "Chapter Labels\n"); | |
| } | |
| if (!is_extk) { | |
| fprintf(stderr, "Media Samples: %d", gf_isom_get_sample_count(file, trackNum)); | |
| cdur = gf_isom_get_constant_sample_duration(file, trackNum); | |
| if (cdur) { | |
| u32 ts = timescale; | |
| gf_media_get_reduced_frame_rate(&ts, &cdur); | |
| if (cdur>1) | |
| fprintf(stderr, " - CFR %f/sec", ((Float)ts)/cdur); | |
| else | |
| fprintf(stderr, " - CFR %u/sec", ts); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| u32 idx=0; | |
| while (1) { | |
| u32 track_group_type, track_group_id; | |
| if (!gf_isom_enum_track_group(file, trackNum, &idx, &track_group_type, &track_group_id)) break; | |
| if (idx==1) fprintf(stderr, "Track Groups:"); | |
| fprintf(stderr, " %s (ID=%d)", gf_4cc_to_str(track_group_type), track_group_id); | |
| } | |
| if (idx) fprintf(stderr, "\n"); | |
| count = gf_isom_get_track_kind_count(file, trackNum); | |
| for (i = 0; i < count; i++) { | |
| char *kind_scheme, *kind_value; | |
| gf_isom_get_track_kind(file, trackNum, i, &kind_scheme, &kind_value); | |
| fprintf(stderr, "Kind: %s - %s\n", kind_scheme ? kind_scheme : "null", kind_value ? kind_value : "null"); | |
| if (kind_scheme) gf_free(kind_scheme); | |
| if (kind_value) gf_free(kind_value); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| if (gf_isom_is_track_fragmented(file, trackID) ) { | |
| u32 defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess; | |
| u8 defaultPadding; | |
| u16 defaultDegradationPriority; | |
| u32 frag_samples; | |
| u64 frag_duration; | |
| u64 tfdt = 0; | |
| u32 j, traf_count = gf_isom_segment_get_track_fragment_count(file, 1); | |
| for (j=0; j<traf_count; j++) { | |
| u32 ID = gf_isom_segment_get_track_fragment_decode_time(file, 1, j+1, &tfdt); | |
| if (ID == trackID) break; | |
| } | |
| gf_isom_get_fragmented_samples_info(file, trackID, &frag_samples, &frag_duration); | |
| fprintf(stderr, "Fragmented track: %d samples - Media Duration %s - First TFDT "LLU"\n", frag_samples, format_duration(frag_duration, timescale, szDur), tfdt); | |
| gf_isom_get_fragment_defaults(file, trackNum, &defaultDuration, &defaultSize, &defaultDescriptionIndex, &defaultRandomAccess, &defaultPadding, &defaultDegradationPriority); | |
| fprintf(stderr, "Fragment sample defaults: duration %d size %d stsd %d sync %d padding %d degradation_priority %d\n", | |
| defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess, | |
| (u32) defaultPadding, (u32) defaultDegradationPriority | |
| ); | |
| } | |
| #endif | |
| if (full_dump) { | |
| if (!is_extk) { | |
| const char *handler_name; | |
| gf_isom_get_handler_name(file, trackNum, &handler_name); | |
| fprintf(stderr, "Handler name: %s\n", handler_name); | |
| } | |
| u32 ridx=0; | |
| while (1) { | |
| u32 rtype, rcount, j; | |
| const GF_ISOTrackID *refs = gf_isom_enum_track_references(file, trackNum, ridx, &rtype, &rcount); | |
| if (!refs) break; | |
| if (!ridx) | |
| fprintf(stderr, "Track References: "); | |
| else | |
| fprintf(stderr, ", "); | |
| ridx++; | |
| fprintf(stderr, "%s to", gf_4cc_to_str(rtype)); | |
| for (j=0; j<rcount; j++) { | |
| fprintf(stderr, " %d", refs[j]); | |
| } | |
| } | |
| if (ridx) | |
| fprintf(stderr, "\n"); | |
| } | |
| print_udta(file, trackNum, GF_TRUE); | |
| DumpMetaItem(file, 0, trackNum, "\tTrack Meta"); | |
| if (gf_isom_is_video_handler_type(mtype) ) { | |
| s32 tx, ty; | |
| u32 w, h; | |
| gf_isom_get_track_layout_info(file, trackNum, &w, &h, &tx, &ty, NULL); | |
| fprintf(stderr, "Visual Track layout: x=%d y=%d width=%d height=%d\n", tx, ty, w, h); | |
| } | |
| gf_isom_get_track_switch_group_count(file, trackNum, &alt_group, &nb_groups); | |
| if (alt_group) { | |
| fprintf(stderr, "Alternate Group ID %d\n", alt_group); | |
| for (i=0; i<nb_groups; i++) { | |
| u32 nb_crit, switchGroupID; | |
| const u32 *criterias = gf_isom_get_track_switch_parameter(file, trackNum, i+1, &switchGroupID, &nb_crit); | |
| if (!nb_crit) { | |
| fprintf(stderr, "\tNo criteria in %s group\n", switchGroupID ? "switch" : "alternate"); | |
| } else { | |
| if (switchGroupID) { | |
| fprintf(stderr, "\tSwitchGroup ID %d criterias: ", switchGroupID); | |
| } else { | |
| fprintf(stderr, "\tAlternate Group criterias: "); | |
| } | |
| for (j=0; j<nb_crit; j++) { | |
| if (j) fprintf(stderr, " "); | |
| fprintf(stderr, "%s", gf_4cc_to_str(criterias[j]) ); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| } | |
| } | |
| if (is_extk) | |
| return; | |
| count = gf_isom_get_sample_description_count(file, trackNum); | |
| if (!full_dump && (count>1)) { | |
| fprintf(stderr, "Sample Descriptions: %d\n\tuse MP4Box -info %d %s to list all sample descriptions\n", count, trackID, gf_isom_get_filename(file)); | |
| count = 1; | |
| } | |
| for (i=0; i<count; i++) { | |
| DumpStsdInfo(file, trackNum, full_dump, dump_m4sys, mtype, i+1, &is_od_track); | |
| } | |
| fprintf(stderr, "\n"); | |
| switch (gf_isom_has_sync_points(file, trackNum)) { | |
| case 0: | |
| fprintf(stderr, "\tAll samples are sync\n"); | |
| break; | |
| case 1: | |
| { | |
| u32 nb_sync = gf_isom_get_sync_point_count(file, trackNum) - 1; | |
| if (! nb_sync) { | |
| fprintf(stderr, "\tOnly one sync sample\n"); | |
| } else { | |
| fprintf(stderr, "\tAverage GOP length: %d samples\n", gf_isom_get_sample_count(file, trackNum) / nb_sync); | |
| } | |
| } | |
| break; | |
| case 2: | |
| fprintf(stderr, "\tNo sync sample found\n"); | |
| break; | |
| } | |
| fprintf(stderr, "\tMax sample duration: %d / %d\n", gf_isom_get_max_sample_delta(file, trackNum), timescale); | |
| if (!full_dump) { | |
| fprintf(stderr, "\n"); | |
| return; | |
| } | |
| dur = size = 0; | |
| max_rate = rate = 0; | |
| time_slice = 0; | |
| ts = gf_isom_get_media_timescale(file, trackNum); | |
| csize = gf_isom_get_constant_sample_size(file, trackNum); | |
| cdur = gf_isom_get_constant_sample_duration(file, trackNum); | |
| count = gf_isom_get_sample_count(file, trackNum); | |
| if (csize && cdur) { | |
| size = count * csize; | |
| dur = cdur * count; | |
| } else { | |
| for (j=0; j<count; j++) { | |
| GF_ISOSample *samp; | |
| if (is_od_track) { | |
| samp = gf_isom_get_sample(file, trackNum, j+1, NULL); | |
| } else { | |
| samp = gf_isom_get_sample_info(file, trackNum, j+1, NULL, NULL); | |
| } | |
| if (!samp) { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to fetch sample %d\n", j+1)); | |
| return; | |
| } | |
| dur = samp->DTS+samp->CTS_Offset; | |
| size += samp->dataLength; | |
| rate += samp->dataLength; | |
| if ((samp->DTS - time_slice > ts) || (j+1==count) ) { | |
| Double max_tmp = rate * ts / (samp->DTS - time_slice); | |
| if (max_rate < max_tmp ) | |
| max_rate = max_tmp; | |
| rate = 0; | |
| time_slice = samp->DTS; | |
| } | |
| gf_isom_sample_del(&samp); | |
| } | |
| } | |
| fprintf(stderr, "\nComputed info from media:\n"); | |
| if (csize && cdur) { | |
| fprintf(stderr, "\tConstant sample size %d bytes and dur %d / %d\n", csize, cdur, ts); | |
| } | |
| scale = 1000.0 / ts; | |
| dur = (u64) (scale * dur); | |
| fprintf(stderr, "\tTotal size "LLU" bytes - Total samples duration "LLU" ms (%s)\n", size, dur, format_duration(dur, 1000, szDur)); | |
| if (!dur) { | |
| fprintf(stderr, "\n"); | |
| return; | |
| } | |
| /*rate in byte, dur is in ms*/ | |
| rate = 8000.0 * size / dur; | |
| if (!max_rate) | |
| max_rate = rate; | |
| else | |
| max_rate *= 8.0; | |
| if (rate >= 1500) { | |
| fprintf(stderr, "\tAverage rate %.2f kbps - Max Rate %.2f kbps\n", rate/1000, max_rate/1000); | |
| } else { | |
| fprintf(stderr, "\tAverage rate %.2f bps - Max Rate %.2f bps\n", rate, max_rate); | |
| } | |
| { | |
| u32 dmin, dmax, davg, smin, smax, savg; | |
| gf_isom_get_chunks_infos(file, trackNum, &dmin, &davg, &dmax, &smin, &savg, &smax); | |
| fprintf(stderr, "\tChunk durations: min %d ms - max %d ms - average %d ms\n", ts ? (1000*dmin)/ts : 0, ts ? (1000*dmax)/ts : 0, ts ? (1000*davg)/ts : 0); | |
| fprintf(stderr, "\tChunk sizes (bytes): min %d - max %d - average %d\n", smin, smax, savg); | |
| } | |
| fprintf(stderr, "\n"); | |
| count = gf_isom_get_chapter_count(file, trackNum); | |
| if (count) { | |
| const char *name; | |
| u64 time; | |
| fprintf(stderr, "\nChapters:\n"); | |
| for (j=0; j<count; j++) { | |
| gf_isom_get_chapter(file, trackNum, j+1, &time, &name); | |
| fprintf(stderr, "\tChapter #%d - %s - \"%s\"\n", j+1, format_duration(time, 1000, szDur), name); | |
| } | |
| } | |
| } | |
| void DumpMovieInfo(GF_ISOFile *file, Bool full_dump) | |
| { | |
| GF_InitialObjectDescriptor *iod; | |
| Bool dump_m4sys = GF_FALSE; | |
| u32 i, brand, min, timescale, count, data_len; | |
| const u8 *data; | |
| u64 create, modif; | |
| Bool has_meta_tags = GF_FALSE; | |
| char szDur[100]; | |
| DumpMetaItem(file, 1, 0, "# File Meta"); | |
| if (!gf_isom_has_movie(file)) { | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| if (gf_isom_has_segment(file, &brand, &min)) { | |
| count = gf_isom_segment_get_fragment_count(file); | |
| fprintf(stderr, "File is a segment - %d movie fragments - Brand %s (version %d):\n", count, gf_4cc_to_str(brand), min); | |
| for (i=0; i<count; i++) { | |
| u32 moof_size, j, traf_count = gf_isom_segment_get_track_fragment_count(file, i+1); | |
| u64 fsize = gf_isom_segment_get_fragment_size(file, i+1, &moof_size); | |
| fprintf(stderr, "\tFragment #%d size "LLU" (moof %d)", i+1, fsize, moof_size); | |
| for (j=0; j<traf_count; j++) { | |
| u32 ID; | |
| u64 tfdt; | |
| ID = gf_isom_segment_get_track_fragment_decode_time(file, i+1, j+1, &tfdt); | |
| fprintf(stderr, "%s {TKID %d TFDT "LLU"}", j ? "," : "", ID, tfdt); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| } else | |
| #endif | |
| { | |
| fprintf(stderr, "File has no movie (moov) - static data container\n"); | |
| } | |
| return; | |
| } | |
| timescale = gf_isom_get_timescale(file); | |
| i=gf_isom_get_track_count(file); | |
| fprintf(stderr, "# Movie Info - %d track%s - TimeScale %d\n", i, i>1 ? "s" : "", timescale); | |
| modif = gf_isom_get_duration(file); | |
| create = gf_isom_get_original_duration(file); | |
| fprintf(stderr, "Duration %s", format_duration(create, timescale, szDur)); | |
| if (create!=modif) { | |
| fprintf(stderr, " (recomputed %s)", format_duration(modif, timescale, szDur)); | |
| } | |
| fprintf(stderr, "\n"); | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| if (gf_isom_is_fragmented(file)) { | |
| fprintf(stderr, "Fragmented: yes - duration %s\n%d fragments - %d SegmentIndexes\n", format_duration(gf_isom_get_fragmented_duration(file), timescale, szDur), gf_isom_get_fragments_count(file, 0) , gf_isom_get_fragments_count(file, 1) ); | |
| } else { | |
| fprintf(stderr, "Fragmented: no\n"); | |
| } | |
| #endif | |
| if (gf_isom_moov_first(file)) | |
| fprintf(stderr, "Progressive (moov before mdat)\n"); | |
| if (gf_isom_get_brand_info(file, &brand, &min, &count) == GF_OK) { | |
| fprintf(stderr, "Major Brand %s - version %d - compatible brands:", gf_4cc_to_str(brand), min); | |
| for (i=0; i<count;i++) { | |
| if (gf_isom_get_alternate_brand(file, i+1, &brand)==GF_OK) | |
| fprintf(stderr, " %s", gf_4cc_to_str(brand) ); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| gf_isom_get_creation_time(file, &create, &modif); | |
| fprintf(stderr, "Created: %s", format_date(create, szDur)); | |
| if (create != modif) | |
| fprintf(stderr, " - Modified: %s", format_date(modif, szDur)); | |
| fprintf(stderr, "\n"); | |
| DumpMetaItem(file, 0, 0, "# Movie Meta"); | |
| iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file); | |
| if (iod) { | |
| u32 desc_size = gf_odf_desc_size((GF_Descriptor *)iod); | |
| if (iod->tag == GF_ODF_IOD_TAG) { | |
| fprintf(stderr, "File has root IOD (%d bytes)\n", desc_size); | |
| fprintf(stderr, "Scene PL 0x%02x - Graphics PL 0x%02x - OD PL 0x%02x\n", iod->scene_profileAndLevel, iod->graphics_profileAndLevel, iod->OD_profileAndLevel); | |
| fprintf(stderr, "Visual PL: %s (0x%02x)\n", gf_m4v_get_profile_name(iod->visual_profileAndLevel), iod->visual_profileAndLevel); | |
| fprintf(stderr, "Audio PL: %s (0x%02x)\n", gf_m4a_get_profile_name(iod->audio_profileAndLevel), iod->audio_profileAndLevel); | |
| //fprintf(stderr, "inline profiles included %s\n", iod->inlineProfileFlag ? "yes" : "no"); | |
| } else { | |
| fprintf(stderr, "File has root OD (%d bytes)\n", desc_size); | |
| } | |
| if (!gf_list_count(iod->ESDescriptors)) | |
| fprintf(stderr, "No streams included in root OD\n"); | |
| else | |
| dump_m4sys = GF_TRUE; | |
| gf_odf_desc_del((GF_Descriptor *) iod); | |
| } | |
| if (gf_isom_is_JPEG2000(file)) fprintf(stderr, "File is JPEG 2000\n"); | |
| count = gf_isom_get_copyright_count(file); | |
| if (count) { | |
| const char *lang, *note; | |
| fprintf(stderr, "\nCopyrights:\n"); | |
| for (i=0; i<count; i++) { | |
| gf_isom_get_copyright(file, i+1, &lang, ¬e); | |
| fprintf(stderr, "\t(%s) %s\n", lang, note); | |
| } | |
| } | |
| count = gf_isom_get_chapter_count(file, 0); | |
| if (count) { | |
| const char *name; | |
| u64 time; | |
| fprintf(stderr, "\nChapters:\n"); | |
| for (i=0; i<count; i++) { | |
| gf_isom_get_chapter(file, 0, i+1, &time, &name); | |
| fprintf(stderr, "\t#%d - %s - \"%s\"\n", i+1, format_duration(time, 1000, szDur), name); | |
| } | |
| } else { | |
| u32 chap_tk=0; | |
| count = gf_isom_get_track_count(file); | |
| for (i=0; i<count; i++) { | |
| u32 nb_ref = gf_isom_get_reference_count(file, i+1, GF_ISOM_REF_CHAP); | |
| if (nb_ref) { | |
| gf_isom_get_reference(file, i+1, GF_ISOM_REF_CHAP, 1, &chap_tk); | |
| break; | |
| } | |
| } | |
| if (chap_tk) { | |
| count = gf_isom_get_sample_count(file, chap_tk); | |
| if (!count) chap_tk=0; | |
| } | |
| if (chap_tk) { | |
| fprintf(stderr, "\nChapter Track:\n"); | |
| for (i=0; i<count; i++) { | |
| u32 di; | |
| GF_ISOSample *s = gf_isom_get_sample(file, chap_tk, i+1, &di); | |
| if (!s) continue; | |
| GF_BitStream *bs = gf_bs_new(s->data, s->dataLength, GF_BITSTREAM_READ); | |
| GF_TextSample *txt = gf_isom_parse_text_sample(bs); | |
| if (txt) { | |
| fprintf(stderr, "\t#%d - %s - \"%s\"\n", i+1, format_duration(s->DTS, 1000, szDur), txt->text ? txt->text : ""); | |
| gf_isom_delete_text_sample(txt); | |
| } | |
| gf_bs_del(bs); | |
| gf_isom_sample_del(&s); | |
| } | |
| } | |
| } | |
| if (gf_isom_apple_get_tag(file, 0, &data, &data_len) == GF_OK) { | |
| has_meta_tags = GF_TRUE; | |
| fprintf(stderr, "\nMeta-Data Tags:\n"); | |
| i=0; | |
| while (1) { | |
| u32 int_val2, flags, itype; | |
| const char *mean=NULL, *name=NULL; | |
| GF_ISOiTunesTag tag; | |
| u64 int_val; | |
| u32 locale=0; | |
| s32 tag_idx; | |
| GF_Err e = gf_isom_apple_enum_tag_ex(file, i, &tag, &data, &data_len, &int_val, &int_val2, &flags, &mean, &name, &locale); | |
| if (e) break; | |
| i++; | |
| tag_idx = gf_itags_find_by_itag(tag); | |
| if (tag_idx<0) { | |
| if (mean && name) | |
| fprintf(stderr, "\t%s (%s): %s\n", name, mean, data); | |
| else if (mean || name) | |
| fprintf(stderr, "\t%s: %s\n", name ? name : mean, data); | |
| else | |
| fprintf(stderr, "\t%s: %s\n", gf_4cc_to_str(tag), data); | |
| continue; | |
| } | |
| fprintf(stderr, "\t%s: ", gf_itags_get_name(tag_idx) ); | |
| itype = gf_itags_get_type(tag_idx); | |
| switch (itype) { | |
| case GF_ITAG_BOOL: fprintf(stderr, int_val ? "yes" : "no"); break; | |
| case GF_ITAG_INT8: | |
| case GF_ITAG_INT16: | |
| case GF_ITAG_INT32: | |
| case GF_ITAG_INT64: | |
| fprintf(stderr, LLU, int_val); | |
| break; | |
| case GF_ITAG_FRAC6: | |
| case GF_ITAG_FRAC8: | |
| fprintf(stderr, LLU" / %u", int_val, int_val2); | |
| break; | |
| case GF_ITAG_FILE: | |
| if (flags==14) fprintf(stderr, "PNG File"); | |
| else if (flags==13) fprintf(stderr, "JPEG File"); | |
| else fprintf(stderr, "unknown (flags %d)", flags); | |
| break; | |
| case GF_ITAG_ID3_GENRE: | |
| if (int_val) { | |
| fprintf(stderr, "%s", gf_id3_get_genre((u32) int_val) ); | |
| break; | |
| } | |
| //fallthrough | |
| default: | |
| if (data) | |
| fprintf(stderr, "%s", data); | |
| else | |
| fprintf(stderr, data_len ? "none" : "unknown"); | |
| break; | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| } | |
| i=0; | |
| while (1) { | |
| GF_QT_UDTAKey key; | |
| u32 j; | |
| GF_Err e = gf_isom_enum_udta_keys(file, i, &key); | |
| if (e) break; | |
| if (!i && !has_meta_tags) { | |
| fprintf(stderr, "\nMeta-Data Tags:\n"); | |
| } | |
| has_meta_tags=GF_TRUE; | |
| i++; | |
| fprintf(stderr, "\t%s", key.name); | |
| if (key.ns && (key.ns!=GF_4CC('m','d','t','a'))) | |
| fprintf(stderr, " (ns %s)", gf_4cc_to_str(key.ns)); | |
| fprintf(stderr, ": "); | |
| switch (key.type) { | |
| case GF_QT_KEY_PNG: fprintf(stderr, "PNG Image (%d bytes)", key.value.data.data_len); break; | |
| case GF_QT_KEY_JPEG: fprintf(stderr, "JPG Image (%d bytes)", key.value.data.data_len); break; | |
| case GF_QT_KEY_BMP: fprintf(stderr, "BMP Image (%d bytes)", key.value.data.data_len); break; | |
| case GF_QT_KEY_UTF8: | |
| case GF_QT_KEY_UTF8_SORT: | |
| fprintf(stderr, "%s", key.value.string ? key.value.string : ""); | |
| break; | |
| case GF_QT_KEY_FLOAT: | |
| case GF_QT_KEY_DOUBLE: | |
| fprintf(stderr, "%g", key.value.number); break; | |
| case GF_QT_KEY_SIGNED_8: | |
| case GF_QT_KEY_SIGNED_16: | |
| case GF_QT_KEY_SIGNED_32: | |
| case GF_QT_KEY_SIGNED_64: | |
| case GF_QT_KEY_SIGNED_VSIZE: | |
| fprintf(stderr, LLD, key.value.sint); break; | |
| case GF_QT_KEY_UNSIGNED_8: | |
| case GF_QT_KEY_UNSIGNED_16: | |
| case GF_QT_KEY_UNSIGNED_32: | |
| case GF_QT_KEY_UNSIGNED_64: | |
| case GF_QT_KEY_UNSIGNED_VSIZE: | |
| fprintf(stderr, LLU, key.value.uint); break; | |
| case GF_QT_KEY_POINTF: | |
| case GF_QT_KEY_SIZEF: | |
| fprintf(stderr, "%gx%g", key.value.pos_size.x, key.value.pos_size.y); break; | |
| case GF_QT_KEY_RECTF: | |
| fprintf(stderr, "%gx%g@%gx%g", key.value.rect.x, key.value.rect.y, key.value.rect.w, key.value.rect.h); break; | |
| case GF_QT_KEY_MATRIXF: | |
| for (j=0; j<9; j++) { | |
| fprintf(stderr, " %g", key.value.matrix[j]); | |
| } | |
| break; | |
| default: | |
| fprintf(stderr, "type %d size %d", key.type, key.value.data.data_len); break; | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| i=0; | |
| while (1) { | |
| u32 type, version; | |
| char *wmatag; | |
| GF_Err e = gf_isom_wma_enum_tag(file, i, &wmatag, &data, &data_len, &version, &type); | |
| if (e) break; | |
| if (!i) { | |
| fprintf(stderr, "\nWMA Info:\n"); | |
| } | |
| i++; | |
| fprintf(stderr, "\t%s", wmatag); | |
| if (version!=1) | |
| fprintf(stderr, " (version %d)", version); | |
| fprintf(stderr, ": "); | |
| if (type) { | |
| fprintf(stderr, "unknown type %d\n", type); | |
| } else { | |
| u16 *src_str = (u16 *) data; | |
| u32 len = UTF8_MAX_BYTES_PER_CHAR * gf_utf8_wcslen(src_str); | |
| char *utf8str = (char *)gf_malloc(len + 1); | |
| u32 res_len = gf_utf8_wcstombs(utf8str, len, (const unsigned short **) &src_str); | |
| if (res_len != GF_UTF8_FAIL) { | |
| utf8str[res_len] = 0; | |
| fprintf(stderr, "%s\n", utf8str); | |
| } | |
| gf_free(utf8str); | |
| } | |
| } | |
| print_udta(file, 0, has_meta_tags); | |
| fprintf(stderr, "\n"); | |
| for (i=0; i<gf_isom_get_track_count(file); i++) { | |
| DumpTrackInfo(file, i+1, full_dump, GF_TRUE, dump_m4sys); | |
| } | |
| } | |
| #endif /*defined(GPAC_DISABLE_ISOM) || defined(GPAC_DISABLE_ISOM_WRITE)*/ | |
| #ifndef GPAC_DISABLE_MPEG2TS | |
| typedef struct | |
| { | |
| /* when writing to file */ | |
| FILE *pes_out; | |
| char dump[100]; | |
| #if 0 | |
| FILE *pes_out_nhml; | |
| char nhml[100]; | |
| FILE *pes_out_info; | |
| char info[100]; | |
| #endif | |
| Bool is_info_dumped; | |
| u32 prog_number; | |
| /* For logging timing information (PCR, PTS/DTS) */ | |
| FILE *timestamps_info_file; | |
| char timestamps_info_name[100]; | |
| /* when dumping TS information */ | |
| u32 dump_pid; | |
| Bool has_seen_pat; | |
| } GF_M2TS_Dump; | |
| static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) | |
| { | |
| u32 i, count; | |
| GF_M2TS_Program *prog; | |
| GF_M2TS_PES_PCK *pck; | |
| GF_M2TS_Dump *dumper = (GF_M2TS_Dump *)ts->user; | |
| switch (evt_type) { | |
| case GF_M2TS_EVT_PAT_FOUND: | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PAT_UPDATE: | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PAT_REPEAT: | |
| /* WARNING: We detect the pat on a repetition, probably to ensure that we also have seen all the PMT | |
| To be checked */ | |
| dumper->has_seen_pat = 1; | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| // fprintf(stderr, "Repeated PAT found - %d programs\n", gf_list_count(ts->programs) ); | |
| break; | |
| case GF_M2TS_EVT_CAT_FOUND: | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_CAT_UPDATE: | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_CAT_REPEAT: | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PMT_FOUND: | |
| prog = (GF_M2TS_Program*)par; | |
| if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number) | |
| break; | |
| count = gf_list_count(prog->streams); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program number %d found - %d streams:\n", prog->number, count)); | |
| for (i=0; i<count; i++) { | |
| GF_M2TS_ES *es = gf_list_get(prog->streams, i); | |
| if (!es) continue; | |
| if (es->pid == prog->pmt_pid) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: Program Map Table\n", es->pid)); | |
| } else { | |
| GF_M2TS_PES *pes = (GF_M2TS_PES *)es; | |
| gf_m2ts_set_pes_framing(pes, dumper->pes_out ? GF_M2TS_PES_FRAMING_RAW : GF_M2TS_PES_FRAMING_DEFAULT); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: %s ", pes->pid, gf_m2ts_get_stream_name(pes->stream_type) )); | |
| if (pes->mpeg4_es_id) GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (" - MPEG-4 ES ID %d", pes->mpeg4_es_id)); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n")); | |
| } | |
| } | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PMT_UPDATE: | |
| prog = (GF_M2TS_Program*)par; | |
| if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number) | |
| break; | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PMT_REPEAT: | |
| prog = (GF_M2TS_Program*)par; | |
| if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number) | |
| break; | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid); | |
| } | |
| break; | |
| case GF_M2TS_EVT_SDT_FOUND: | |
| #ifndef GPAC_DISABLE_LOG | |
| count = gf_list_count(ts->SDTs) ; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description found - %d desc:\n", count)); | |
| for (i=0; i<count; i++) { | |
| GF_M2TS_SDT *sdt = gf_list_get(ts->SDTs, i); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service)); | |
| } | |
| #endif | |
| break; | |
| case GF_M2TS_EVT_SDT_UPDATE: | |
| #ifndef GPAC_DISABLE_LOG | |
| count = gf_list_count(ts->SDTs) ; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description updated - %d desc\n", count)); | |
| for (i=0; i<count; i++) { | |
| GF_M2TS_SDT *sdt = gf_list_get(ts->SDTs, i); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service)); | |
| } | |
| #endif | |
| break; | |
| case GF_M2TS_EVT_SDT_REPEAT: | |
| break; | |
| case GF_M2TS_EVT_PES_TIMING: | |
| pck = par; | |
| if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number) | |
| break; | |
| break; | |
| case GF_M2TS_EVT_PES_PCK: | |
| pck = par; | |
| if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number) | |
| break; | |
| if (dumper->has_seen_pat) { | |
| /*We need the interpolated PCR for the pcrb, hence moved this calculus out, and saving the calculated value in index_info to put it in the pcrb*/ | |
| GF_M2TS_PES *pes = pck->stream; | |
| /*FIXME : not used GF_M2TS_Program *prog = pes->program; */ | |
| /* Interpolated PCR value for the TS packet containing the PES header start */ | |
| u64 interpolated_pcr_value = 0; | |
| if (pes->last_pcr_value && pes->before_last_pcr_value_pck_number && pes->last_pcr_value > pes->before_last_pcr_value) { | |
| u32 delta_pcr_pck_num = pes->last_pcr_value_pck_number - pes->before_last_pcr_value_pck_number; | |
| u32 delta_pts_pcr_pck_num = pes->pes_start_packet_number - pes->last_pcr_value_pck_number; | |
| u64 delta_pcr_value = pes->last_pcr_value - pes->before_last_pcr_value; | |
| if ((pes->pes_start_packet_number > pes->last_pcr_value_pck_number) | |
| && (pes->last_pcr_value > pes->before_last_pcr_value)) { | |
| pes->last_pcr_value = pes->before_last_pcr_value; | |
| } | |
| /* we can compute the interpolated pcr value for the packet containing the PES header */ | |
| interpolated_pcr_value = pes->last_pcr_value + (u64)((delta_pcr_value*delta_pts_pcr_pck_num*1.0)/delta_pcr_pck_num); | |
| } | |
| if (dumper->timestamps_info_file) { | |
| Double diff; | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\t", pck->stream->pes_start_packet_number, pck->stream->pid); | |
| if (interpolated_pcr_value) fprintf(dumper->timestamps_info_file, "%f", interpolated_pcr_value/(300.0 * 90000)); | |
| fprintf(dumper->timestamps_info_file, "\t"); | |
| if (pck->DTS) fprintf(dumper->timestamps_info_file, "%f", (pck->DTS / 90000.0)); | |
| fprintf(dumper->timestamps_info_file, "\t%f\t%d\t%d", pck->PTS / 90000.0, (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0, (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY) ? 1 : 0); | |
| if (interpolated_pcr_value) { | |
| diff = (pck->DTS ? pck->DTS : pck->PTS) / 90000.0; | |
| diff -= pes->last_pcr_value / (300.0 * 90000); | |
| fprintf(dumper->timestamps_info_file, "\t%f\n", diff); | |
| if (diff<0) { | |
| M4_LOG(GF_LOG_WARNING, ("Warning: detected PTS/DTS value less than current PCR of %g sec\n", diff)); | |
| } | |
| } else { | |
| fprintf(dumper->timestamps_info_file, "\t\n"); | |
| } | |
| } | |
| } | |
| if (dumper->has_seen_pat && dumper->pes_out && (dumper->dump_pid == pck->stream->pid)) { | |
| gf_fwrite(pck->data, pck->data_len, dumper->pes_out); | |
| } | |
| break; | |
| case GF_M2TS_EVT_PES_PCR: | |
| pck = par; | |
| if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number) | |
| break; | |
| if (dumper->timestamps_info_file) { | |
| fprintf(dumper->timestamps_info_file, "%u\t%d\t%f\t\t\t\t%d\n", pck->stream->program->last_pcr_value_pck_number, pck->stream->pid, pck->PTS / (300*90000.0), (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY) ? 1 : 0); | |
| } | |
| break; | |
| case GF_M2TS_EVT_SL_PCK: | |
| #if 0 | |
| { | |
| GF_M2TS_SL_PCK *sl_pck = par; | |
| if (dumper->pes_out && (dumper->dump_pid == sl_pck->stream->pid)) { | |
| GF_SLHeader header; | |
| u32 header_len; | |
| if (sl_pck->stream->mpeg4_es_id) { | |
| GF_ESD *esd = ((GF_M2TS_PES*)sl_pck->stream)->esd; | |
| if (!dumper->is_info_dumped) { | |
| if (esd->decoderConfig->decoderSpecificInfo) gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, dumper->pes_out_info); | |
| dumper->is_info_dumped = 1; | |
| fprintf(dumper->pes_out_nhml, "<NHNTStream version=\"1.0\" "); | |
| fprintf(dumper->pes_out_nhml, "timeScale=\"%d\" ", esd->slConfig->timestampResolution); | |
| fprintf(dumper->pes_out_nhml, "streamType=\"%d\" ", esd->decoderConfig->streamType); | |
| fprintf(dumper->pes_out_nhml, "objectTypeIndication=\"%d\" ", esd->decoderConfig->objectTypeIndication); | |
| if (esd->decoderConfig->decoderSpecificInfo) fprintf(dumper->pes_out_nhml, "specificInfoFile=\"%s\" ", dumper->info); | |
| fprintf(dumper->pes_out_nhml, "baseMediaFile=\"%s\" ", dumper->dump); | |
| fprintf(dumper->pes_out_nhml, "inRootOD=\"yes\">\n"); | |
| } | |
| gf_odf_sl_depacketize(esd->slConfig, &header, sl_pck->data, sl_pck->data_len, &header_len); | |
| gf_fwrite(sl_pck->data+header_len, sl_pck->data_len-header_len, dumper->pes_out); | |
| fprintf(dumper->pes_out_nhml, "<NHNTSample DTS=\""LLD"\" dataLength=\"%d\" isRAP=\"%s\"/>\n", header.decodingTimeStamp, sl_pck->data_len-header_len, (header.randomAccessPointFlag?"yes":"no")); | |
| } | |
| } | |
| } | |
| #endif | |
| break; | |
| } | |
| } | |
| void dump_mpeg2_ts(char *mpeg2ts_file, char *out_name, Bool prog_num) | |
| { | |
| u8 data[188]; | |
| GF_M2TS_Dump dumper; | |
| u32 size; | |
| u64 fsize, fdone; | |
| GF_M2TS_Demuxer *ts; | |
| FILE *src; | |
| if (!prog_num && !out_name) { | |
| fprintf(stderr, "No program number nor output filename specified. No timestamp file will be generated\n."); | |
| } | |
| src = gf_fopen(mpeg2ts_file, "rb"); | |
| if (!src) { | |
| M4_LOG(GF_LOG_ERROR, ("Cannot open %s: no such file\n", mpeg2ts_file)); | |
| return; | |
| } | |
| ts = gf_m2ts_demux_new(); | |
| ts->on_event = on_m2ts_dump_event; | |
| ts->notify_pes_timing = 1; | |
| memset(&dumper, 0, sizeof(GF_M2TS_Dump)); | |
| ts->user = &dumper; | |
| dumper.prog_number = prog_num; | |
| /*PES dumping*/ | |
| if (out_name) { | |
| char *pid = strrchr(out_name, '#'); | |
| if (pid) { | |
| dumper.dump_pid = parse_u32(pid+1, "PID"); | |
| pid[0] = 0; | |
| sprintf(dumper.dump, "%s_%d.raw", out_name, dumper.dump_pid); | |
| dumper.pes_out = gf_fopen(dumper.dump, "wb"); | |
| #if 0 | |
| sprintf(dumper.nhml, "%s_%d.nhml", pes_out_name, dumper.dump_pid); | |
| dumper.pes_out_nhml = gf_fopen(dumper.nhml, "wt"); | |
| sprintf(dumper.info, "%s_%d.info", pes_out_name, dumper.dump_pid); | |
| dumper.pes_out_info = gf_fopen(dumper.info, "wb"); | |
| #endif | |
| pid[0] = '#'; | |
| } | |
| } | |
| gf_fseek(src, 0, SEEK_END); | |
| fsize = gf_ftell(src); | |
| gf_fseek(src, 0, SEEK_SET); | |
| /* first loop to process all packets between two PAT, and assume all signaling was found between these 2 PATs */ | |
| while (!feof(src)) { | |
| size = (u32) gf_fread(data, 188, src); | |
| if (size<188) break; | |
| gf_m2ts_process_data(ts, data, size); | |
| if (dumper.has_seen_pat) break; | |
| } | |
| dumper.has_seen_pat = GF_TRUE; | |
| if (!prog_num) { | |
| GF_M2TS_Program *p = gf_list_get(ts->programs, 0); | |
| if (p) prog_num = p->number; | |
| fprintf(stderr, "No program number specified, defaulting to first program\n"); | |
| } | |
| if (!prog_num && !out_name) { | |
| fprintf(stderr, "No program number nor output filename specified. No timestamp file will be generated\n"); | |
| } | |
| if (prog_num) { | |
| snprintf(dumper.timestamps_info_name, sizeof(dumper.timestamps_info_name), "%s_prog_%d_timestamps.txt", mpeg2ts_file, prog_num/*, mpeg2ts_file*/); | |
| dumper.timestamps_info_file = gf_fopen(dumper.timestamps_info_name, "wt"); | |
| if (!dumper.timestamps_info_file) { | |
| M4_LOG(GF_LOG_ERROR, ("Cannot open file %s\n", dumper.timestamps_info_name)); | |
| return; | |
| } | |
| fprintf(dumper.timestamps_info_file, "PCK#\tPID\tPCR\tDTS\tPTS\tRAP\tDiscontinuity\tDTS-PCR Diff\n"); | |
| } | |
| gf_m2ts_reset_parsers(ts); | |
| gf_fseek(src, 0, SEEK_SET); | |
| fdone = 0; | |
| while (!feof(src)) { | |
| size = (u32) gf_fread(data, 188, src); | |
| if (size<188) break; | |
| gf_m2ts_process_data(ts, data, size); | |
| fdone += size; | |
| gf_set_progress("MPEG-2 TS Parsing", fdone, fsize); | |
| } | |
| gf_fclose(src); | |
| gf_m2ts_demux_del(ts); | |
| if (dumper.pes_out) gf_fclose(dumper.pes_out); | |
| #if 0 | |
| if (dumper.pes_out_nhml) { | |
| if (dumper.is_info_dumped) fprintf(dumper.pes_out_nhml, "</NHNTStream>\n"); | |
| gf_fclose(dumper.pes_out_nhml); | |
| gf_fclose(dumper.pes_out_info); | |
| } | |
| #endif | |
| if (dumper.timestamps_info_file) gf_fclose(dumper.timestamps_info_file); | |
| } | |
| #endif /*GPAC_DISABLE_MPEG2TS*/ | |
| #ifndef GPAC_DISABLE_NETWORK | |
| void get_file_callback(void *usr_cbk, GF_NETIO_Parameter *parameter) | |
| { | |
| if (parameter->msg_type==GF_NETIO_DATA_EXCHANGE) { | |
| u64 tot_size, done, max; | |
| u32 bps; | |
| gf_dm_sess_get_stats(parameter->sess, NULL, NULL, &tot_size, &done, &bps, NULL); | |
| if (tot_size) { | |
| max = done; | |
| max *= 100; | |
| max /= tot_size; | |
| fprintf(stderr, "download %02d %% at %05d kpbs\r", (u32) max, bps*8/1000); | |
| } | |
| } | |
| } | |
| static GF_DownloadSession *get_file(const char *url, GF_DownloadManager *dm, GF_Err *e) | |
| { | |
| GF_DownloadSession *sess; | |
| sess = gf_dm_sess_new(dm, url, GF_NETIO_SESSION_NOT_THREADED, get_file_callback, NULL, e); | |
| if (!sess) return NULL; | |
| *e = gf_dm_sess_process(sess); | |
| if (*e) { | |
| gf_dm_sess_del(sess); | |
| return NULL; | |
| } | |
| return sess; | |
| } | |
| static void revert_cache_file(char *item_path) | |
| { | |
| char szPATH[GF_MAX_PATH]; | |
| const char *url; | |
| GF_Config *cached; | |
| if (!strstr(item_path, "gpac_cache_")) { | |
| fprintf(stderr, "%s is not a gpac cache file\n", item_path); | |
| return; | |
| } | |
| if (!strncmp(item_path, "./", 2) || !strncmp(item_path, ".\\", 2)) | |
| item_path += 2; | |
| gf_strcpy(szPATH, item_path); | |
| gf_strcat(szPATH, ".txt"); | |
| cached = gf_cfg_new(NULL, szPATH); | |
| url = gf_cfg_get_key(cached, "cache", "url"); | |
| if (url) url = strstr(url, "://"); | |
| if (url) { | |
| u32 i, len, dir_len=0, k=0; | |
| char *sep; | |
| char *dst_name; | |
| sep = strstr(item_path, "gpac_cache_"); | |
| if (sep) { | |
| sep[0] = 0; | |
| dir_len = (u32) strlen(item_path); | |
| sep[0] = 'g'; | |
| } | |
| url+=3; | |
| len = (u32) strlen(url); | |
| GF_SAFE_ALLOC_N(dst_name, len+dir_len+1, char); | |
| memcpy(dst_name, item_path, dir_len); | |
| dst_name[dir_len]=0; | |
| k=dir_len; | |
| for (i=0; i<len; i++) { | |
| dst_name[k] = url[i]; | |
| if (dst_name[k]==':') dst_name[k]='_'; | |
| else if (dst_name[k]=='/') { | |
| if (!gf_dir_exists(dst_name)) | |
| gf_mkdir(dst_name); | |
| } | |
| k++; | |
| } | |
| if (gf_file_exists(item_path)) { | |
| gf_file_move(item_path, dst_name); | |
| } | |
| gf_free(dst_name); | |
| } else { | |
| M4_LOG(GF_LOG_ERROR, ("Failed to reverse %s cache file\n", item_path)); | |
| } | |
| gf_cfg_del(cached); | |
| gf_file_delete(szPATH); | |
| } | |
| #endif | |
| GF_Err rip_mpd(const char *mpd_src, const char *output_dir) | |
| { | |
| #if !defined(GPAC_DISABLE_NETWORK) && !defined(GPAC_DISABLE_MPD) | |
| GF_DownloadSession *sess; | |
| u32 i, connect_time, reply_time, download_time, req_hdr_size, rsp_hdr_size; | |
| GF_Err e; | |
| GF_DOMParser *mpd_parser=NULL; | |
| GF_MPD *mpd=NULL; | |
| GF_MPD_Period *period; | |
| GF_MPD_AdaptationSet *as; | |
| GF_MPD_Representation *rep; | |
| char szName[GF_MAX_PATH]; | |
| GF_DownloadManager *dm; | |
| if (output_dir) { | |
| char *sep; | |
| gf_strcpy(szName, output_dir); | |
| sep = gf_file_basename(szName); | |
| if (sep) sep[0] = 0; | |
| gf_opts_set_key("temp", "cache", szName); | |
| } else { | |
| gf_opts_set_key("temp", "cache", "."); | |
| } | |
| gf_opts_set_key("temp", "clean-cache", "true"); | |
| dm = gf_dm_new(NULL); | |
| /* | |
| char *name = strrchr(mpd_src, '/'); | |
| if (!name) name = strrchr(mpd_src, '\\'); | |
| if (!name) name = "manifest.mpd"; | |
| else name ++; | |
| if (strchr(name, '?') || strchr(name, '&')) name = "manifest.mpd"; | |
| */ | |
| fprintf(stderr, "Downloading %s\n", mpd_src); | |
| sess = get_file(mpd_src, dm, &e); | |
| if (!sess) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading MPD file %s: %s\n", mpd_src, gf_error_to_string(e) )); | |
| goto err_exit; | |
| } | |
| gf_strcpy(szName, gf_dm_sess_get_cache_name(sess) ); | |
| gf_dm_sess_get_header_sizes_and_times(sess, &req_hdr_size, &rsp_hdr_size, &connect_time, &reply_time, &download_time); | |
| gf_dm_sess_del(sess); | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error fetching MPD file %s: %s\n", mpd_src, gf_error_to_string(e))); | |
| goto err_exit; | |
| } | |
| else { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Fetched file %s\n", mpd_src)); | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("GET Header size %d - Reply header size %d\n", req_hdr_size, rsp_hdr_size)); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("GET time: Connect Time %d - Reply Time %d - Download Time %d\n", connect_time, reply_time, download_time)); | |
| mpd_parser = gf_xml_dom_new(); | |
| e = gf_xml_dom_parse(mpd_parser, szName, NULL, NULL); | |
| if (e != GF_OK) { | |
| gf_xml_dom_del(mpd_parser); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error parsing MPD %s : %s\n", mpd_src, gf_error_to_string(e))); | |
| return e; | |
| } | |
| mpd = gf_mpd_new(); | |
| e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), mpd, mpd_src); | |
| gf_xml_dom_del(mpd_parser); | |
| mpd_parser=NULL; | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error initializing MPD %s : %s\n", mpd_src, gf_error_to_string(e))); | |
| goto err_exit; | |
| } | |
| else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("MPD %s initialized: %s\n", szName, gf_error_to_string(e))); | |
| } | |
| revert_cache_file(szName); | |
| if (mpd->type==GF_MPD_TYPE_DYNAMIC) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("MPD rip is not supported on live sources\n")); | |
| e = GF_NOT_SUPPORTED; | |
| goto err_exit; | |
| } | |
| i=0; | |
| while ((period = (GF_MPD_Period *) gf_list_enum(mpd->periods, &i))) { | |
| char *initTemplate = NULL; | |
| Bool segment_base = GF_FALSE; | |
| u32 j=0; | |
| if (period->segment_base) segment_base=GF_TRUE; | |
| if (period->segment_template && period->segment_template->initialization) { | |
| initTemplate = period->segment_template->initialization; | |
| } | |
| while ((as = gf_list_enum(period->adaptation_sets, &j))) { | |
| u32 k=0; | |
| if (!initTemplate && as->segment_template && as->segment_template->initialization) { | |
| initTemplate = as->segment_template->initialization; | |
| } | |
| if (as->segment_base) segment_base=GF_TRUE; | |
| while ((rep = gf_list_enum(as->representations, &k))) { | |
| u64 out_range_start, out_range_end, segment_duration; | |
| Bool is_in_base_url; | |
| char *seg_url; | |
| u32 seg_idx=0; | |
| if (rep->segment_template && rep->segment_template->initialization) { | |
| initTemplate = rep->segment_template->initialization; | |
| } else if (k>1) { | |
| initTemplate = NULL; | |
| } | |
| if (rep->segment_base) segment_base=GF_TRUE; | |
| e = gf_mpd_resolve_url(mpd, rep, as, period, mpd_src, 0, GF_MPD_RESOLVE_URL_INIT, 0, 0, &seg_url, &out_range_start, &out_range_end, &segment_duration, &is_in_base_url, NULL, NULL, NULL, -1); | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error resolving init segment name : %s\n", gf_error_to_string(e))); | |
| continue; | |
| } | |
| //not a byte range, replace URL | |
| if (segment_base) { | |
| } else if (out_range_start || out_range_end || !seg_url) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("byte range rip not yet implemented\n")); | |
| if (seg_url) gf_free(seg_url); | |
| e = GF_NOT_SUPPORTED; | |
| goto err_exit; | |
| } | |
| fprintf(stderr, "Downloading %s\n", seg_url); | |
| sess = get_file(seg_url, dm, &e); | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading init segment %s from MPD %s : %s\n", seg_url, mpd_src, gf_error_to_string(e))); | |
| goto err_exit; | |
| } | |
| revert_cache_file((char *) gf_dm_sess_get_cache_name(sess) ); | |
| gf_free(seg_url); | |
| gf_dm_sess_del(sess); | |
| if (segment_base) continue; | |
| while (1) { | |
| e = gf_mpd_resolve_url(mpd, rep, as, period, mpd_src, 0, GF_MPD_RESOLVE_URL_MEDIA, seg_idx, 0, &seg_url, &out_range_start, &out_range_end, &segment_duration, NULL, NULL, NULL, NULL, -1); | |
| if (e) { | |
| if (e<0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error resolving segment name : %s\n", gf_error_to_string(e))); | |
| } | |
| break; | |
| } | |
| seg_idx++; | |
| if (out_range_start || out_range_end || !seg_url) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("byte range rip not yet implemented\n")); | |
| if (seg_url) gf_free(seg_url); | |
| break; | |
| } | |
| fprintf(stderr, "Downloading %s\n", seg_url); | |
| sess = get_file(seg_url, dm, &e); | |
| if (e) { | |
| gf_free(seg_url); | |
| if (e != GF_URL_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading segment %s: %s\n", seg_url, gf_error_to_string(e))); | |
| } else { | |
| //todo, properly detect end of dash representation | |
| e = GF_OK; | |
| } | |
| break; | |
| } | |
| revert_cache_file((char *) gf_dm_sess_get_cache_name(sess) ); | |
| gf_free(seg_url); | |
| gf_dm_sess_del(sess); | |
| } | |
| } | |
| } | |
| } | |
| err_exit: | |
| if (mpd) gf_mpd_del(mpd); | |
| gf_dm_del(dm); | |
| return e; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } |