Commit ef68bdea authored by Philipp Schafft's avatar Philipp Schafft 🦁
Browse files

Update: Moved handlers for all codecs out of ogginfo2.c

parent 5c6a19d1
## Process this file with automake to produce Makefile.in
mans = ogginfo.1
ogginfosources = ogginfo2.c theora.c codec_other.c
ogginfosources = \
ogginfo2.c \
theora.c \
codec_vorbis.c \
codec_theora.c \
codec_kate.c \
codec_other.c \
codec_invalid.c
noinst_HEADERS = \
private.h \
......
/* Ogginfo
*
* A tool to describe ogg file contents and metadata.
*
* This file handles codecs we have no specific handling for.
*
* Copyright 2002-2005 Michael Smith <msmith@xiph.org>
* Copyright 2020 Philipp Schafft <lion@lion.leolix.org>
* Licensed under the GNU GPL, distributed with this program.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ogg/ogg.h>
#include "private.h"
static void process_invalid(stream_processor *stream, ogg_page *page)
{
/* This is for invalid streams. */
}
void invalid_start(stream_processor *stream)
{
stream->process_end = NULL;
stream->type = "invalid";
stream->process_page = process_invalid;
}
/* Ogginfo
*
* A tool to describe ogg file contents and metadata.
*
* This file handles codecs we have no specific handling for.
*
* Copyright 2002-2005 Michael Smith <msmith@xiph.org>
* Copyright 2020 Philipp Schafft <lion@lion.leolix.org>
* Licensed under the GNU GPL, distributed with this program.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ogg/ogg.h>
#ifdef HAVE_KATE
#include <kate/oggkate.h>
#endif
#include "i18n.h"
#include "private.h"
typedef struct {
#ifdef HAVE_KATE
kate_info ki;
kate_comment kc;
#else
int num_headers;
#endif
int major;
int minor;
char language[16];
char category[16];
ogg_int64_t bytes;
ogg_int64_t lastgranulepos;
ogg_int64_t firstgranulepos;
int doneheaders;
} misc_kate_info;
static void kate_process(stream_processor *stream, ogg_page *page )
{
ogg_packet packet;
misc_kate_info *inf = stream->data;
int header=0, packets=0;
int res;
#ifdef HAVE_KATE
int i;
const char *encoding = NULL, *directionality = NULL;
#endif
ogg_stream_pagein(&stream->os, page);
if (!inf->doneheaders)
header = 1;
while (1) {
res = ogg_stream_packetout(&stream->os, &packet);
if (res < 0) {
warn(_("WARNING: discontinuity in stream (%d)\n"), stream->num);
continue;
} else if (res == 0) {
break;
}
packets++;
if (!inf->doneheaders) {
#ifdef HAVE_KATE
int ret = kate_ogg_decode_headerin(&inf->ki, &inf->kc, &packet);
if (ret < 0) {
warn(_("WARNING: Could not decode Kate header "
"packet %d - invalid Kate stream (%d)\n"),
packet.packetno, stream->num);
continue;
} else if (ret > 0) {
inf->doneheaders=1;
}
#else
/* if we're not building against libkate, do some limited checks */
if (packet.bytes<64 || memcmp(packet.packet+1, "kate\0\0\0", 7)) {
warn(_("WARNING: packet %d does not seem to be a Kate header - "
"invalid Kate stream (%d)\n"),
packet.packetno, stream->num);
continue;
}
if (packet.packetno==inf->num_headers) {
inf->doneheaders=1;
}
#endif
if (packet.packetno==0) {
#ifdef HAVE_KATE
inf->major = inf->ki.bitstream_version_major;
inf->minor = inf->ki.bitstream_version_minor;
memcpy(inf->language, inf->ki.language, 16);
inf->language[15] = 0;
memcpy(inf->category, inf->ki.category, 16);
inf->category[15] = 0;
#else
inf->major = packet.packet[9];
inf->minor = packet.packet[10];
inf->num_headers = packet.packet[11];
memcpy(inf->language, packet.packet+32, 16);
inf->language[15] = 0;
memcpy(inf->category, packet.packet+48, 16);
inf->category[15] = 0;
#endif
}
if (inf->doneheaders) {
if (ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1)
warn(_("WARNING: Kate stream %d does not have headers "
"correctly framed. Terminal header page contains "
"additional packets or has non-zero granulepos\n"),
stream->num);
info(_("Kate headers parsed for stream %d, "
"information follows...\n"), stream->num);
info(_("Version: %d.%d\n"), inf->major, inf->minor);
#ifdef HAVE_KATE
info(_("Vendor: %s\n"), inf->kc.vendor);
#endif
if (*inf->language) {
info(_("Language: %s\n"), inf->language);
} else {
info(_("No language set\n"));
}
if (*inf->category) {
info(_("Category: %s\n"), inf->category);
} else {
info(_("No category set\n"));
}
#ifdef HAVE_KATE
switch (inf->ki.text_encoding) {
case kate_utf8: encoding=_("utf-8"); break;
default: encoding=NULL; break;
}
if (encoding) {
info(_("Character encoding: %s\n"),encoding);
} else {
info(_("Unknown character encoding\n"));
}
if (printlots) {
switch (inf->ki.text_directionality) {
case kate_l2r_t2b: directionality=_("left to right, top to bottom"); break;
case kate_r2l_t2b: directionality=_("right to left, top to bottom"); break;
case kate_t2b_r2l: directionality=_("top to bottom, right to left"); break;
case kate_t2b_l2r: directionality=_("top to bottom, left to right"); break;
default: directionality=NULL; break;
}
if (directionality) {
info(_("Text directionality: %s\n"),directionality);
} else {
info(_("Unknown text directionality\n"));
}
info("%u regions, %u styles, %u curves, %u motions, %u palettes,\n"
"%u bitmaps, %u font ranges, %u font mappings\n",
inf->ki.nregions, inf->ki.nstyles,
inf->ki.ncurves, inf->ki.nmotions,
inf->ki.npalettes, inf->ki.nbitmaps,
inf->ki.nfont_ranges, inf->ki.nfont_mappings);
}
if (inf->ki.gps_numerator == 0 || inf->ki.gps_denominator == 0) {
warn(_("Invalid zero granulepos rate\n"));
} else {
info(_("Granulepos rate %d/%d (%.02f gps)\n"),
inf->ki.gps_numerator, inf->ki.gps_denominator,
(float)inf->ki.gps_numerator/(float)inf->ki.gps_denominator);
}
if (inf->kc.comments > 0)
info(_("User comments section follows...\n"));
for (i=0; i < inf->kc.comments; i++) {
const char *comment = inf->kc.user_comments[i];
check_xiph_comment(stream, i, comment,
inf->kc.comment_lengths[i]);
}
#endif
info(_("\n"));
}
}
}
if (!header) {
ogg_int64_t gp = ogg_page_granulepos(page);
if (gp > 0) {
if (gp < inf->lastgranulepos) {
warn(_("WARNING: granulepos in stream %d decreases from %"
PRId64 " to %" PRId64 "\n" ),
stream->num, inf->lastgranulepos, gp);
}
inf->lastgranulepos = gp;
} else if (packets && gp<0) { /* zero granpos on data is valid for kate */
/* Only do this if we saw at least one packet ending on this page.
* It's legal (though very unusual) to have no packets in a page at
* all - this is occasionally used to have an empty EOS page */
warn(_("Negative granulepos (%" PRId64 ") on Kate stream outside of headers. This file was created by a buggy encoder\n"), gp);
}
if (inf->firstgranulepos < 0) { /* Not set yet */
}
inf->bytes += page->header_len + page->body_len;
}
}
#ifdef HAVE_KATE
static void kate_end(stream_processor *stream)
{
misc_kate_info *inf = stream->data;
long minutes, seconds, milliseconds;
double bitrate, time;
/* This should be lastgranulepos - startgranulepos, or something like that*/
//time = (double)(inf->lastgranulepos>>inf->ki.granule_shift) * inf->ki.gps_denominator / inf->ki.gps_numerator;
ogg_int64_t gbase=inf->lastgranulepos>>inf->ki.granule_shift;
ogg_int64_t goffset=inf->lastgranulepos-(gbase<<inf->ki.granule_shift);
time = (double)(gbase+goffset) / ((float)inf->ki.gps_numerator/(float)inf->ki.gps_denominator);
minutes = (long)time / 60;
seconds = (long)time - minutes*60;
milliseconds = (long)((time - minutes*60 - seconds)*1000);
bitrate = inf->bytes*8 / time / 1000.0;
info(_("Kate stream %d:\n"
"\tTotal data length: %" PRId64 " bytes\n"
"\tPlayback length: %ldm:%02ld.%03lds\n"
"\tAverage bitrate: %f kb/s\n"),
stream->num,inf->bytes, minutes, seconds, milliseconds, bitrate);
kate_comment_clear(&inf->kc);
kate_info_clear(&inf->ki);
free(stream->data);
}
#else
static void kate_end(stream_processor *stream)
{
}
#endif
void kate_start(stream_processor *stream)
{
misc_kate_info *info;
stream->type = "kate";
stream->process_page = kate_process;
stream->process_end = kate_end;
stream->data = calloc(1, sizeof(misc_kate_info));
info = stream->data;
#ifdef HAVE_KATE
kate_comment_init(&info->kc);
kate_info_init(&info->ki);
#endif
}
/* Ogginfo
*
* A tool to describe ogg file contents and metadata.
*
* This file handles codecs we have no specific handling for.
*
* Copyright 2002-2005 Michael Smith <msmith@xiph.org>
* Copyright 2020 Philipp Schafft <lion@lion.leolix.org>
* Licensed under the GNU GPL, distributed with this program.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <math.h>
#include <ogg/ogg.h>
#include "i18n.h"
#include "theora.h"
#include "private.h"
typedef struct {
theora_info ti;
theora_comment tc;
ogg_int64_t bytes;
ogg_int64_t lastgranulepos;
ogg_int64_t firstgranulepos;
int doneheaders;
ogg_int64_t framenum_expected;
} misc_theora_info;
static void theora_process(stream_processor *stream, ogg_page *page)
{
ogg_packet packet;
misc_theora_info *inf = stream->data;
int i, header=0;
int res;
ogg_stream_pagein(&stream->os, page);
if (inf->doneheaders < 3)
header = 1;
while (1) {
res = ogg_stream_packetout(&stream->os, &packet);
if (res < 0) {
warn(_("WARNING: discontinuity in stream (%d)\n"), stream->num);
continue;
} else if (res == 0) {
break;
}
if (inf->doneheaders < 3) {
if (theora_decode_header(&inf->ti, &inf->tc, &packet) < 0) {
warn(_("WARNING: Could not decode Theora header "
"packet - invalid Theora stream (%d)\n"), stream->num);
continue;
}
inf->doneheaders++;
if (inf->doneheaders == 3) {
if (ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1)
warn(_("WARNING: Theora stream %d does not have headers "
"correctly framed. Terminal header page contains "
"additional packets or has non-zero granulepos\n"),
stream->num);
info(_("Theora headers parsed for stream %d, "
"information follows...\n"), stream->num);
info(_("Version: %d.%d.%d\n"), inf->ti.version_major, inf->ti.version_minor, inf->ti.version_subminor);
info(_("Vendor: %s\n"), inf->tc.vendor);
info(_("Width: %d\n"), inf->ti.frame_width);
info(_("Height: %d\n"), inf->ti.frame_height);
info(_("Total image: %d by %d, crop offset (%d, %d)\n"),
inf->ti.width, inf->ti.height, inf->ti.offset_x, inf->ti.offset_y);
if (inf->ti.offset_x + inf->ti.frame_width > inf->ti.width)
warn(_("Frame offset/size invalid: width incorrect\n"));
if (inf->ti.offset_y + inf->ti.frame_height > inf->ti.height)
warn(_("Frame offset/size invalid: height incorrect\n"));
if (inf->ti.fps_numerator == 0 || inf->ti.fps_denominator == 0) {
warn(_("Invalid zero framerate\n"));
} else {
info(_("Framerate %d/%d (%.02f fps)\n"), inf->ti.fps_numerator, inf->ti.fps_denominator, (float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator);
}
if (inf->ti.aspect_numerator == 0 || inf->ti.aspect_denominator == 0) {
info(_("Aspect ratio undefined\n"));
} else {
float frameaspect = (float)inf->ti.frame_width/(float)inf->ti.frame_height * (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator;
info(_("Pixel aspect ratio %d:%d (%f:1)\n"), inf->ti.aspect_numerator, inf->ti.aspect_denominator, (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator);
if (fabs(frameaspect - 4.0/3.0) < 0.02)
info(_("Frame aspect 4:3\n"));
else if (fabs(frameaspect - 16.0/9.0) < 0.02)
info(_("Frame aspect 16:9\n"));
else
info(_("Frame aspect %f:1\n"), frameaspect);
}
if (inf->ti.colorspace == OC_CS_ITU_REC_470M)
info(_("Colourspace: Rec. ITU-R BT.470-6 System M (NTSC)\n"));
else if (inf->ti.colorspace == OC_CS_ITU_REC_470BG)
info(_("Colourspace: Rec. ITU-R BT.470-6 Systems B and G (PAL)\n"));
else
info(_("Colourspace unspecified\n"));
if (inf->ti.pixelformat == OC_PF_420)
info(_("Pixel format 4:2:0\n"));
else if (inf->ti.pixelformat == OC_PF_422)
info(_("Pixel format 4:2:2\n"));
else if (inf->ti.pixelformat == OC_PF_444)
info(_("Pixel format 4:4:4\n"));
else
warn(_("Pixel format invalid\n"));
info(_("Target bitrate: %d kbps\n"), inf->ti.target_bitrate/1000);
info(_("Nominal quality setting (0-63): %d\n"), inf->ti.quality);
if (inf->tc.comments > 0)
info(_("User comments section follows...\n"));
for (i=0; i < inf->tc.comments; i++) {
char *comment = inf->tc.user_comments[i];
check_xiph_comment(stream, i, comment,
inf->tc.comment_lengths[i]);
}
}
}
else {
ogg_int64_t framenum;
ogg_int64_t iframe,pframe;
ogg_int64_t gp = packet.granulepos;
if (gp > 0) {
iframe=gp>>inf->ti.granule_shift;
pframe=gp-(iframe<<inf->ti.granule_shift);
framenum = iframe+pframe;
if (inf->framenum_expected >= 0 &&
inf->framenum_expected != framenum)
{
warn(_("WARNING: Expected frame %" PRId64
", got %" PRId64 "\n"),
inf->framenum_expected, framenum);
}
inf->framenum_expected = framenum + 1;
} else if (inf->framenum_expected >= 0) {
inf->framenum_expected++;
}
}
}
if (!header) {
ogg_int64_t gp = ogg_page_granulepos(page);
if (gp > 0) {
if (gp < inf->lastgranulepos)
warn(_("WARNING: granulepos in stream %d decreases from %"
PRId64 " to %" PRId64 "\n"),
stream->num, inf->lastgranulepos, gp);
inf->lastgranulepos = gp;
}
if (inf->firstgranulepos < 0) { /* Not set yet */
}
inf->bytes += page->header_len + page->body_len;
}
}
static void theora_end(stream_processor *stream)
{
misc_theora_info *inf = stream->data;
long minutes, seconds, milliseconds;
double bitrate, time;
int new_gp;
new_gp = inf->ti.version_major > 3
|| (inf->ti.version_major == 3 && (inf->ti.version_minor > 2
|| (inf->ti.version_minor == 2 && inf->ti.version_subminor > 0)));
/* This should be lastgranulepos - startgranulepos, or something like that*/
ogg_int64_t iframe=inf->lastgranulepos>>inf->ti.granule_shift;
ogg_int64_t pframe=inf->lastgranulepos-(iframe<<inf->ti.granule_shift);
/* The granule position starts at 0 for stream version 3.2.0, but starts at
1 for version 3.2.1 and above. In the former case, we need to add one
to the final granule position to get the frame count. */
time = (double)(iframe+pframe+!new_gp) /
((float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator);
minutes = (long)time / 60;
seconds = (long)time - minutes*60;
milliseconds = (long)((time - minutes*60 - seconds)*1000);
bitrate = inf->bytes*8 / time / 1000.0;
info(_("Theora stream %d:\n"
"\tTotal data length: %" PRId64 " bytes\n"
"\tPlayback length: %ldm:%02ld.%03lds\n"
"\tAverage bitrate: %f kb/s\n"),
stream->num,inf->bytes, minutes, seconds, milliseconds, bitrate);
theora_comment_clear(&inf->tc);
theora_info_clear(&inf->ti);
free(stream->data);
}
void theora_start(stream_processor *stream)
{
misc_theora_info *info;
stream->type = "theora";
stream->process_page = theora_process;
stream->process_end = theora_end;
stream->data = calloc(1, sizeof(misc_theora_info));
info = stream->data;
info->framenum_expected = -1;
}
/* Ogginfo
*
* A tool to describe ogg file contents and metadata.
*
* This file handles codecs we have no specific handling for.
*
* Copyright 2002-2005 Michael Smith <msmith@xiph.org>
* Copyright 2020 Philipp Schafft <lion@lion.leolix.org>
* Licensed under the GNU GPL, distributed with this program.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ogg/ogg.h>
#include <vorbis/codec.h>
#include "i18n.h"
#include "private.h"
typedef struct {
vorbis_info vi;
vorbis_comment vc;
ogg_int64_t bytes;
ogg_int64_t lastgranulepos;
ogg_int64_t firstgranulepos;
int doneheaders;
} misc_vorbis_info;
static const struct vorbis_release {
const char *vendor_string;
const char *desc;
} releases[] = {
{"Xiphophorus libVorbis I 20000508", "1.0 beta 1 or beta 2"},
{"Xiphophorus libVorbis I 20001031", "1.0 beta 3"},
{"Xiphophorus libVorbis I 20010225", "1.0 beta 4"},
{"Xiphophorus libVorbis I 20010615", "1.0 rc1"},
{"Xiphophorus libVorbis I 20010813", "1.0 rc2"},
{"Xiphophorus libVorbis I 20011217", "1.0 rc3"},
{"Xiphophorus libVorbis I 20011231", "1.0 rc3"},
{"Xiph.Org libVorbis I 20020717", "1.0"},
{"Xiph.Org libVorbis I 20030909", "1.0.1"},
{"Xiph.Org libVorbis I 20040629", "1.1.0"},
{"Xiph.Org libVorbis I 20050304", "1.1.1"},
{"Xiph.Org libVorbis I 20050304", "1.1.2"},
{"Xiph.Org libVorbis I 20070622", "1.2.0"},
{"Xiph.Org libVorbis I 20080501", "1.2.1"},
{NULL, NULL},
};
static void vorbis_process(stream_processor *stream, ogg_page *page )
{
ogg_packet packet;
misc_vorbis_info *inf = stream->data;
int i, header=0, packets=0;
int k;
int res;
ogg_stream_pagein(&stream->os, page);
if (inf->doneheaders < 3)
<