You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
9.9 KiB
338 lines
9.9 KiB
/*
|
|
* probe_ogg.c
|
|
*
|
|
* Copyright (C) Tilmann Bitterberg, July 2002
|
|
* Based heavily on code by Moritz Bunkus for ogminfo from
|
|
* http://www.bunkus.org/videotools/ogmtools/index.html
|
|
*
|
|
* This file is part of transcode, a video stream processing tool
|
|
*
|
|
* transcode is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* transcode 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "transcode.h"
|
|
#include "tcinfo.h"
|
|
#include "ioaux.h"
|
|
#include "tc.h"
|
|
#include "libtc/libtc.h"
|
|
#include "libtc/ratiocodes.h"
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#if (HAVE_OGG && HAVE_VORBIS)
|
|
|
|
#include <ogg/ogg.h>
|
|
#include <vorbis/codec.h>
|
|
|
|
#ifdef HAVE_THEORA
|
|
#include <theora/theora.h>
|
|
#endif
|
|
|
|
#include "ogmstreams.h"
|
|
|
|
#define MAX_AUDIO_TRACKS 255
|
|
#define MAX_VIDEO_TRACKS 255
|
|
#define BLOCK_SIZE 4096
|
|
|
|
//#define OGM_DEBUG
|
|
|
|
struct demux_t {
|
|
int serial;
|
|
int fd;
|
|
int vorbis;
|
|
ogg_stream_state state;
|
|
};
|
|
|
|
enum { none, Vorbis, Theora, DirectShow, StreamHeader };
|
|
|
|
static int ogm_packet_type (ogg_packet pack)
|
|
{
|
|
if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "vorbis", 6))
|
|
return Vorbis;
|
|
else if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "theora", 6))
|
|
return Theora;
|
|
else if ((pack.bytes >= 142) &&
|
|
!strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg", 35) )
|
|
return DirectShow;
|
|
else if (((*pack.packet & OGM_PACKET_TYPE_BITS ) == OGM_PACKET_TYPE_HEADER) &&
|
|
(pack.bytes >= (int)sizeof(ogm_stream_header) + 1))
|
|
return StreamHeader;
|
|
|
|
return none;
|
|
}
|
|
|
|
void probe_ogg(info_t *ipipe)
|
|
{
|
|
ogg_sync_state sync;
|
|
ogg_page page;
|
|
ogg_packet pack;
|
|
char *buf;
|
|
int nread, np, sno, nvtracks = 0, natracks = 0, i, idx;
|
|
//int endofstream = 0, k, n;
|
|
struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS];
|
|
int fdin = -1;
|
|
char vid_codec[5];
|
|
ogm_stream_header *sth;
|
|
|
|
fdin = ipipe->fd_in;
|
|
|
|
if (fdin == -1) {
|
|
tc_log_error(__FILE__, "Could not open file.");
|
|
goto ogg_out;
|
|
}
|
|
|
|
ipipe->probe_info->magic=TC_MAGIC_OGG;
|
|
|
|
memset(streams, 0, sizeof(streams));
|
|
for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++)
|
|
streams[i].serial = -1;
|
|
|
|
ogg_sync_init(&sync);
|
|
|
|
while (1) {
|
|
np = ogg_sync_pageseek(&sync, &page);
|
|
if (np < 0) {
|
|
tc_log_error(__FILE__, "ogg_sync_pageseek failed");
|
|
goto ogg_out;
|
|
}
|
|
if (np == 0) {
|
|
buf = ogg_sync_buffer(&sync, BLOCK_SIZE);
|
|
if (!buf) {
|
|
tc_log_error(__FILE__, "ogg_sync_buffer failed");
|
|
goto ogg_out;
|
|
}
|
|
|
|
if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) {
|
|
}
|
|
ogg_sync_wrote(&sync, nread);
|
|
continue;
|
|
}
|
|
|
|
if (!ogg_page_bos(&page)) {
|
|
break;
|
|
} else {
|
|
ogg_stream_state sstate;
|
|
vorbis_info *inf = tc_malloc (sizeof(vorbis_info));
|
|
vorbis_comment *com = tc_malloc (sizeof(vorbis_comment));
|
|
|
|
if (!inf || !com) {
|
|
tc_log_error(__FILE__, "Out of Memory at %d", __LINE__);
|
|
goto ogg_out;
|
|
}
|
|
sno = ogg_page_serialno(&page);
|
|
if (ogg_stream_init(&sstate, sno)) {
|
|
tc_log_error(__FILE__, "ogg_stream_init failed");
|
|
goto ogg_out;
|
|
}
|
|
ogg_stream_pagein(&sstate, &page);
|
|
ogg_stream_packetout(&sstate, &pack);
|
|
|
|
switch (ogm_packet_type(pack))
|
|
{
|
|
case Vorbis:
|
|
vorbis_info_init(inf);
|
|
vorbis_comment_init(com);
|
|
|
|
if(vorbis_synthesis_headerin(inf, com, &pack) < 0) {
|
|
tc_log_warn(__FILE__, "Could not decode vorbis header "
|
|
"packet - invalid vorbis stream ()");
|
|
} else {
|
|
#ifdef OGM_DEBUG
|
|
tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; "
|
|
"rate: %ldHz, channels: %d, bitrate %3.2f kb/s",
|
|
natracks + 1, natracks + nvtracks + 1, inf->rate,
|
|
inf->channels, (double)inf->bitrate_nominal/1000.0);
|
|
#endif
|
|
|
|
ipipe->probe_info->track[natracks].samplerate = inf->rate;
|
|
ipipe->probe_info->track[natracks].chan = inf->channels;
|
|
ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/
|
|
ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS;
|
|
ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0;
|
|
|
|
ipipe->probe_info->track[natracks].tid=natracks;
|
|
if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks;
|
|
|
|
streams[natracks].serial = sno;
|
|
streams[natracks].vorbis = 1;
|
|
ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate));
|
|
natracks++;
|
|
}
|
|
break;
|
|
#ifdef HAVE_THEORA
|
|
case Theora:
|
|
{
|
|
theora_info ti;
|
|
theora_comment tc;
|
|
|
|
theora_decode_header(&ti, &tc, &pack);
|
|
|
|
ipipe->probe_info->width = ti.width;
|
|
ipipe->probe_info->height = ti.height;
|
|
ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator;
|
|
tc_frc_code_from_ratio(&(ipipe->probe_info->frc),
|
|
ti.fps_numerator, ti.fps_denominator);
|
|
|
|
ipipe->probe_info->codec=TC_CODEC_THEORA;
|
|
|
|
idx = natracks + MAX_AUDIO_TRACKS;
|
|
|
|
streams[idx].serial = sno;
|
|
ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
|
|
nvtracks++;
|
|
break;
|
|
}
|
|
#endif
|
|
case DirectShow:
|
|
if ((*(int32_t*)(pack.packet+96) == 0x05589f80) &&
|
|
(pack.bytes >= 184)) {
|
|
tc_log_warn(__FILE__, "(v%d/%d) Found old video "
|
|
"header. Not supported.", nvtracks + 1,
|
|
natracks + nvtracks + 1);
|
|
} else if (*(int32_t*)pack.packet+96 == 0x05589F81) {
|
|
tc_log_warn(__FILE__, "(a%d/%d) Found old audio "
|
|
"header. Not supported.", natracks + 1,
|
|
natracks + nvtracks + 1);
|
|
}
|
|
break;
|
|
case StreamHeader:
|
|
sth = (ogm_stream_header *)(pack.packet + 1);
|
|
|
|
if (!strncmp(sth->streamtype, "video", 5)) {
|
|
#ifdef OGM_DEBUG
|
|
unsigned long codec;
|
|
codec = (sth->subtype[0] << 24) +
|
|
(sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3];
|
|
tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d "
|
|
"codec: %p (%c%c%c%c)", nvtracks + 1,
|
|
natracks + nvtracks + 1,
|
|
(double)10000000 / (double)sth->time_unit,
|
|
sth->sh.video.width, sth->sh.video.height, (void *)codec,
|
|
sth->subtype[0], sth->subtype[1], sth->subtype[2],
|
|
sth->subtype[3]);
|
|
#endif
|
|
vid_codec[0] = sth->subtype[0];
|
|
vid_codec[1] = sth->subtype[1];
|
|
vid_codec[2] = sth->subtype[2];
|
|
vid_codec[3] = sth->subtype[3];
|
|
vid_codec[4] = '\0';
|
|
|
|
//ipipe->probe_info->frames = AVI_video_frames(avifile);
|
|
|
|
ipipe->probe_info->width = sth->sh.video.width;
|
|
ipipe->probe_info->height = sth->sh.video.height;
|
|
ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit;
|
|
tc_frc_code_from_value(&(ipipe->probe_info->frc),
|
|
ipipe->probe_info->fps);
|
|
|
|
ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten
|
|
|
|
if(strlen(vid_codec)==0) {
|
|
ipipe->probe_info->codec=TC_CODEC_RGB;
|
|
} else {
|
|
|
|
if(strcasecmp(vid_codec,"dvsd")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_DV;
|
|
|
|
if(strcasecmp(vid_codec,"DIV3")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_DIVX3;
|
|
|
|
if(strcasecmp(vid_codec,"DIVX")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_DIVX4;
|
|
|
|
if(strcasecmp(vid_codec,"DX50")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_DIVX5;
|
|
|
|
if(strcasecmp(vid_codec,"XVID")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_XVID;
|
|
|
|
if(strcasecmp(vid_codec,"MJPG")==0)
|
|
ipipe->probe_info->codec=TC_CODEC_MJPEG;
|
|
}
|
|
|
|
idx = natracks + MAX_AUDIO_TRACKS;
|
|
|
|
streams[idx].serial = sno;
|
|
ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
|
|
nvtracks++;
|
|
} else if (!strncmp(sth->streamtype, "audio", 5)) {
|
|
int codec;
|
|
char buf[5];
|
|
ac_memcpy(buf, sth->subtype, 4);
|
|
buf[4] = 0;
|
|
codec = strtoul(buf, NULL, 16);
|
|
#ifdef OGM_DEBUG
|
|
tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per "
|
|
"sample: %d channels: %hd samples per second: %ld "
|
|
"avgbytespersec: %hd blockalign: %d",
|
|
natracks + 1, natracks + nvtracks + 1,
|
|
codec, codec,
|
|
codec == 0x1 ? "PCM" : codec == 55 ? "MP3" :
|
|
codec == 0x55 ? "MP3" :
|
|
codec == 0x2000 ? "AC3" : "unknown",
|
|
sth->bits_per_sample, sth->sh.audio.channels,
|
|
(long)sth->samples_per_unit,
|
|
sth->sh.audio.avgbytespersec,
|
|
sth->sh.audio.blockalign);
|
|
#endif
|
|
idx = natracks;
|
|
|
|
ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit;
|
|
ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels;
|
|
ipipe->probe_info->track[natracks].bits =
|
|
(sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample;
|
|
ipipe->probe_info->track[natracks].format = codec;
|
|
ipipe->probe_info->track[natracks].bitrate = 0;
|
|
|
|
ipipe->probe_info->track[natracks].tid=natracks;
|
|
|
|
|
|
if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks;
|
|
|
|
streams[idx].serial = sno;
|
|
ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
|
|
natracks++;
|
|
} else {
|
|
tc_log_warn(__FILE__, "(%d) found new header of unknown/"
|
|
"unsupported type\n", nvtracks + natracks + 1);
|
|
}
|
|
break;
|
|
case none:
|
|
tc_log_warn(__FILE__, "OGG stream %d is of an unknown type "
|
|
"(bad header?)", nvtracks + natracks + 1);
|
|
break;
|
|
} /* switch type */
|
|
free(inf);
|
|
free(com);
|
|
ogg_stream_clear(&sstate);
|
|
} /* beginning of page */
|
|
} /* while (1) */
|
|
ogg_out:
|
|
//close(fdin);
|
|
return;
|
|
}
|
|
|
|
#else // (HAVE_OGG && HAVE_VORBIS)
|
|
|
|
void probe_ogg(info_t *ipipe)
|
|
{
|
|
tc_log_error(__FILE__, "No support for Ogg/Vorbis compiled in");
|
|
ipipe->probe_info->codec=TC_CODEC_UNKNOWN;
|
|
ipipe->probe_info->magic=TC_MAGIC_UNKNOWN;
|
|
}
|
|
|
|
#endif
|