|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2009-2010 John Stebbins
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is free software; you can redistribute it and/or
|
|
Packit |
5e46da |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License as published by the Free Software Foundation; either
|
|
Packit |
5e46da |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
5e46da |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e46da |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
5e46da |
* Lesser General Public License for more details.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License along with this library. If not, see
|
|
Packit |
5e46da |
* <http://www.gnu.org/licenses/>.
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <sys/stat.h>
|
|
Packit |
5e46da |
#include <dirent.h>
|
|
Packit |
5e46da |
#include <stdio.h>
|
|
Packit |
5e46da |
#include <stdlib.h>
|
|
Packit |
5e46da |
#include <unistd.h>
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
#include <libgen.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "bdnav/mpls_data.h"
|
|
Packit |
5e46da |
#include "bluray.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "util.h"
|
|
Packit |
5e46da |
#include "strings.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#ifdef _WIN32
|
|
Packit |
5e46da |
# define DIR_SEP "\\"
|
|
Packit |
5e46da |
# define PLAYLIST_DIR "\\BDMV\\PLAYLIST"
|
|
Packit |
5e46da |
#else
|
|
Packit |
5e46da |
# define DIR_SEP "/"
|
|
Packit |
5e46da |
# define PLAYLIST_DIR "/BDMV/PLAYLIST"
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int verbose;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const VALUE_MAP subpath_type_map[] = {
|
|
Packit |
5e46da |
{2, "Primary audio of the Browsable slideshow"},
|
|
Packit |
5e46da |
{3, "Interactive Graphics presentation menu"},
|
|
Packit |
5e46da |
{4, "Text Subtitle"},
|
|
Packit |
5e46da |
{5, "Out-of-mux Synchronous elementary streams"},
|
|
Packit |
5e46da |
{6, "Out-of-mux Asynchronous Picture-in-Picture presentation"},
|
|
Packit |
5e46da |
{7, "In-mux Synchronous Picture-in-Picture presentation"},
|
|
Packit |
5e46da |
{8, "SS Video"},
|
|
Packit |
5e46da |
{0,NULL}
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const VALUE_MAP playback_type_map[] = {
|
|
Packit |
5e46da |
{1, "Sequential"},
|
|
Packit |
5e46da |
{2, "Random"},
|
|
Packit |
5e46da |
{3, "Shuffle"},
|
|
Packit |
5e46da |
{0, NULL}
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const VALUE_MAP connection_type_map[] = {
|
|
Packit |
5e46da |
{1, "Non-seamless"},
|
|
Packit |
5e46da |
{5, "Seamless"},
|
|
Packit |
5e46da |
{6, "Seamless"},
|
|
Packit |
5e46da |
{0, NULL}
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static char *
|
|
Packit |
5e46da |
_mk_path(const char *base, const char *sub)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
size_t n1 = strlen(base);
|
|
Packit |
5e46da |
size_t n2 = strlen(sub);
|
|
Packit |
5e46da |
char *result = (char*)malloc(n1 + n2 + strlen(DIR_SEP) + 1);
|
|
Packit |
5e46da |
if (result) {
|
|
Packit |
5e46da |
strcpy(result, base);
|
|
Packit |
5e46da |
strcat(result, DIR_SEP);
|
|
Packit |
5e46da |
strcat(result, sub);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_stream(MPLS_STREAM *ss, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
indent_printf(level, "Codec (%04x): %s", ss->coding_type,
|
|
Packit |
5e46da |
_lookup_str(codec_map, ss->coding_type));
|
|
Packit |
5e46da |
switch (ss->stream_type) {
|
|
Packit |
5e46da |
case 1:
|
|
Packit |
5e46da |
indent_printf(level, "PID: %04x", ss->pid);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 2:
|
|
Packit |
5e46da |
case 4:
|
|
Packit |
5e46da |
indent_printf(level, "SubPath Id: %02x", ss->subpath_id);
|
|
Packit |
5e46da |
indent_printf(level, "SubClip Id: %02x", ss->subclip_id);
|
|
Packit |
5e46da |
indent_printf(level, "PID: %04x", ss->pid);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 3:
|
|
Packit |
5e46da |
indent_printf(level, "SubPath Id: %02x", ss->subpath_id);
|
|
Packit |
5e46da |
indent_printf(level, "PID: %04x", ss->pid);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
default:
|
|
Packit |
5e46da |
fprintf(stderr, "unrecognized stream type %02x\n", ss->stream_type);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
switch (ss->coding_type) {
|
|
Packit |
5e46da |
case 0x01:
|
|
Packit |
5e46da |
case 0x02:
|
|
Packit |
5e46da |
case 0xea:
|
|
Packit |
5e46da |
case 0x1b:
|
|
Packit |
5e46da |
case 0x24:
|
|
Packit |
5e46da |
indent_printf(level, "Format %02x: %s", ss->format,
|
|
Packit |
5e46da |
_lookup_str(video_format_map, ss->format));
|
|
Packit |
5e46da |
indent_printf(level, "Rate %02x: %s", ss->rate,
|
|
Packit |
5e46da |
_lookup_str(video_rate_map, ss->rate));
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 0x03:
|
|
Packit |
5e46da |
case 0x04:
|
|
Packit |
5e46da |
case 0x80:
|
|
Packit |
5e46da |
case 0x81:
|
|
Packit |
5e46da |
case 0x82:
|
|
Packit |
5e46da |
case 0x83:
|
|
Packit |
5e46da |
case 0x84:
|
|
Packit |
5e46da |
case 0x85:
|
|
Packit |
5e46da |
case 0x86:
|
|
Packit |
5e46da |
case 0xa1:
|
|
Packit |
5e46da |
case 0xa2:
|
|
Packit |
5e46da |
indent_printf(level, "Format %02x: %s", ss->format,
|
|
Packit |
5e46da |
_lookup_str(audio_format_map, ss->format));
|
|
Packit |
5e46da |
indent_printf(level, "Rate %02x: %s", ss->rate,
|
|
Packit |
5e46da |
_lookup_str(audio_rate_map, ss->rate));
|
|
Packit |
5e46da |
indent_printf(level, "Language: %s", ss->lang);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 0x90:
|
|
Packit |
5e46da |
case 0x91:
|
|
Packit |
5e46da |
indent_printf(level, "Language: %s", ss->lang);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 0x92:
|
|
Packit |
5e46da |
indent_printf(level, "Char Code: %02x", ss->char_code);
|
|
Packit |
5e46da |
indent_printf(level, "Language: %s", ss->lang);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
default:
|
|
Packit |
5e46da |
fprintf(stderr, "unrecognized coding type %02x\n", ss->coding_type);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_details(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii, jj, kk;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->list_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi = &pl->play_item[ii];
|
|
Packit |
5e46da |
indent_printf(level, "Clip Id %s", pi->clip[0].clip_id);
|
|
Packit |
5e46da |
indent_printf(level+1, "Stc Id: %02x", pi->clip[0].stc_id);
|
|
Packit |
5e46da |
indent_printf(level+1, "Connection Condition: %s (%02x)",
|
|
Packit |
5e46da |
_lookup_str(connection_type_map, pi->connection_condition),
|
|
Packit |
5e46da |
pi->connection_condition);
|
|
Packit |
5e46da |
indent_printf(level+1, "In-Time: %d", pi->in_time);
|
|
Packit |
5e46da |
indent_printf(level+1, "Out-Time: %d", pi->out_time);
|
|
Packit |
5e46da |
if (pi->still_mode == 1) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Still time: %ds\n", pi->still_time);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pi->still_mode == 2) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Still time: infinite\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pi->angle_count > 1) {
|
|
Packit |
5e46da |
for (jj = 1; jj < pi->angle_count; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Angle %d:", jj);
|
|
Packit |
5e46da |
indent_printf(level+2, "Clip Id %s", pi->clip[jj].clip_id);
|
|
Packit |
5e46da |
indent_printf(level+2, "Stc Id: %02x", pi->clip[jj].stc_id);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pi->stn.num_video; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Video Stream %d:", jj);
|
|
Packit |
5e46da |
_show_stream(&pi->stn.video[jj], level + 2);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pi->stn.num_audio; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Audio Stream %d:", jj);
|
|
Packit |
5e46da |
_show_stream(&pi->stn.audio[jj], level + 2);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pi->stn.num_ig; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Interactive Graphics Stream %d:", jj);
|
|
Packit |
5e46da |
_show_stream(&pi->stn.ig[jj], level + 2);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < (pi->stn.num_pg + pi->stn.num_pip_pg); jj++) {
|
|
Packit |
5e46da |
if (jj < pi->stn.num_pg) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Presentation Graphics Stream %d:", jj);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
indent_printf(level+1, "PIP Presentation Graphics Stream %d:", jj);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
_show_stream(&pi->stn.pg[jj], level + 2);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pi->stn.num_secondary_video; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Secondary Video Stream %d:", jj);
|
|
Packit |
5e46da |
_show_stream(&pi->stn.secondary_video[jj], level + 2);
|
|
Packit |
5e46da |
for (kk = 0; kk < pi->stn.secondary_video[jj].sv_num_secondary_audio_ref; kk++) {
|
|
Packit |
5e46da |
indent_printf(level+2, "Secondary Audio Ref %d: %d", kk,pi->stn.secondary_video[jj].sv_secondary_audio_ref[kk]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (kk = 0; kk < pi->stn.secondary_video[jj].sv_num_pip_pg_ref; kk++) {
|
|
Packit |
5e46da |
indent_printf(level+2, "PIP Presentation Graphic Ref %d: %d", kk,pi->stn.secondary_video[jj].sv_pip_pg_ref[kk]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pi->stn.num_secondary_audio; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Secondary Audio Stream %d:", jj);
|
|
Packit |
5e46da |
_show_stream(&pi->stn.secondary_audio[jj], level + 2);
|
|
Packit |
5e46da |
for (kk = 0; kk < pi->stn.secondary_audio[jj].sa_num_primary_audio_ref; kk++) {
|
|
Packit |
5e46da |
indent_printf(level+2, "Primary Audio Ref %d: %d", kk,pi->stn.secondary_audio[jj].sa_primary_audio_ref[kk]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
printf("\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_ai(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
indent_printf(level, "Playback type: %s (%d)",
|
|
Packit |
5e46da |
_lookup_str(playback_type_map, pl->app_info.playback_type),
|
|
Packit |
5e46da |
pl->app_info.playback_type);
|
|
Packit |
5e46da |
if (pl->app_info.playback_type == 2 || pl->app_info.playback_type == 3) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Playback count: %d", pl->app_info.playback_count);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_marks(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level, "PlayMark Count %d", pl->mark_count);
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->mark_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
MPLS_PLM *plm;
|
|
Packit |
5e46da |
int min;
|
|
Packit |
5e46da |
double sec;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
plm = &pl->play_mark[ii];
|
|
Packit |
5e46da |
indent_printf(level, "PlayMark %d", ii);
|
|
Packit |
5e46da |
indent_printf(level+1, "Type: %02x", plm->mark_type);
|
|
Packit |
5e46da |
if (plm->play_item_ref < pl->list_count) {
|
|
Packit |
5e46da |
pi = &pl->play_item[plm->play_item_ref];
|
|
Packit |
5e46da |
indent_printf(level+1, "PlayItem: %s", pi->clip[0].clip_id);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
indent_printf(level+1, "PlayItem: Invalid reference");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
indent_printf(level+1, "Time (ticks): %u", plm->time);
|
|
Packit |
5e46da |
min = plm->duration / (45000*60);
|
|
Packit |
5e46da |
sec = (double)(plm->duration - min * 45000 * 60) / 45000;
|
|
Packit |
5e46da |
indent_printf(level+1, "Duration (mm:ss.ms, ticks): %d:%.2f, %u",
|
|
Packit |
5e46da |
min, sec, plm->duration);
|
|
Packit |
5e46da |
printf("\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_clip_list(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii, jj;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->list_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi = &pl->play_item[ii];
|
|
Packit |
5e46da |
if (verbose) {
|
|
Packit |
5e46da |
uint32_t duration;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
duration = pi->out_time - pi->in_time;
|
|
Packit |
5e46da |
indent_printf(level, "%s.m2ts -- Duration: %3d:%02d",
|
|
Packit |
5e46da |
pi->clip[0].clip_id,
|
|
Packit |
5e46da |
duration / (45000 * 60), (duration / 45000) % 60);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
indent_printf(level, "%s.m2ts", pi->clip[0].clip_id);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pi->angle_count > 1) {
|
|
Packit |
5e46da |
for (jj = 1; jj < pi->angle_count; jj++) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Angle %d: %s.m2ts", jj+1, pi->clip[jj].clip_id);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
printf("\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_sub_path(MPLS_SUB *sub, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level+1, "Type: %d (%s)", sub->type, _lookup_str(subpath_type_map, sub->type));
|
|
Packit |
5e46da |
indent_printf(level+1, "Repeat: %d", sub->is_repeat);
|
|
Packit |
5e46da |
indent_printf(level+1, "Sub playitem count: %d", sub->sub_playitem_count);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < sub->sub_playitem_count; ii++) {
|
|
Packit |
5e46da |
MPLS_SUB_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi = &sub->sub_play_item[ii];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (verbose) {
|
|
Packit |
5e46da |
indent_printf(level+1, "Sub playitem %d", ii);
|
|
Packit |
5e46da |
indent_printf(level+2, "Clip Id %s", pi->clip[0].clip_id);
|
|
Packit |
5e46da |
indent_printf(level+2, "Multi clip: %d", pi->is_multi_clip);
|
|
Packit |
5e46da |
indent_printf(level+2, "Clip count: %d", pi->clip_count);
|
|
Packit |
5e46da |
indent_printf(level+2, "Connection Condition: %s (%02x)",
|
|
Packit |
5e46da |
_lookup_str(connection_type_map, pi->connection_condition),
|
|
Packit |
5e46da |
pi->connection_condition);
|
|
Packit |
5e46da |
indent_printf(level+2, "In-Time: %d", pi->in_time);
|
|
Packit |
5e46da |
indent_printf(level+2, "Out-Time: %d", pi->out_time);
|
|
Packit |
5e46da |
indent_printf(level+2, "Sync playitem Id: %d", pi->sync_play_item_id);
|
|
Packit |
5e46da |
indent_printf(level+2, "Sync PTS: %d", pi->sync_pts);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
indent_printf(level+1, "%s.m2ts", pi->clip[0].clip_id);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_pip_metadata_block(MPLS_PIP_METADATA *block, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level, "Clip ref: %d", block->clip_ref);
|
|
Packit |
5e46da |
indent_printf(level, "Secondary video ref: %d", block->secondary_video_ref);
|
|
Packit |
5e46da |
indent_printf(level, "Timeline type: %d", block->timeline_type);
|
|
Packit |
5e46da |
indent_printf(level, "Luma key flag: %d", block->luma_key_flag);
|
|
Packit |
5e46da |
if (block->luma_key_flag) {
|
|
Packit |
5e46da |
indent_printf(level, "Upper limit luma key: %d", block->upper_limit_luma_key);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
indent_printf(level, "Trick play flag: %d", block->trick_play_flag);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < block->data_count; ii++) {
|
|
Packit |
5e46da |
indent_printf(level, "data block %d:", ii);
|
|
Packit |
5e46da |
indent_printf(level+1, "Timestamp: %d", block->data[ii].time);
|
|
Packit |
5e46da |
indent_printf(level+1, "Horizontal position %d", block->data[ii].xpos);
|
|
Packit |
5e46da |
indent_printf(level+1, "Vertical position: %d", block->data[ii].ypos);
|
|
Packit |
5e46da |
indent_printf(level+1, "Scaling factor: %d", block->data[ii].scale_factor);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_pip_metadata(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->ext_pip_data_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PIP_METADATA *data;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
data = &pl->ext_pip_data[ii];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level, "PiP metadata block %d:", ii);
|
|
Packit |
5e46da |
_show_pip_metadata_block(data, level+1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_sub_paths(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ss;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ss = 0; ss < pl->sub_count; ss++) {
|
|
Packit |
5e46da |
MPLS_SUB *sub;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
sub = &pl->sub_path[ss];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level, "Sub Path %d:", ss);
|
|
Packit |
5e46da |
_show_sub_path(sub, level+1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_show_sub_paths_ss(MPLS_PL *pl, int level)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ss;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ss = 0; ss < pl->ext_sub_count; ss++) {
|
|
Packit |
5e46da |
MPLS_SUB *sub;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
sub = &pl->ext_sub_path[ss];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
indent_printf(level, "Extension Sub Path %d:", ss);
|
|
Packit |
5e46da |
_show_sub_path(sub, level+1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static uint32_t
|
|
Packit |
5e46da |
_pl_duration(MPLS_PL *pl)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
uint32_t duration = 0;
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->list_count; ii++) {
|
|
Packit |
5e46da |
pi = &pl->play_item[ii];
|
|
Packit |
5e46da |
duration += pi->out_time - pi->in_time;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return duration;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int
|
|
Packit |
5e46da |
_filter_dup(MPLS_PL *pl_list[], int count, MPLS_PL *pl)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii, jj;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < count; ii++) {
|
|
Packit |
5e46da |
if (pl->list_count != pl_list[ii]->list_count ||
|
|
Packit |
5e46da |
_pl_duration(pl) != _pl_duration(pl_list[ii])) {
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (jj = 0; jj < pl->list_count; jj++) {
|
|
Packit |
5e46da |
MPLS_PI *pi1, *pi2;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi1 = &pl->play_item[jj];
|
|
Packit |
5e46da |
pi2 = &pl_list[ii]->play_item[jj];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (memcmp(pi1->clip[0].clip_id, pi2->clip[0].clip_id, 5) != 0 ||
|
|
Packit |
5e46da |
pi1->in_time != pi2->in_time ||
|
|
Packit |
5e46da |
pi1->out_time != pi2->out_time) {
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (jj != pl->list_count) {
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int
|
|
Packit |
5e46da |
_find_repeats(MPLS_PL *pl, const char *m2ts)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii, count = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->list_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi = &pl->play_item[ii];
|
|
Packit |
5e46da |
// Ignore titles with repeated segments
|
|
Packit |
5e46da |
if (strcmp(pi->clip[0].clip_id, m2ts) == 0) {
|
|
Packit |
5e46da |
count++;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return count;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int
|
|
Packit |
5e46da |
_filter_short(MPLS_PL *pl, unsigned int seconds)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
// Ignore short playlists
|
|
Packit |
5e46da |
if (_pl_duration(pl) / 45000 <= seconds) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int
|
|
Packit |
5e46da |
_filter_repeats(MPLS_PL *pl, int repeats)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < pl->list_count; ii++) {
|
|
Packit |
5e46da |
MPLS_PI *pi;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pi = &pl->play_item[ii];
|
|
Packit |
5e46da |
// Ignore titles with repeated segments
|
|
Packit |
5e46da |
if (_find_repeats(pl, pi->clip[0].clip_id) > repeats) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int clip_list = 0, playlist_info = 0, chapter_marks = 0, sub_paths = 0, pip_metadata = 0;
|
|
Packit |
5e46da |
static int repeats = 0, seconds = 0, dups = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static MPLS_PL*
|
|
Packit |
5e46da |
_process_file(char *name, MPLS_PL *pl_list[], int pl_count)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
MPLS_PL *pl;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pl = bd_read_mpls(name);
|
|
Packit |
5e46da |
if (pl == NULL) {
|
|
Packit |
5e46da |
fprintf(stderr, "Parse failed: %s\n", name);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (seconds) {
|
|
Packit |
5e46da |
if (!_filter_short(pl, seconds)) {
|
|
Packit |
5e46da |
bd_free_mpls(pl);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (repeats) {
|
|
Packit |
5e46da |
if (!_filter_repeats(pl, repeats)) {
|
|
Packit |
5e46da |
bd_free_mpls(pl);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (dups) {
|
|
Packit |
5e46da |
if (!_filter_dup(pl_list, pl_count, pl)) {
|
|
Packit |
5e46da |
bd_free_mpls(pl);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (verbose) {
|
|
Packit |
5e46da |
indent_printf(0,
|
|
Packit |
5e46da |
"%s -- Num Clips: %3d , Duration: minutes %4u:%02u",
|
|
Packit |
5e46da |
basename(name),
|
|
Packit |
5e46da |
pl->list_count,
|
|
Packit |
5e46da |
_pl_duration(pl) / (45000 * 60),
|
|
Packit |
5e46da |
(_pl_duration(pl) / 45000) % 60);
|
|
Packit |
5e46da |
_show_ai(pl, 1);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
indent_printf(0, "%s -- Duration: minutes %4u:%02u",
|
|
Packit |
5e46da |
basename(name),
|
|
Packit |
5e46da |
_pl_duration(pl) / (45000 * 60),
|
|
Packit |
5e46da |
(_pl_duration(pl) / 45000) % 60);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (playlist_info) {
|
|
Packit |
5e46da |
_show_details(pl, 1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (chapter_marks) {
|
|
Packit |
5e46da |
_show_marks(pl, 1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pip_metadata) {
|
|
Packit |
5e46da |
_show_pip_metadata(pl, 1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (clip_list) {
|
|
Packit |
5e46da |
_show_clip_list(pl, 1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (sub_paths) {
|
|
Packit |
5e46da |
_show_sub_paths(pl, 1);
|
|
Packit |
5e46da |
_show_sub_paths_ss(pl, 1);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return pl;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void
|
|
Packit |
5e46da |
_usage(char *cmd)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
fprintf(stderr,
|
|
Packit |
5e46da |
"Usage: %s -vli <mpls file> [<mpls file> ...]\n"
|
|
Packit |
5e46da |
"With no options, produces a list of the playlist(s) with durations\n"
|
|
Packit |
5e46da |
"Options:\n"
|
|
Packit |
5e46da |
" v - Verbose output.\n"
|
|
Packit |
5e46da |
" l - Produces a list of the m2ts clips\n"
|
|
Packit |
5e46da |
" i - Dumps detailed information about each clip\n"
|
|
Packit |
5e46da |
" c - Show chapter marks\n"
|
|
Packit |
5e46da |
" p - Show sub paths\n"
|
|
Packit |
5e46da |
" P - Show picture-in-picture metadata\n"
|
|
Packit |
5e46da |
" r <N> - Filter out titles that have >N repeating clips\n"
|
|
Packit |
5e46da |
" d - Filter out duplicate titles\n"
|
|
Packit |
5e46da |
" s <seconds> - Filter out short titles\n"
|
|
Packit |
5e46da |
" f - Filter combination -r2 -d -s900\n"
|
|
Packit |
5e46da |
, cmd);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
exit(EXIT_FAILURE);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define OPTS "vlicpPfr:ds:"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int
|
|
Packit |
5e46da |
_qsort_str_cmp(const void *a, const void *b)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
const char *stra = *(char * const *)a;
|
|
Packit |
5e46da |
const char *strb = *(char * const *)b;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return strcmp(stra, strb);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int
|
|
Packit |
5e46da |
main(int argc, char *argv[])
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
MPLS_PL *pl;
|
|
Packit |
5e46da |
int opt;
|
|
Packit |
5e46da |
int ii, pl_ii;
|
|
Packit |
5e46da |
MPLS_PL *pl_list[1000];
|
|
Packit |
5e46da |
struct stat st;
|
|
Packit |
5e46da |
char *path = NULL;
|
|
Packit |
5e46da |
DIR *dir = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
do {
|
|
Packit |
5e46da |
opt = getopt(argc, argv, OPTS);
|
|
Packit |
5e46da |
switch (opt) {
|
|
Packit |
5e46da |
case -1:
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'v':
|
|
Packit |
5e46da |
verbose = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'l':
|
|
Packit |
5e46da |
clip_list = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'i':
|
|
Packit |
5e46da |
playlist_info = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'c':
|
|
Packit |
5e46da |
chapter_marks = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'p':
|
|
Packit |
5e46da |
sub_paths = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'P':
|
|
Packit |
5e46da |
pip_metadata = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'd':
|
|
Packit |
5e46da |
dups = 1;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'r':
|
|
Packit |
5e46da |
repeats = atoi(optarg);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 'f':
|
|
Packit |
5e46da |
repeats = 2;
|
|
Packit |
5e46da |
dups = 1;
|
|
Packit |
5e46da |
seconds = 900;
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case 's':
|
|
Packit |
5e46da |
seconds = atoi(optarg);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
default:
|
|
Packit |
5e46da |
_usage(argv[0]);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} while (opt != -1);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (optind >= argc) {
|
|
Packit |
5e46da |
_usage(argv[0]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (pl_ii = 0, ii = optind; pl_ii < 1000 && ii < argc; ii++) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (stat(argv[ii], &st)) {
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
dir = NULL;
|
|
Packit |
5e46da |
if (S_ISDIR(st.st_mode)) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
printf("Directory: %s:\n", argv[ii]);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* drop old ones (do not check for duplicates across directories) */
|
|
Packit |
5e46da |
for (int jj = 0; jj < pl_ii; jj++) {
|
|
Packit |
5e46da |
bd_free_mpls(pl_list[jj]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
pl_ii = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
path = _mk_path(argv[ii], PLAYLIST_DIR);
|
|
Packit |
5e46da |
if (path == NULL) {
|
|
Packit |
5e46da |
fprintf(stderr, "Failed to find playlist path: %s\n", argv[ii]);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
dir = opendir(path);
|
|
Packit |
5e46da |
if (dir == NULL) {
|
|
Packit |
5e46da |
fprintf(stderr, "Failed to open dir: %s\n", path);
|
|
Packit |
5e46da |
free(path);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (dir != NULL) {
|
|
Packit |
5e46da |
char **dirlist = (char**)calloc(10001, sizeof(char*));
|
|
Packit |
5e46da |
if (!dirlist) {
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
struct dirent *ent;
|
|
Packit |
5e46da |
int jj = 0;
|
|
Packit |
5e46da |
for (ent = readdir(dir); ent != NULL && jj < 1000; ent = readdir(dir)) {
|
|
Packit |
5e46da |
char *s = (char*)malloc(strlen(ent->d_name) + 1);
|
|
Packit |
5e46da |
if (s) {
|
|
Packit |
5e46da |
dirlist[jj++] = strcpy(s, ent->d_name);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
qsort(dirlist, jj, sizeof(char*), _qsort_str_cmp);
|
|
Packit |
5e46da |
for (jj = 0; dirlist[jj] != NULL && pl_ii < 1000; jj++) {
|
|
Packit |
5e46da |
char *name = NULL;
|
|
Packit |
5e46da |
name = _mk_path(path, dirlist[jj]);
|
|
Packit |
5e46da |
if (name == NULL) {
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
free(dirlist[jj]);
|
|
Packit |
5e46da |
if (stat(name, &st)) {
|
|
Packit |
5e46da |
free(name);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!S_ISREG(st.st_mode)) {
|
|
Packit |
5e46da |
free(name);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
pl = _process_file(name, pl_list, pl_ii);
|
|
Packit |
5e46da |
free(name);
|
|
Packit |
5e46da |
if (pl != NULL) {
|
|
Packit |
5e46da |
pl_list[pl_ii++] = pl;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
free(dirlist);
|
|
Packit |
5e46da |
free(path);
|
|
Packit |
5e46da |
closedir(dir);
|
|
Packit |
5e46da |
dir = NULL;
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
pl = _process_file(argv[ii], pl_list, pl_ii);
|
|
Packit |
5e46da |
if (pl != NULL) {
|
|
Packit |
5e46da |
pl_list[pl_ii++] = pl;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pl_ii >= 999) {
|
|
Packit |
5e46da |
fprintf(stderr, "Error: too many play lists given. Output is truncated.\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
// Cleanup
|
|
Packit |
5e46da |
for (ii = 0; ii < pl_ii; ii++) {
|
|
Packit |
5e46da |
bd_free_mpls(pl_list[ii]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|