/* mkvinfo -- utility for gathering information about Matroska files mkvinfo.cpp Written by Moritz Bunkus Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ /*! \file \version \$Id: mkvinfo.cpp,v 1.51 2003/05/29 18:35:19 mosu Exp $ \brief retrieves and displays information about a Matroska file \author Moritz Bunkus */ #include #include #include #include #include #include #include //SLM #ifdef WIN32 #include #else #include #endif #include #ifdef LIBEBML_GCC2 #include #endif extern "C" { #include "avilib/avilib.h" } #include "EbmlHead.h" #include "EbmlSubHead.h" #include "EbmlStream.h" #include "EbmlVoid.h" #include "FileKax.h" #include "KaxAttachements.h" #include "KaxBlock.h" #include "KaxBlockData.h" #include "KaxChapters.h" #include "KaxCluster.h" #include "KaxClusterData.h" #include "KaxCues.h" #include "KaxCuesData.h" #include "KaxInfo.h" #include "KaxInfoData.h" #include "KaxSeekHead.h" #include "KaxSegment.h" #include "KaxTags.h" #include "KaxTracks.h" #include "KaxTrackEntryData.h" #include "KaxTrackAudio.h" #include "KaxTrackVideo.h" #include "mkvinfo.h" #include "common.h" #include "matroska.h" #include "mm_io.h" using namespace LIBMATROSKA_NAMESPACE; using namespace std; typedef struct { unsigned int tnum, tuid; char type; int64_t size; } mkv_track_t; mkv_track_t **tracks = NULL; int num_tracks = 0; bool use_gui = false; void add_track(mkv_track_t *s) { tracks = (mkv_track_t **)saferealloc(tracks, sizeof(mkv_track_t *) * (num_tracks + 1)); tracks[num_tracks] = s; num_tracks++; } mkv_track_t *find_track(int tnum) { int i; for (i = 0; i < num_tracks; i++) if (tracks[i]->tnum == tnum) return tracks[i]; return NULL; } mkv_track_t *find_track_by_uid(int tuid) { int i; for (i = 0; i < num_tracks; i++) if (tracks[i]->tuid == tuid) return tracks[i]; return NULL; } void usage() { fprintf(stdout, "Usage: mkvinfo [options] inname\n\n" " options:\n" #ifdef HAVE_WXWINDOWS " -g, --gui Start the GUI. All other options are ignored.\n" #endif " inname Use 'inname' as the source.\n" " -v, --verbose Increase verbosity. See the man page for a detailed\n" " description of what mkvinfo outputs.\n" " -h, --help Show this help.\n" " -V, --version Show version information.\n"); } #define ARGS_BUFFER_LEN (200 * 1024) // Ok let's be ridiculous here :) static char args_buffer[ARGS_BUFFER_LEN]; void show_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); args_buffer[ARGS_BUFFER_LEN - 1] = 0; vsnprintf(args_buffer, ARGS_BUFFER_LEN - 1, fmt, ap); va_end(ap); #ifdef HAVE_WXWINDOWS if (use_gui) frame->show_error(args_buffer); else #endif fprintf(stdout, "(%s) %s\n", NAME, args_buffer); } void show_element(EbmlElement *l, int level, const char *fmt, ...) { va_list ap; char level_buffer[10]; if (level > 9) die("level > 9: %d", level); va_start(ap, fmt); args_buffer[ARGS_BUFFER_LEN - 1] = 0; vsnprintf(args_buffer, ARGS_BUFFER_LEN - 1, fmt, ap); va_end(ap); if (!use_gui) { memset(&level_buffer[1], ' ', 9); level_buffer[0] = '|'; level_buffer[level] = 0; fprintf(stdout, "(%s) %s+ %s", NAME, level_buffer, args_buffer); if ((verbose > 1) && (l != NULL)) fprintf(stdout, " at %llu", l->GetElementPosition()); fprintf(stdout, "\n"); } #ifdef HAVE_WXWINDOWS else { if (l != NULL) sprintf(&args_buffer[strlen(args_buffer)], " at %llu", l->GetElementPosition()); frame->add_item(level, args_buffer); } #endif // HAVE_WXWINDOWS } #define show_warning(l, f, args...) show_element(NULL, l, f, ## args) #define show_unknown_element(e, l) \ show_element(e, l, "Unknown element: %s", typeid(*e).name()) void parse_args(int argc, char **argv, char *&file_name, bool &use_gui) { int i; verbose = 0; file_name = NULL; use_gui = false; for (i = 1; i < argc; i++) if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--gui")) { #ifndef HAVE_WXWINDOWS fprintf(stderr, "Error: mkvinfo was compiled without GUI support.\n"); exit(1); #else // HAVE_WXWINDOWS use_gui = true; #endif // HAVE_WXWINDOWS } else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) { fprintf(stdout, "mkvinfo v" VERSION "\n"); exit(0); } else if (!strcmp(argv[i], "-v") || ! strcmp(argv[i], "--verbose")) verbose++; else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?") || !strcmp(argv[i], "--help")) { usage(); exit(0); } else if (file_name != NULL) { fprintf(stderr, "Error: Only one input file is allowed.\n"); exit(1); } else file_name = argv[i]; } int is_ebmlvoid(EbmlElement *l, int level) { if (EbmlId(*l) == EbmlVoid::ClassInfos.GlobalId) { show_element(l, level, "EbmlVoid"); return 1; } return 0; } bool process_file(const char *file_name) { int upper_lvl_el, i; // Elements for different levels EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL; EbmlElement *l5 = NULL; EbmlStream *es; KaxCluster *cluster; uint64_t cluster_tc, tc_scale = TIMECODE_SCALE, file_size; char mkv_track_type; bool ms_compat; mm_io_c *in; // open input file try { in = new mm_io_c(file_name, MODE_READ); } catch (std::exception &ex) { show_error("Error: Couldn't open input file %s (%s).\n", file_name, strerror(errno)); return false; } in->setFilePointer(0, seek_end); file_size = in->getFilePointer(); in->setFilePointer(0, seek_beginning); try { es = new EbmlStream(*in); // Find the EbmlHead element. Must be the first one. l0 = es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL); if (l0 == NULL) { show_error("No EBML head found."); delete es; return false; } show_element(l0, 0, "EBML head"); // Don't verify its data for now. l0->SkipData(*es, l0->Generic().Context); delete l0; while (1) { // Next element must be a segment l0 = es->FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFL); if (l0 == NULL) { show_error("No segment/level 0 element found."); return false; } if (EbmlId(*l0) == KaxSegment::ClassInfos.GlobalId) { show_element(l0, 0, "Segment"); break; } show_element(l0, 0, "Next level 0 element is not a segment but %s", typeid(*l0).name()); l0->SkipData(*es, l0->Generic().Context); delete l0; } upper_lvl_el = 0; // We've got our segment, so let's find the tracks l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l1 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l1) == KaxInfo::ClassInfos.GlobalId) { // General info about this Matroska file show_element(l1, 1, "Segment information"); l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l2) == KaxTimecodeScale::ClassInfos.GlobalId) { KaxTimecodeScale &ktc_scale = *static_cast(l2); ktc_scale.ReadData(es->I_O()); tc_scale = uint64(ktc_scale); show_element(l2, 2, "Timecode scale: %llu", tc_scale); } else if (EbmlId(*l2) == KaxDuration::ClassInfos.GlobalId) { KaxDuration &duration = *static_cast(l2); duration.ReadData(es->I_O()); show_element(l2, 2, "Duration: %.3fs", float(duration) * tc_scale / 1000000000.0); } else if (EbmlId(*l2) == KaxMuxingApp::ClassInfos.GlobalId) { KaxMuxingApp &muxingapp = *static_cast(l2); muxingapp.ReadData(es->I_O()); #ifdef NO_WSTRING show_element(l2, 2, "Muxing application: %s", UTFstring(muxingapp).c_str()); #else show_element(l2, 2, "Muxing application: %ls", UTFstring(muxingapp).c_str()); #endif } else if (EbmlId(*l2) == KaxWritingApp::ClassInfos.GlobalId) { KaxWritingApp &writingapp = *static_cast(l2); writingapp.ReadData(es->I_O()); #ifdef NO_WSTRING show_element(l2, 2, "Writing application: %s", UTFstring(writingapp).c_str()); #else show_element(l2, 2, "Writing application: %ls", UTFstring(writingapp).c_str()); #endif } else if (!is_ebmlvoid(l2, 2)) show_unknown_element(l2, 2); if (upper_lvl_el > 0) { // we're coming from l3 upper_lvl_el--; delete l2; l2 = l3; if (upper_lvl_el > 0) break; } else { l2->SkipData(*es, l2->Generic().Context); delete l2; l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } } else if (EbmlId(*l1) == KaxTracks::ClassInfos.GlobalId) { // Yep, we've found our KaxTracks element. Now find all tracks // contained in this segment. show_element(l1, 1, "Segment tracks"); l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l2) == KaxTrackEntry::ClassInfos.GlobalId) { // We actually found a track entry :) We're happy now. show_element(l2, 2, "A track"); mkv_track_type = '?'; ms_compat = false; l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l3 != NULL) { if (upper_lvl_el != 0) break; // Now evaluate the data belonging to this track if (EbmlId(*l3) == KaxTrackNumber::ClassInfos.GlobalId) { KaxTrackNumber &tnum = *static_cast(l3); tnum.ReadData(es->I_O()); show_element(l3, 3, "Track number: %u", uint32(tnum)); if (find_track(uint32(tnum)) != NULL) show_warning(3, "Warning: There's more than one " "track with the number %u.", uint32(tnum)); } else if (EbmlId(*l3) == KaxTrackUID::ClassInfos.GlobalId) { KaxTrackUID &tuid = *static_cast(l3); tuid.ReadData(es->I_O()); show_element(l3, 3, "Track UID: %u", uint32(tuid)); if (find_track_by_uid(uint32(tuid)) != NULL) show_warning(3, "Warning: There's more than one " "track with the UID %u.", uint32(tuid)); } else if (EbmlId(*l3) == KaxTrackType::ClassInfos.GlobalId) { KaxTrackType &ttype = *static_cast(l3); ttype.ReadData(es->I_O()); switch (uint8(ttype)) { case track_audio: mkv_track_type = 'a'; break; case track_video: mkv_track_type = 'v'; break; case track_subtitle: mkv_track_type = 's'; break; default: mkv_track_type = '?'; break; } show_element(l3, 3, "Track type: %s", mkv_track_type == 'a' ? "audio" : mkv_track_type == 'v' ? "video" : mkv_track_type == 's' ? "subtitles" : "unknown"); } else if (EbmlId(*l3) == KaxTrackAudio::ClassInfos.GlobalId) { show_element(l3, 3, "Audio track"); l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l4 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l4) == KaxAudioSamplingFreq::ClassInfos.GlobalId) { KaxAudioSamplingFreq &freq = *static_cast(l4); freq.ReadData(es->I_O()); show_element(l4, 4, "Sampling frequency: %f", float(freq)); } else if (EbmlId(*l4) == KaxAudioChannels::ClassInfos.GlobalId) { KaxAudioChannels &channels = *static_cast(l4); channels.ReadData(es->I_O()); show_element(l4, 4, "Channels: %u", uint8(channels)); } else if (EbmlId(*l4) == KaxAudioBitDepth::ClassInfos.GlobalId) { KaxAudioBitDepth &bps = *static_cast(l4); bps.ReadData(es->I_O()); show_element(l4, 4, "Bit depth: %u", uint8(bps)); } else if (!is_ebmlvoid(l4, 4)) show_unknown_element(l4, 4); l4->SkipData(*es, l4->Generic().Context); delete l4; l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } // while (l4 != NULL) } else if (EbmlId(*l3) == KaxTrackVideo::ClassInfos.GlobalId) { show_element(l3, 3, "Video track"); l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l4 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l4) == KaxVideoPixelWidth::ClassInfos.GlobalId) { KaxVideoPixelWidth &width = *static_cast(l4); width.ReadData(es->I_O()); show_element(l4, 4, "Pixel width: %u", uint16(width)); } else if (EbmlId(*l4) == KaxVideoPixelHeight::ClassInfos.GlobalId) { KaxVideoPixelHeight &height = *static_cast(l4); height.ReadData(es->I_O()); show_element(l4, 4, "Pixel height: %u", uint16(height)); } else if (EbmlId(*l4) == KaxVideoDisplayWidth::ClassInfos.GlobalId) { KaxVideoDisplayWidth &width = *static_cast(l4); width.ReadData(es->I_O()); show_element(l4, 4, "Display width: %u", uint16(width)); } else if (EbmlId(*l4) == KaxVideoDisplayHeight::ClassInfos.GlobalId) { KaxVideoDisplayHeight &height = *static_cast(l4); height.ReadData(es->I_O()); show_element(l4, 4, "Display height: %u", uint16(height)); } else if (EbmlId(*l4) == KaxVideoFrameRate::ClassInfos.GlobalId) { KaxVideoFrameRate &framerate = *static_cast(l4); framerate.ReadData(es->I_O()); show_element(l4, 4, "Frame rate: %f", float(framerate)); } else if (!is_ebmlvoid(l4, 4)) show_unknown_element(l4, 4); l4->SkipData(*es, l4->Generic().Context); delete l4; l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } // while (l4 != NULL) } else if (EbmlId(*l3) == KaxCodecID::ClassInfos.GlobalId) { KaxCodecID &codec_id = *static_cast(l3); codec_id.ReadData(es->I_O()); show_element(l3, 3, "Codec ID: %s", &binary(codec_id)); if ((!strcmp((char *)&binary(codec_id), MKV_V_MSCOMP) && (mkv_track_type == 'v')) || (!strcmp((char *)&binary(codec_id), MKV_A_ACM) && (mkv_track_type == 'a'))) ms_compat = true; } else if (EbmlId(*l3) == KaxCodecPrivate::ClassInfos.GlobalId) { char pbuffer[100]; KaxCodecPrivate &c_priv = *static_cast(l3); c_priv.ReadData(es->I_O()); if (ms_compat && (mkv_track_type == 'v') && (c_priv.GetSize() >= sizeof(BITMAPINFOHEADER))) { BITMAPINFOHEADER *bih = (BITMAPINFOHEADER *)&binary(c_priv); unsigned char *fcc = (unsigned char *)&bih->bi_compression; sprintf(pbuffer, " (FourCC: %c%c%c%c, 0x%08x)", fcc[0], fcc[1], fcc[2], fcc[3], get_uint32(&bih->bi_compression)); } else if (ms_compat && (mkv_track_type == 'a') && (c_priv.GetSize() >= sizeof(WAVEFORMATEX))) { WAVEFORMATEX *wfe = (WAVEFORMATEX *)&binary(c_priv); sprintf(pbuffer, " (format tag: 0x%04x)", get_uint16(&wfe->w_format_tag)); } else pbuffer[0] = 0; show_element(l3, 3, "CodecPrivate, length %llu%s", c_priv.GetSize(), pbuffer); } else if (EbmlId(*l3) == KaxTrackMinCache::ClassInfos.GlobalId) { KaxTrackMinCache &min_cache = *static_cast(l3); min_cache.ReadData(es->I_O()); show_element(l3, 3, "MinCache: %u", uint32(min_cache)); } else if (EbmlId(*l3) == KaxTrackMaxCache::ClassInfos.GlobalId) { KaxTrackMaxCache &max_cache = *static_cast(l3); max_cache.ReadData(es->I_O()); show_element(l3, 3, "MaxCache: %u", uint32(max_cache)); } else if (EbmlId(*l3) == KaxTrackFlagLacing::ClassInfos.GlobalId) { KaxTrackFlagLacing &f_lacing = *static_cast(l3); f_lacing.ReadData(es->I_O()); show_element(l3, 3, "Lacing flag: %d", uint32(f_lacing)); } else if (EbmlId(*l3) == KaxTrackFlagDefault::ClassInfos.GlobalId) { KaxTrackFlagDefault &f_default = *static_cast(l3); f_default.ReadData(es->I_O()); show_element(l3, 3, "Default flag: %d", uint32(f_default)); } else if (EbmlId(*l3) == KaxTrackLanguage::ClassInfos.GlobalId) { KaxTrackLanguage &language = *static_cast(l3); language.ReadData(es->I_O()); show_element(l3, 3, "Language: %s", string(language).c_str()); } else if (!is_ebmlvoid(l3, 3)) show_unknown_element(l3, 3); if (upper_lvl_el > 0) { // we're coming from l4 upper_lvl_el--; delete l3; l3 = l4; if (upper_lvl_el > 0) break; } else { l3->SkipData(*es, l3->Generic().Context); delete l3; l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l3 != NULL) } else if (!is_ebmlvoid(l2, 2)) show_unknown_element(l2, 2); if (upper_lvl_el > 0) { // we're coming from l3 upper_lvl_el--; delete l2; l2 = l3; if (upper_lvl_el > 0) break; } else { l2->SkipData(*es, l2->Generic().Context); delete l2; l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l2 != NULL) } else if (EbmlId(*l1) == KaxSeekHead::ClassInfos.GlobalId) { show_element(l1, 1, "Seek head"); l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l2) == KaxSeek::ClassInfos.GlobalId) { show_element(l2, 2, "Seek entry"); l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l3 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l3) == KaxSeekID::ClassInfos.GlobalId) { binary *b; int s; char pbuffer[100]; KaxSeekID &seek_id = static_cast(*l3); seek_id.ReadData(es->I_O()); b = seek_id.GetBuffer(); s = seek_id.GetSize(); EbmlId id(b, s); pbuffer[0] = 0; for (i = 0; i < s; i++) sprintf(&pbuffer[strlen(pbuffer)], "0x%02x ", ((unsigned char *)b)[i]); show_element(l3, 3, "Seek ID: %s (%s)", pbuffer, (id == KaxInfo::ClassInfos.GlobalId) ? "KaxInfo" : (id == KaxCluster::ClassInfos.GlobalId) ? "KaxCluster" : (id == KaxTracks::ClassInfos.GlobalId) ? "KaxTracks" : (id == KaxCues::ClassInfos.GlobalId) ? "KaxCues" : (id == KaxAttachements::ClassInfos.GlobalId) ? "KaxAttachements" : (id == KaxChapters::ClassInfos.GlobalId) ? "KaxChapters" : (id == KaxTags::ClassInfos.GlobalId) ? "KaxTags" : "unknown"); } else if (EbmlId(*l3) == KaxSeekPosition::ClassInfos.GlobalId) { KaxSeekPosition &seek_pos = static_cast(*l3); seek_pos.ReadData(es->I_O()); show_element(l3, 3, "Seek position: %llu", uint64(seek_pos)); } else if (!is_ebmlvoid(l3, 3)) show_unknown_element(l3, 3); l3->SkipData(*es, l3->Generic().Context); delete l3; l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } // while (l3 != NULL) } else if (!is_ebmlvoid(l2, 2)) show_unknown_element(l2, 2); if (upper_lvl_el > 0) { // we're coming from l3 upper_lvl_el--; delete l2; l2 = l3; if (upper_lvl_el > 0) break; } else { l2->SkipData(*es, l2->Generic().Context); delete l2; l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l2 != NULL) } else if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) { show_element(l1, 1, "Cluster"); if (verbose == 0) { delete l1; delete l0; delete es; delete in; return true; } cluster = (KaxCluster *)l1; #ifdef HAVE_WXWINDOWS frame->show_progress(100 * cluster->GetElementPosition() / file_size, "Parsing file"); #endif // HAVE_WXWINDOWS l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l2) == KaxClusterTimecode::ClassInfos.GlobalId) { KaxClusterTimecode &ctc = *static_cast(l2); ctc.ReadData(es->I_O()); cluster_tc = uint64(ctc); show_element(l2, 2, "Cluster timecode: %.3fs", (float)cluster_tc * (float)tc_scale / 1000000000.0); cluster->InitTimecode(cluster_tc); } else if (EbmlId(*l2) == KaxBlockGroup::ClassInfos.GlobalId) { show_element(l2, 2, "Block group"); l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, false, 1); while (l3 != NULL) { if (upper_lvl_el > 0) break; if (EbmlId(*l3) == KaxBlock::ClassInfos.GlobalId) { KaxBlock &block = *static_cast(l3); block.ReadData(es->I_O()); block.SetParent(*cluster); show_element(l3, 3, "Block (track number %u, %d frame(s), " "timecode %.3fs)", block.TrackNum(), block.NumberFrames(), (float)block.GlobalTimecode() / 1000000000.0); for (i = 0; i < (int)block.NumberFrames(); i++) { DataBuffer &data = block.GetBuffer(i); show_element(NULL, 4, "Frame with size %u", data.Size()); } } else if (EbmlId(*l3) == KaxBlockDuration::ClassInfos.GlobalId) { KaxBlockDuration &duration = *static_cast(l3); duration.ReadData(es->I_O()); show_element(l3, 3, "Block duration: %.3fms", ((float)uint64(duration)) * tc_scale / 1000000.0); } else if (EbmlId(*l3) == KaxReferenceBlock::ClassInfos.GlobalId) { KaxReferenceBlock &reference = *static_cast(l3); reference.ReadData(es->I_O()); show_element(l3, 3, "Reference block: %.3fms", ((float)int64(reference)) * tc_scale / 1000000.0); } else if (!is_ebmlvoid(l3, 3)) show_unknown_element(l3, 3); if (upper_lvl_el > 0) { // we're coming from l4 upper_lvl_el--; delete l3; l3 = l4; if (upper_lvl_el > 0) break; } else { l3->SkipData(*es, l3->Generic().Context); delete l3; l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l3 != NULL) } else if (!is_ebmlvoid(l2, 2)) show_unknown_element(l2, 2); if (upper_lvl_el > 0) { // we're coming from l3 upper_lvl_el--; delete l2; l2 = l3; if (upper_lvl_el > 0) break; } else { l2->SkipData(*es, l2->Generic().Context); delete l2; l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l2 != NULL) } else if (EbmlId(*l1) == KaxCues::ClassInfos.GlobalId) { show_element(l1, 1, "Cues"); l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l2) == KaxCuePoint::ClassInfos.GlobalId) { show_element(l2, 2, "Cue point"); l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l3 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l3) == KaxCueTime::ClassInfos.GlobalId) { KaxCueTime &cue_time = *static_cast(l3); cue_time.ReadData(es->I_O()); show_element(l3, 3, "Cue time: %.3fs", tc_scale * ((float)uint64(cue_time)) / 1000000000.0); } else if (EbmlId(*l3) == KaxCueTrackPositions::ClassInfos.GlobalId) { show_element(l3, 3, "Cue track positions"); l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l4 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l4) == KaxCueTrack::ClassInfos.GlobalId) { KaxCueTrack &cue_track = *static_cast(l4); cue_track.ReadData(es->I_O()); show_element(l4, 4, "Cue track: %u", uint32(cue_track)); } else if (EbmlId(*l4) == KaxCueClusterPosition::ClassInfos.GlobalId) { KaxCueClusterPosition &cue_cp = *static_cast(l4); cue_cp.ReadData(es->I_O()); show_element(l4, 4, "Cue cluster position: %llu", uint64(cue_cp)); } else if (EbmlId(*l4) == KaxCueBlockNumber::ClassInfos.GlobalId) { KaxCueBlockNumber &cue_bn = *static_cast(l4); cue_bn.ReadData(es->I_O()); show_element(l4, 4, "Cue block number: %llu", uint64(cue_bn)); } else if (EbmlId(*l4) == KaxCueCodecState::ClassInfos.GlobalId) { KaxCueCodecState &cue_cs = *static_cast(l4); cue_cs.ReadData(es->I_O()); show_element(l4, 4, "Cue codec state: %llu", uint64(cue_cs)); } else if (EbmlId(*l4) == KaxCueReference::ClassInfos.GlobalId) { show_element(l4, 4, "Cue reference"); l5 = es->FindNextElement(l4->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l5 != NULL) { if (upper_lvl_el != 0) break; if (EbmlId(*l5) == KaxCueRefTime::ClassInfos.GlobalId) { KaxCueRefTime &cue_rt = *static_cast(l5); show_element(l5, 5, "Cue ref time: %.3fs", tc_scale, ((float)uint64(cue_rt)) / 1000000000.0); } else if (EbmlId(*l5) == KaxCueRefCluster::ClassInfos.GlobalId) { KaxCueRefCluster &cue_rc = *static_cast(l5); cue_rc.ReadData(es->I_O()); show_element(l5, 5, "Cue ref cluster: %llu", uint64(cue_rc)); } else if (EbmlId(*l5) == KaxCueRefNumber::ClassInfos.GlobalId) { KaxCueRefNumber &cue_rn = *static_cast(l5); cue_rn.ReadData(es->I_O()); show_element(l5, 5, "Cue ref number: %llu", uint64(cue_rn)); } else if (EbmlId(*l5) == KaxCueRefCodecState::ClassInfos.GlobalId) { KaxCueRefCodecState &cue_rcs = *static_cast(l5); cue_rcs.ReadData(es->I_O()); show_element(l5, 5, "Cue ref codec state: %llu", uint64(cue_rcs)); } else if (!is_ebmlvoid(l5, 5)) show_unknown_element(l5, 5); l5->SkipData(*es, l5->Generic().Context); delete l5; l5 = es->FindNextElement(l4->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } // while (l5 != NULL) } else if (!is_ebmlvoid(l4, 4)) show_unknown_element(l4, 4); if (upper_lvl_el > 0) { // we're coming from l5 upper_lvl_el--; delete l4; l4 = l5; if (upper_lvl_el > 0) break; } else { l4->SkipData(*es, l4->Generic().Context); delete l4; l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l4 != NULL) } else if (!is_ebmlvoid(l3, 3)) show_unknown_element(l3, 3); if (upper_lvl_el > 0) { // we're coming from l4 upper_lvl_el--; delete l3; l3 = l4; if (upper_lvl_el > 0) break; } else { l3->SkipData(*es, l3->Generic().Context); delete l3; l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l3 != NULL) } else if (!is_ebmlvoid(l2, 2)) show_unknown_element(l2, 2); if (upper_lvl_el > 0) { // we're coming from l3 upper_lvl_el--; delete l2; l2 = l3; if (upper_lvl_el > 0) break; } else { l2->SkipData(*es, l2->Generic().Context); delete l2; l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l2 != NULL) } else if (EbmlId(*l1) == KaxTags::ClassInfos.GlobalId) { show_element(l1, 1, "Tags (skipping all subelements!)"); } else if (!is_ebmlvoid(l1, 1)) show_unknown_element(l1, 1); if (upper_lvl_el > 0) { // we're coming from l2 upper_lvl_el--; delete l1; l1 = l2; if (upper_lvl_el > 0) break; } else { l1->SkipData(*es, l1->Generic().Context); delete l1; l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); } } // while (l1 != NULL) delete l0; delete es; delete in; return true; } catch (std::exception &ex) { show_error("Caught exception: %s", ex.what()); delete in; return false; } } int console_main(int argc, char **argv) { char *file_name; nice(2); parse_args(argc, argv, file_name, use_gui); if (file_name == NULL) { usage(); exit(1); } if (process_file(file_name)) return 0; else return 1; } #ifndef HAVE_WXWINDOWS int main(int argc, char **argv) { return console_main(argc, argv); } #endif // HAVE_WXWINDOWS