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.
685 lines
22 KiB
685 lines
22 KiB
4 years ago
|
/*
|
||
|
ogminfo -- utility for extracting raw streams from an OGG media file
|
||
|
|
||
|
Written by Moritz Bunkus <moritz@bunkus.org>
|
||
|
Integrated into transcode by Tilmann Bitterberg <transcode@tibit.org>
|
||
|
|
||
|
Distributed under the GPL
|
||
|
see the file COPYING for details
|
||
|
or visit http://www.gnu.org/copyleft/gpl.html
|
||
|
*/
|
||
|
|
||
|
#include "transcode.h"
|
||
|
#include "libtc/libtc.h"
|
||
|
#include "tcinfo.h"
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#include "ioaux.h"
|
||
|
#include "tc.h"
|
||
|
|
||
|
#define NOAUDIO 0
|
||
|
#define NOVIDEO 1
|
||
|
#define NOTEXT 2
|
||
|
|
||
|
unsigned char *xaudio = NULL;
|
||
|
unsigned char *xvideo = NULL;
|
||
|
unsigned char *xtext = NULL;
|
||
|
int no[3];
|
||
|
int xraw = 0;
|
||
|
|
||
|
static int verbose_flag = TC_QUIET;
|
||
|
|
||
|
#if (HAVE_OGG && HAVE_VORBIS)
|
||
|
|
||
|
#include <ogg/ogg.h>
|
||
|
#include <vorbis/codec.h>
|
||
|
|
||
|
#include "ogmstreams.h"
|
||
|
|
||
|
#define BLOCK_SIZE 4096
|
||
|
|
||
|
#define ACVORBIS 0xffff
|
||
|
#define ACPCM 0x0001
|
||
|
#define ACMP3 0x0055
|
||
|
#define ACAC3 0x2000
|
||
|
|
||
|
typedef struct stream_t {
|
||
|
int serial;
|
||
|
int fd;
|
||
|
double sample_rate;
|
||
|
int eos, comment;
|
||
|
int sno;
|
||
|
char stype;
|
||
|
ogg_stream_state instate;
|
||
|
struct stream_t *next;
|
||
|
|
||
|
int acodec;
|
||
|
ogg_int64_t bwritten;
|
||
|
|
||
|
ogg_stream_state outstate;
|
||
|
int packetno;
|
||
|
ogg_int64_t max_granulepos;
|
||
|
|
||
|
int subnr;
|
||
|
} stream_t;
|
||
|
|
||
|
stream_t *first;
|
||
|
int nastreams = 0, nvstreams = 0, ntstreams = 0, numstreams = 0;
|
||
|
char basename[] = "stdout";
|
||
|
|
||
|
static void add_stream(stream_t *ndmx) {
|
||
|
stream_t *cur = first;
|
||
|
|
||
|
if (first == NULL) {
|
||
|
first = ndmx;
|
||
|
first->next = NULL;
|
||
|
} else {
|
||
|
cur = first;
|
||
|
while (cur->next != NULL)
|
||
|
cur = cur->next;
|
||
|
cur->next = ndmx;
|
||
|
ndmx->next = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static stream_t *find_stream(int fserial) {
|
||
|
stream_t *cur = first;
|
||
|
|
||
|
while (cur != NULL) {
|
||
|
if (cur->serial == fserial)
|
||
|
break;
|
||
|
cur = cur->next;
|
||
|
}
|
||
|
|
||
|
return cur;
|
||
|
}
|
||
|
|
||
|
double highest_ts = 0;
|
||
|
|
||
|
static int extraction_requested(unsigned char *s, int stream, int type) {
|
||
|
int i;
|
||
|
|
||
|
if (no[type])
|
||
|
return 0;
|
||
|
if (strlen((char *)s) == 0)
|
||
|
return 1;
|
||
|
for (i = 0; i < strlen((char *)s); i++)
|
||
|
if (s[i] == stream)
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void flush_pages(stream_t *stream, ogg_packet *op) {
|
||
|
ogg_page page;
|
||
|
|
||
|
while (ogg_stream_flush(&stream->outstate, &page)) {
|
||
|
int ih, ib;
|
||
|
|
||
|
ih = tc_pwrite(stream->fd, page.header, page.header_len);
|
||
|
if (ih != page.header_len) {
|
||
|
tc_log_error(__FILE__, "error while writing page header");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
ib = tc_pwrite(stream->fd, page.body, page.body_len);
|
||
|
if (ib != page.body_len) {
|
||
|
tc_log_error(__FILE__, "error while writing page bofy");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/a%d: %d + %d written", stream->sno, ih, ib);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void write_pages(stream_t *stream, ogg_packet *op) {
|
||
|
ogg_page page;
|
||
|
|
||
|
while (ogg_stream_pageout(&stream->outstate, &page)) {
|
||
|
int ih, ib;
|
||
|
|
||
|
ih = tc_pwrite(stream->fd, page.header, page.header_len);
|
||
|
if (ih != page.header_len) {
|
||
|
tc_log_error(__FILE__, "error while writing page header");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
ib = tc_pwrite(stream->fd, page.body, page.body_len);
|
||
|
if (ib != page.body_len) {
|
||
|
tc_log_error(__FILE__, "error while writing page bofy");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/a%d: %d + %d written", stream->sno, ih, ib);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void handle_packet(stream_t *stream, ogg_packet *pack, ogg_page *page) {
|
||
|
int i, w, hdrlen, end;
|
||
|
long long lenbytes;
|
||
|
char *sub;
|
||
|
char out[100];
|
||
|
ogg_int64_t pgp, sst;
|
||
|
|
||
|
//tc_log_msg(__FILE__, "Going handle 1");
|
||
|
if (pack->e_o_s) {
|
||
|
stream->eos = 1;
|
||
|
pack->e_o_s = 1;
|
||
|
}
|
||
|
|
||
|
if (((double)pack->granulepos * 1000.0 / (double)stream->sample_rate) >
|
||
|
highest_ts)
|
||
|
highest_ts = (double)pack->granulepos * 1000.0 /
|
||
|
(double)stream->sample_rate;
|
||
|
|
||
|
switch (stream->stype) {
|
||
|
case 'v':
|
||
|
if (!extraction_requested(xvideo, stream->sno, NOVIDEO))
|
||
|
return;
|
||
|
break;
|
||
|
case 'a':
|
||
|
if (!extraction_requested(xaudio, stream->sno, NOAUDIO))
|
||
|
return;
|
||
|
break;
|
||
|
case 't':
|
||
|
if (!extraction_requested(xtext, stream->sno, NOTEXT))
|
||
|
return;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
hdrlen = (*pack->packet & OGM_PACKET_LEN_BITS01) >> 6;
|
||
|
hdrlen |= (*pack->packet & OGM_PACKET_LEN_BITS2) << 1;
|
||
|
for (i = 0, lenbytes = 0; i < hdrlen; i++) {
|
||
|
lenbytes = lenbytes << 8;
|
||
|
lenbytes += *((unsigned char *)pack->packet + hdrlen - i);
|
||
|
}
|
||
|
|
||
|
switch (stream->stype) {
|
||
|
case 'v':
|
||
|
if (((*pack->packet & 3) == OGM_PACKET_TYPE_HEADER) ||
|
||
|
((*pack->packet & 3) == OGM_PACKET_TYPE_COMMENT))
|
||
|
return;
|
||
|
i = tc_pwrite(stream->fd, (char *)&pack->packet[hdrlen + 1],
|
||
|
pack->bytes - 1 - hdrlen);
|
||
|
if (i != pack->bytes - 1 - hdrlen) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/v%d: %d written", stream->sno, i);
|
||
|
break;
|
||
|
case 't':
|
||
|
if (((*pack->packet & 3) == OGM_PACKET_TYPE_HEADER) ||
|
||
|
((*pack->packet & 3) == OGM_PACKET_TYPE_COMMENT))
|
||
|
return;
|
||
|
|
||
|
if (xraw) {
|
||
|
i = tc_pwrite(stream->fd, (char *)&pack->packet[hdrlen + 1],
|
||
|
pack->bytes - 1 - hdrlen);
|
||
|
if (i != pack->bytes - 1 - hdrlen) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/t%d: %d written", stream->sno, i);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sub = (char *)&pack->packet[hdrlen + 1];
|
||
|
if ((strlen(sub) > 1) || (*sub != ' ')) {
|
||
|
sst = (pack->granulepos / stream->sample_rate) * 1000;
|
||
|
pgp = sst + lenbytes;
|
||
|
tc_snprintf(out,sizeof(out), "%d\r\n%02d:%02d:%02d,%03d --> " \
|
||
|
"%02d:%02d:%02d,%03d\r\n", stream->subnr + 1,
|
||
|
(int)(sst / 3600000),
|
||
|
(int)(sst / 60000) % 60,
|
||
|
(int)(sst / 1000) % 60,
|
||
|
(int)(sst % 1000),
|
||
|
(int)(pgp / 3600000),
|
||
|
(int)(pgp / 60000) % 60,
|
||
|
(int)(pgp / 1000) % 60,
|
||
|
(int)(pgp % 1000));
|
||
|
i = tc_pwrite(stream->fd, out, strlen(out));
|
||
|
if (i != strlen(out)) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
end = strlen(sub) - 1;
|
||
|
while ((end >= 0) && ((sub[end] == '\n') || (sub[end] == '\r'))) {
|
||
|
sub[end] = 0;
|
||
|
end--;
|
||
|
}
|
||
|
w = tc_pwrite(stream->fd, sub, strlen(sub));
|
||
|
if (i == strlen(sub)) {
|
||
|
i += w;
|
||
|
} else {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1);
|
||
|
}
|
||
|
w = tc_pwrite(stream->fd, "\r\n\r\n", 4);
|
||
|
if (w == 4) {
|
||
|
i += w;
|
||
|
} else {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1);
|
||
|
}
|
||
|
stream->subnr++;
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/t%d: %d written", stream->sno, i);
|
||
|
}
|
||
|
break;
|
||
|
case 'a':
|
||
|
switch (stream->acodec) {
|
||
|
case ACVORBIS:
|
||
|
if (xraw) {
|
||
|
if (stream->packetno == 0) {
|
||
|
i = tc_pwrite(stream->fd, (char *)pack->packet, pack->bytes);
|
||
|
if (i != pack->bytes) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
} else {
|
||
|
i = tc_pwrite(stream->fd, (char *)&pack->packet[1],
|
||
|
pack->bytes - 1);
|
||
|
if (i != pack->bytes - 1) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
}
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/a%d: %d written", stream->sno, i);
|
||
|
return;
|
||
|
}
|
||
|
stream->max_granulepos = (pack->granulepos > stream->max_granulepos ?
|
||
|
pack->granulepos : stream->max_granulepos);
|
||
|
if ((stream->packetno == 0) || (stream->packetno == 2)) {
|
||
|
ogg_stream_packetin(&stream->outstate, pack);
|
||
|
flush_pages(stream, pack);
|
||
|
} else {
|
||
|
ogg_stream_packetin(&stream->outstate, pack);
|
||
|
write_pages(stream, pack);
|
||
|
}
|
||
|
stream->packetno++;
|
||
|
break;
|
||
|
default:
|
||
|
if (((*pack->packet & 3) == OGM_PACKET_TYPE_HEADER) ||
|
||
|
((*pack->packet & 3) == OGM_PACKET_TYPE_COMMENT))
|
||
|
return;
|
||
|
|
||
|
i = tc_pwrite(stream->fd, pack->packet + 1 + hdrlen,
|
||
|
pack->bytes - 1 - hdrlen);
|
||
|
if (i != pack->bytes - 1 - hdrlen) {
|
||
|
tc_log_error(__FILE__, "error while writing data");
|
||
|
import_exit(1); /* XXX ugly */
|
||
|
}
|
||
|
stream->bwritten += i;
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "x/a%d: %d written", stream->sno, i);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void process_ogm(int fdin, int fdout)
|
||
|
{
|
||
|
ogg_sync_state sync;
|
||
|
ogg_page page;
|
||
|
ogg_packet pack;
|
||
|
vorbis_info vi;
|
||
|
vorbis_comment vc;
|
||
|
char *buf, *new_name;
|
||
|
int nread, np, sno;
|
||
|
int endofstream = 0;
|
||
|
stream_t *stream = NULL;
|
||
|
|
||
|
ogg_sync_init(&sync);
|
||
|
while (1) {
|
||
|
np = ogg_sync_pageseek(&sync, &page);
|
||
|
if (np < 0) {
|
||
|
tc_log_error(__FILE__, "ogg_sync_pageseek failed");
|
||
|
return;
|
||
|
}
|
||
|
if (np == 0) {
|
||
|
buf = ogg_sync_buffer(&sync, BLOCK_SIZE);
|
||
|
if (!buf) {
|
||
|
tc_log_error(__FILE__, "ogg_sync_buffer failed");
|
||
|
return;
|
||
|
}
|
||
|
if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) {
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "end of stream 1");
|
||
|
return;
|
||
|
}
|
||
|
ogg_sync_wrote(&sync, nread);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!ogg_page_bos(&page)) {
|
||
|
break;
|
||
|
} else {
|
||
|
ogg_stream_state sstate;
|
||
|
sno = ogg_page_serialno(&page);
|
||
|
if (ogg_stream_init(&sstate, sno)) {
|
||
|
tc_log_error(__FILE__, "ogg_stream_init failed");
|
||
|
return;
|
||
|
}
|
||
|
ogg_stream_pagein(&sstate, &page);
|
||
|
ogg_stream_packetout(&sstate, &pack);
|
||
|
|
||
|
if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "vorbis", 6)) {
|
||
|
|
||
|
stream = tc_malloc(sizeof(stream_t));
|
||
|
if (stream == NULL) {
|
||
|
tc_log_error(__FILE__, "malloc failed.");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
memset(stream, 0, sizeof(stream_t));
|
||
|
if (verbose_flag & TC_INFO) {
|
||
|
vorbis_info_init(&vi);
|
||
|
vorbis_comment_init(&vc);
|
||
|
if (vorbis_synthesis_headerin(&vi, &vc, &pack) >= 0) {
|
||
|
tc_log_info(__FILE__, "(a%d/%d) Vorbis audio (channels %d "
|
||
|
"rate %ld)", nastreams + 1, numstreams + 1,
|
||
|
vi.channels, vi.rate);
|
||
|
stream->sample_rate = vi.rate;
|
||
|
} else
|
||
|
tc_log_warn(__FILE__, "(a%d/%d) Vorbis audio stream indicated "
|
||
|
"but no Vorbis stream header found.",
|
||
|
nastreams + 1, numstreams + 1);
|
||
|
}
|
||
|
stream->serial = sno;
|
||
|
stream->acodec = ACVORBIS;
|
||
|
stream->sample_rate = -1;
|
||
|
stream->sno = nastreams + 1;
|
||
|
stream->stype = 'a';
|
||
|
ac_memcpy(&stream->instate, &sstate, sizeof(sstate));
|
||
|
if (extraction_requested(xaudio, nastreams + 1, NOAUDIO)) {
|
||
|
stream->fd = fdout;
|
||
|
if (stream->fd == -1) {
|
||
|
tc_log_error(__FILE__, "Failed to create \"%s\" (%d, %s).",
|
||
|
"new_name", errno, strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!xraw)
|
||
|
ogg_stream_init(&stream->outstate, rand());
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "Extracting a%d to \"%s\".",
|
||
|
nastreams + 1, "new_name");
|
||
|
do
|
||
|
handle_packet(stream, &pack, &page);
|
||
|
while (ogg_stream_packetout(&stream->instate, &pack) == 1);
|
||
|
}
|
||
|
add_stream(stream);
|
||
|
nastreams++;
|
||
|
numstreams++;
|
||
|
} else if ((pack.bytes >= 142) &&
|
||
|
!strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg",
|
||
|
35) ) {
|
||
|
if ((*(int32_t*)(pack.packet+96) == 0x05589f80) &&
|
||
|
(pack.bytes >= 184)) {
|
||
|
tc_log_warn(__FILE__, "(v%d/%d) Found old video header. Not "
|
||
|
"supported.", nvstreams + 1, numstreams + 1);
|
||
|
} else if (*(int32_t*)pack.packet+96 == 0x05589F81) {
|
||
|
tc_log_warn(__FILE__, "(a%d/%d) Found old audio header. Not "
|
||
|
"supported.", nastreams + 1, numstreams + 1);
|
||
|
} else {
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_warn(__FILE__, "OGG stream %d has an old header with an "
|
||
|
"unknown type.", numstreams + 1);
|
||
|
}
|
||
|
} else if (((*pack.packet & OGM_PACKET_TYPE_BITS ) == OGM_PACKET_TYPE_HEADER) &&
|
||
|
(pack.bytes >= (int)(sizeof(ogm_stream_header) + 1 - sizeof(int)))) {
|
||
|
ogm_stream_header *sth = (ogm_stream_header *)(pack.packet + 1);
|
||
|
if (!strncmp(sth->streamtype, "video", 5)) {
|
||
|
unsigned long codec;
|
||
|
char ccodec[5];
|
||
|
strncpy(ccodec, sth->subtype, 4);
|
||
|
ccodec[4] = 0;
|
||
|
codec = (sth->subtype[0] << 24) +
|
||
|
(sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3];
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "(v%d/%d) fps: %.3f width height: %dx%d "
|
||
|
"codec: %p (%s)", nvstreams + 1, numstreams + 1,
|
||
|
(double)10000000 / (double)sth->time_unit,
|
||
|
sth->sh.video.width, sth->sh.video.height,
|
||
|
(void *)codec, ccodec);
|
||
|
stream = tc_malloc(sizeof(stream_t));
|
||
|
if (stream == NULL) {
|
||
|
tc_log_error(__FILE__, "malloc failed.");
|
||
|
exit(1);
|
||
|
}
|
||
|
stream->stype = 'v';
|
||
|
stream->serial = sno;
|
||
|
stream->sample_rate = (double)10000000 / (double)sth->time_unit;
|
||
|
stream->sno = nvstreams + 1;
|
||
|
ac_memcpy(&stream->instate, &sstate, sizeof(sstate));
|
||
|
if (extraction_requested(xvideo, nvstreams + 1, NOVIDEO)) {
|
||
|
stream->fd = fdout;
|
||
|
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "Extracting v%d to \"%s\".",
|
||
|
nvstreams + 1, "new_name");
|
||
|
do {
|
||
|
handle_packet(stream, &pack, &page);
|
||
|
} while (ogg_stream_packetout(&stream->instate, &pack) == 1);
|
||
|
}
|
||
|
add_stream(stream);
|
||
|
nvstreams++;
|
||
|
numstreams++;
|
||
|
} 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);
|
||
|
if (verbose_flag & TC_INFO) {
|
||
|
tc_log_info(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s), bits per "
|
||
|
"sample: %d channels: %hd samples per second: %lld "
|
||
|
" avgbytespersec: %hd blockalign: %d",
|
||
|
nastreams + 1, numstreams + 1, codec, codec,
|
||
|
codec == ACPCM ? "PCM" : codec == 55 ? "MP3" :
|
||
|
codec == ACMP3 ? "MP3" :
|
||
|
codec == ACAC3 ? "AC3" : "unknown",
|
||
|
sth->bits_per_sample, sth->sh.audio.channels,
|
||
|
(long long)sth->samples_per_unit,
|
||
|
sth->sh.audio.avgbytespersec, sth->sh.audio.blockalign);
|
||
|
}
|
||
|
stream = tc_malloc(sizeof(stream_t));
|
||
|
if (stream == NULL) {
|
||
|
tc_log_error(__FILE__, "malloc failed.");
|
||
|
exit(1);
|
||
|
}
|
||
|
stream->sno = nastreams + 1;
|
||
|
stream->stype = 'a';
|
||
|
stream->sample_rate = sth->samples_per_unit *
|
||
|
sth->sh.audio.channels;
|
||
|
stream->serial = sno;
|
||
|
stream->acodec = codec;
|
||
|
ac_memcpy(&stream->instate, &sstate, sizeof(sstate));
|
||
|
if (extraction_requested(xaudio, nastreams + 1, NOAUDIO)) {
|
||
|
|
||
|
/*
|
||
|
codec == ACPCM ? "wav" :
|
||
|
codec == ACMP3 ? "mp3" :
|
||
|
codec == ACAC3 ? "ac3" : "audio");
|
||
|
*/
|
||
|
stream->fd = fdout;
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "Extracting a%d to \"%s\".",
|
||
|
nastreams + 1, "new_name");
|
||
|
do {
|
||
|
handle_packet(stream, &pack, &page);
|
||
|
} while (ogg_stream_packetout(&stream->instate, &pack) == 1);
|
||
|
}
|
||
|
add_stream(stream);
|
||
|
nastreams++;
|
||
|
numstreams++;
|
||
|
} else if (!strncmp(sth->streamtype, "text", 4)) {
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "(t%d/%d) text/subtitle stream",
|
||
|
ntstreams + 1, numstreams + 1);
|
||
|
stream = tc_malloc(sizeof(stream_t));
|
||
|
if (stream == NULL) {
|
||
|
tc_log_error(__FILE__, "malloc failed.");
|
||
|
exit(1);
|
||
|
}
|
||
|
stream->sno = ntstreams + 1;
|
||
|
stream->stype = 't';
|
||
|
stream->sample_rate = (double)10000000 / (double)sth->time_unit;
|
||
|
stream->serial = sno;
|
||
|
ac_memcpy(&stream->instate, &sstate, sizeof(sstate));
|
||
|
if (extraction_requested(xtext, ntstreams + 1, NOTEXT)) {
|
||
|
new_name = tc_malloc(strlen(basename) + 20);
|
||
|
if (!new_name) {
|
||
|
tc_log_error(__FILE__, "Failed to allocate %d bytes.",
|
||
|
(int)strlen(basename) + 20);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!xraw)
|
||
|
tc_snprintf(new_name, strlen(basename) + 20, "%s-t%d.srt", basename, ntstreams + 1);
|
||
|
else
|
||
|
tc_snprintf(new_name, strlen(basename) + 20, "%s-t%d.raw", basename, ntstreams + 1);
|
||
|
//stream->fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC,
|
||
|
// S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||
|
stream->fd = fdout;
|
||
|
if (stream->fd == -1) {
|
||
|
tc_log_error(__FILE__, "Failed to create \"%s\" (%d, %s).",
|
||
|
new_name, errno, strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "Extracting t%d to \"%s\".",
|
||
|
ntstreams + 1, new_name);
|
||
|
do
|
||
|
handle_packet(stream, &pack, &page);
|
||
|
while (ogg_stream_packetout(&stream->instate, &pack) == 1);
|
||
|
}
|
||
|
add_stream(stream);
|
||
|
ntstreams++;
|
||
|
numstreams++;
|
||
|
} else {
|
||
|
tc_log_warn(__FILE__, "(%d) found new header of unknown/"
|
||
|
"unsupported type\n", numstreams + 1);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
tc_log_warn(__FILE__, "OGG stream %d is of an unknown type "
|
||
|
"(bad header?)\n", numstreams + 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
endofstream = 0;
|
||
|
while (!endofstream) {
|
||
|
sno = ogg_page_serialno(&page);
|
||
|
stream = find_stream(sno);
|
||
|
if (stream == NULL) {
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_warn(__FILE__, "Encountered packet for an unknown serial "
|
||
|
"%d !?\n", sno);
|
||
|
} else {
|
||
|
if (verbose_flag & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "%c%d: NEW PAGE", stream->stype, stream->sno);
|
||
|
|
||
|
ogg_stream_pagein(&stream->instate, &page);
|
||
|
while (ogg_stream_packetout(&stream->instate, &pack) == 1)
|
||
|
handle_packet(stream, &pack, &page);
|
||
|
}
|
||
|
|
||
|
while (ogg_sync_pageseek(&sync, &page) <= 0) {
|
||
|
buf = ogg_sync_buffer(&sync, BLOCK_SIZE);
|
||
|
nread = read(fdin, buf, BLOCK_SIZE);
|
||
|
if (nread <= 0) {
|
||
|
stream = first;
|
||
|
while (stream != NULL) {
|
||
|
switch (stream->stype) {
|
||
|
case 'v':
|
||
|
close(stream->fd);
|
||
|
break;
|
||
|
case 't':
|
||
|
if (stream->fd > 0)
|
||
|
close(stream->fd);
|
||
|
break;
|
||
|
case 'a':
|
||
|
if ((stream->fd > 0) && !xraw) {
|
||
|
switch (stream->acodec) {
|
||
|
case ACVORBIS:
|
||
|
if (!stream->eos) {
|
||
|
pack.b_o_s = 0;
|
||
|
pack.e_o_s = 1;
|
||
|
pack.packet = NULL;
|
||
|
pack.bytes = 0;
|
||
|
pack.granulepos = stream->max_granulepos;
|
||
|
pack.packetno = stream->packetno;
|
||
|
ogg_stream_packetin(&stream->outstate, &pack);
|
||
|
}
|
||
|
flush_pages(stream, &pack);
|
||
|
ogg_stream_clear(&stream->outstate);
|
||
|
break;
|
||
|
}
|
||
|
close(stream->fd);
|
||
|
} else if (stream->fd > 0)
|
||
|
close(stream->fd);
|
||
|
break;
|
||
|
}
|
||
|
stream = stream->next;
|
||
|
}
|
||
|
if (verbose_flag & TC_INFO)
|
||
|
tc_log_info(__FILE__, "end of stream");
|
||
|
endofstream = 1;
|
||
|
break;
|
||
|
} else
|
||
|
ogg_sync_wrote(&sync, nread);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif /* have OGG */
|
||
|
|
||
|
void extract_ogm (info_t *ipipe)
|
||
|
{
|
||
|
// track
|
||
|
|
||
|
no[NOTEXT] = 1;
|
||
|
no[NOAUDIO] = 0;
|
||
|
no[NOVIDEO] = 0;
|
||
|
xraw = 1;
|
||
|
|
||
|
xvideo = tc_zalloc (16); xaudio = tc_zalloc (16); xtext = tc_zalloc (16);
|
||
|
|
||
|
verbose_flag = ipipe->verbose;
|
||
|
|
||
|
if (ipipe->select == TC_VIDEO) {
|
||
|
|
||
|
no[NOAUDIO] = 1;
|
||
|
xvideo[0] = (unsigned char)(ipipe->track+1);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (ipipe->select == TC_AUDIO) {
|
||
|
|
||
|
no[NOVIDEO] = 1;
|
||
|
xaudio[0] = (unsigned char)(ipipe->track+1);
|
||
|
|
||
|
// we need !xraw because no tool seems to be able to handle
|
||
|
// raw vorbis streams -- tibit
|
||
|
|
||
|
if (ipipe->codec == TC_CODEC_VORBIS) {
|
||
|
xraw = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#if (HAVE_OGG && HAVE_VORBIS)
|
||
|
process_ogm(ipipe->fd_in, ipipe->fd_out);
|
||
|
#else
|
||
|
tc_log_error(__FILE__, "No support for Ogg/Vorbis compiled in");
|
||
|
import_exit(1);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* vim: sw=2
|
||
|
*/
|