Commit a3a5051b authored by ivo's avatar ivo
Browse files

applied skeleton patch by Tahseen; ticket #1008

svn path=/trunk/vorbis-tools/; revision=14147
parent 0e0f98e9
......@@ -23,7 +23,7 @@ oggenc_DEPENDENCIES = @SHARE_LIBS@
oggenc_SOURCES = $(flac_sources) \
oggenc.c audio.c encode.c platform.c \
audio.h encode.h platform.h resample.c resample.h
audio.h encode.h platform.h resample.c resample.h skeleton.c skeleton.h
debug:
......
......@@ -17,11 +17,13 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include "platform.h"
#include <vorbis/vorbisenc.h>
#include "encode.h"
#include "i18n.h"
#include "skeleton.h"
#define READSIZE 1024
......@@ -110,12 +112,47 @@ static void set_advanced_encoder_options(adv_opt *opts, int count,
}
}
void add_fishead_packet (ogg_stream_state *os) {
fishead_packet fp;
memset(&fp, 0, sizeof(fp));
fp.ptime_n = 0;
fp.ptime_d = 1000;
fp.btime_n = 0;
fp.btime_d = 1000;
add_fishead_to_stream(os, &fp);
}
/*
* Adds the fishead packets in the skeleton output stream along with the e_o_s packet
*/
void add_fisbone_packet (ogg_stream_state *os, oe_enc_opt *opt) {
fisbone_packet fp;
memset(&fp, 0, sizeof(fp));
fp.serial_no = opt->serialno;
fp.nr_header_packet = 3;
fp.granule_rate_n = opt->rate;
fp.granule_rate_d = 1;
fp.start_granule = 0;
fp.preroll = 2;
fp.granule_shift = 0;
add_message_header_field(&fp, "Content-Type", "audio/vorbis");
add_fisbone_to_stream(os, &fp);
}
int oe_encode(oe_enc_opt *opt)
{
ogg_stream_state os;
ogg_page og;
ogg_packet op;
ogg_stream_state so; /* stream for skeleton bitstream */
ogg_page og;
ogg_packet op;
vorbis_dsp_state vd;
vorbis_block vb;
......@@ -127,9 +164,10 @@ int oe_encode(oe_enc_opt *opt)
double time_elapsed;
int ret=0;
TIMER *timer;
int result;
if(opt->channels > 255) {
fprintf(stderr, _("255 channels should be enough for anyone. (Sorry, vorbis doesn't support more)\n"));
fprintf(stderr, _("255 channels should be enough for anyone. (Sorry, but Vorbis doesn't support more)\n"));
return 1;
}
......@@ -152,7 +190,7 @@ int oe_encode(oe_enc_opt *opt)
/* Have vorbisenc choose a mode for us */
vorbis_info_init(&vi);
if(opt->quality_set > 0){
if(vorbis_encode_setup_vbr(&vi, opt->channels, opt->rate, opt->quality)){
fprintf(stderr, _("Mode initialisation failed: invalid parameters for quality\n"));
......@@ -192,7 +230,7 @@ int oe_encode(oe_enc_opt *opt)
ai.bitrate_average_damping = 1.5;
ai.bitrate_limit_reservoir_bits = bitrate * 2;
ai.bitrate_limit_reservoir_bias = .1;
/* And now the ones we actually wanted to set */
ai.bitrate_limit_min_kbps=opt->min_bitrate;
ai.bitrate_limit_max_kbps=opt->max_bitrate;
......@@ -218,7 +256,7 @@ int oe_encode(oe_enc_opt *opt)
return 1;
}
}
if(opt->managed && opt->bitrate < 0)
{
struct ovectl_ratemanage2_arg ai;
......@@ -231,9 +269,9 @@ int oe_encode(oe_enc_opt *opt)
/* Turn off management entirely (if it was turned on). */
vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE2_SET, NULL);
}
set_advanced_encoder_options(opt->advopt, opt->advopt_count, &vi);
vorbis_encode_setup_init(&vi);
......@@ -245,6 +283,17 @@ int oe_encode(oe_enc_opt *opt)
vorbis_block_init(&vd,&vb);
ogg_stream_init(&os, opt->serialno);
if (opt->with_skeleton)
ogg_stream_init(&so, opt->skeleton_serialno);
/* create the skeleton fishead packet and output it */
if (opt->with_skeleton) {
add_fishead_packet(&so);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error("Failed writing fishead packet to output stream\n");
goto cleanup;
}
}
/* Now, build the three header packets and send through to the stream
output stage (but defer actual file output until the main encode loop) */
......@@ -253,14 +302,33 @@ int oe_encode(oe_enc_opt *opt)
ogg_packet header_main;
ogg_packet header_comments;
ogg_packet header_codebooks;
int result;
/* Build the packets */
vorbis_analysis_headerout(&vd,opt->comments,
&header_main,&header_comments,&header_codebooks);
/* And stream them out */
/* output the vorbis bos first, then the fisbone packets */
ogg_stream_packetin(&os,&header_main);
while((result = ogg_stream_flush(&os, &og)))
{
if(!result) break;
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
opt->error(_("Failed writing header to output stream\n"));
ret = 1;
goto cleanup; /* Bail and try to clean up stuff */
}
}
if (opt->with_skeleton) {
add_fisbone_packet(&so, opt);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error("Failed writing fisbone header packet to output stream\n");
goto cleanup;
}
}
ogg_stream_packetin(&os,&header_comments);
ogg_stream_packetin(&os,&header_codebooks);
......@@ -277,6 +345,14 @@ int oe_encode(oe_enc_opt *opt)
}
}
if (opt->with_skeleton) {
add_eos_packet_to_stream(&so);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error("Failed writing skeleton eos packet to output stream\n");
goto cleanup;
}
}
eos = 0;
/* Main encode loop - continue until end of file */
......@@ -342,7 +418,7 @@ int oe_encode(oe_enc_opt *opt)
}
else
bytes_written += ret;
if(ogg_page_eos(&og))
eos = 1;
}
......@@ -376,7 +452,7 @@ void update_statistics_full(char *fn, long total, long done, double time)
static int spinpoint = 0;
double remain_time;
int minutes=0,seconds=0;
remain_time = time/((double)done/(double)total) - time;
minutes = ((int)remain_time)/60;
seconds = (int)(remain_time - (double)((int)remain_time/60)*60);
......@@ -390,7 +466,7 @@ void update_statistics_notime(char *fn, long total, long done, double time)
{
static char *spinner="|/-\\";
static int spinpoint =0;
fprintf(stderr, "\r");
fprintf(stderr, _("\tEncoding [%2dm%.2ds so far] %c "),
((int)time)/60, (int)(time - (double)((int)time/60)*60),
......@@ -415,7 +491,7 @@ void final_statistics(char *fn, double time, int rate, long samples, long bytes)
fprintf(stderr, _("\n\nDone encoding.\n"));
speed_ratio = (double)samples / (double)rate / time;
fprintf(stderr, _("\n\tFile length: %dm %04.1fs\n"),
(int)(samples/rate/60),
samples/rate -
......
......@@ -57,6 +57,7 @@ typedef struct
adv_opt *advopt;
int advopt_count;
int copy_comments;
int with_skeleton;
int quiet;
......@@ -86,6 +87,7 @@ typedef struct
float scale;
unsigned int serial;
unsigned int skeleton_serial;
int fixedserial;
} oe_options;
......@@ -93,13 +95,14 @@ typedef struct
{
vorbis_comment *comments;
unsigned int serialno;
unsigned int skeleton_serialno;
audio_read_func read_samples;
progress_func progress_update;
enc_end_func end_encode;
enc_start_func start_encode;
error_func error;
void *readdata;
long total_samples_per_channel;
......@@ -109,6 +112,7 @@ typedef struct
int endianness;
int resamplefreq;
int copy_comments;
int with_skeleton;
/* Various bitrate/quality options */
int managed;
......
......@@ -37,6 +37,7 @@
struct option long_options[] = {
{"quiet",0,0,'Q'},
{"help",0,0,'h'},
{"skeleton",no_argument,NULL, 'k'},
{"comment",1,0,'c'},
{"artist",1,0,'a'},
{"album",1,0,'l'},
......@@ -67,7 +68,7 @@ struct option long_options[] = {
{"discard-comments", 0, 0, 0},
{NULL,0,0,0}
};
static char *generate_name_string(char *format, char *remove_list,
char *replace_list, char *artist, char *title, char *album,
char *track, char *date, char *genre);
......@@ -81,7 +82,7 @@ int main(int argc, char **argv)
{
/* Default values */
oe_options opt = {NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL,
0, NULL, 0, NULL, 0, NULL, 0, 1, 0, 0,16,44100,2, 0, NULL,
0, NULL, 0, NULL, 0, NULL, 0, 1, 0, 0, 0,16,44100,2, 0, NULL,
DEFAULT_NAMEFMT_REMOVE, DEFAULT_NAMEFMT_REPLACE,
NULL, 0, -1,-1,-1,.3,-1,0, 0,0.f, 0, 0};
......@@ -130,6 +131,7 @@ int main(int argc, char **argv)
/* We randomly pick a serial number. This is then incremented for each file */
srand(time(NULL));
opt.serial = rand();
opt.skeleton_serial = opt.serial + numfiles;
}
for(i = 0; i < numfiles; i++)
......@@ -149,12 +151,14 @@ int main(int argc, char **argv)
/* Set various encoding defaults */
enc_opts.serialno = opt.serial++;
enc_opts.skeleton_serialno = opt.skeleton_serial++;
enc_opts.progress_update = update_statistics_full;
enc_opts.start_encode = start_encode_full;
enc_opts.end_encode = final_statistics;
enc_opts.error = encode_error;
enc_opts.comments = &vc;
enc_opts.copy_comments = opt.copy_comments;
enc_opts.with_skeleton = opt.with_skeleton;
/* OK, let's build the vorbis_comments structure */
build_comments(&vc, &opt, i, &artist, &album, &title, &track,
......@@ -262,11 +266,12 @@ int main(int argc, char **argv)
start = infiles[i];
end = strrchr(infiles[i], '.');
end = end?end:(start + strlen(infiles[i])+1);
char *extension = (opt.with_skeleton) ? ".ogg" : ".oga";
out_fn = malloc(end - start + 5);
strncpy(out_fn, start, end-start);
out_fn[end-start] = 0;
strcat(out_fn, ".ogg");
strcat(out_fn, extension);
}
else {
fprintf(stderr, _("WARNING: No filename, defaulting to \"default.ogg\"\n"));
......@@ -394,6 +399,7 @@ static void usage(void)
" -Q, --quiet Produce no output to stderr\n"
" -h, --help Print this help text\n"
" -v, --version Print the version number\n"
" -k, --skeleton Outputs ogg skeleton metadata\n"
" -r, --raw Raw mode. Input files are read directly as PCM data\n"
" -B, --raw-bits=n Set bits/sample for raw input. Default is 16\n"
" -C, --raw-chan=n Set number of channels for raw input. Default is 2\n"
......@@ -591,13 +597,16 @@ static void parse_options(int argc, char **argv, oe_options *opt)
int ret;
int option_index = 1;
while((ret = getopt_long(argc, argv, "A:a:b:B:c:C:d:G:hl:m:M:n:N:o:P:q:QrR:s:t:vX:",
while((ret = getopt_long(argc, argv, "A:a:b:B:c:C:d:G:hl:m:M:n:N:o:P:q:QrR:s:t:vX:k",
long_options, &option_index)) != -1)
{
switch(ret)
{
case 0:
if(!strcmp(long_options[option_index].name, "managed")) {
if(!strcmp(long_options[option_index].name, "skleton")) {
opt->with_skeleton = 1;
}
else if(!strcmp(long_options[option_index].name, "managed")) {
if(!opt->managed){
if(!opt->quiet)
fprintf(stderr,
......@@ -841,6 +850,8 @@ static void parse_options(int argc, char **argv, oe_options *opt)
fprintf(stderr, _("WARNING: Invalid sample rate specified, assuming 44100.\n"));
}
break;
case 'k':
opt->with_skeleton = 1;
case '?':
fprintf(stderr, _("WARNING: Unknown option specified, ignoring->\n"));
break;
......
/*
* skeleton.c
* author: Tahseen Mohammad
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ogg/ogg.h>
#include "skeleton.h"
int add_message_header_field(fisbone_packet *fp,
char *header_key,
char *header_value) {
/* size of both key and value + ': ' + CRLF */
int this_message_size = strlen(header_key) + strlen(header_value) + 4;
if (fp->message_header_fields == NULL) {
fp->message_header_fields = _ogg_calloc(this_message_size, sizeof(char));
} else {
int new_size = (fp->current_header_size + this_message_size) * sizeof(char);
fp->message_header_fields = _ogg_realloc(fp->message_header_fields, new_size);
}
snprintf(fp->message_header_fields + fp->current_header_size,
this_message_size+1,
"%s: %s\r\n",
header_key,
header_value);
fp->current_header_size += this_message_size;
return 0;
}
/* create a ogg_packet from a fishead_packet structure */
ogg_packet ogg_from_fishead(fishead_packet *fp) {
ogg_packet op;
memset(&op, 0, sizeof(op));
op.packet = _ogg_calloc(FISHEAD_SIZE, sizeof(unsigned char));
memset(op.packet, 0, FISHEAD_SIZE);
memcpy (op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */
*((ogg_uint16_t*)(op.packet+8)) = SKELETON_VERSION_MAJOR; /* version major */
*((ogg_uint16_t*)(op.packet+10)) = SKELETON_VERSION_MINOR; /* version minor */
*((ogg_int64_t*)(op.packet+12)) = (ogg_int64_t)fp->ptime_n; /* presentationtime numerator */
*((ogg_int64_t*)(op.packet+20)) = (ogg_int64_t)fp->ptime_d; /* presentationtime denominator */
*((ogg_int64_t*)(op.packet+28)) = (ogg_int64_t)fp->btime_n; /* basetime numerator */
*((ogg_int64_t*)(op.packet+36)) = (ogg_int64_t)fp->btime_d; /* basetime denominator */
/* TODO: UTC time, set to zero for now */
op.b_o_s = 1; /* its the first packet of the stream */
op.e_o_s = 0; /* its not the last packet of the stream */
op.bytes = FISHEAD_SIZE; /* length of the packet in bytes */
return op;
}
/* create a ogg_packet from a fisbone_packet structure.
* call this method after the fisbone_packet is filled and all message header fields are added
* by calling add_message_header_field method.
*/
ogg_packet ogg_from_fisbone(fisbone_packet *fp) {
ogg_packet op;
int packet_size = FISBONE_SIZE + fp->current_header_size;
memset (&op, 0, sizeof (op));
op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
memset (op.packet, 0, packet_size);
memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
*((ogg_uint32_t*)(op.packet+8)) = FISBONE_MESSAGE_HEADER_OFFSET; /* offset of the message header fields */
*((ogg_uint32_t*)(op.packet+12)) = fp->serial_no; /* serialno of the respective stream */
*((ogg_uint32_t*)(op.packet+16)) = fp->nr_header_packet; /* number of header packets */
*((ogg_int64_t*)(op.packet+20)) = fp->granule_rate_n; /* granulrate numerator */
*((ogg_int64_t*)(op.packet+28)) = fp->granule_rate_d; /* granulrate denominator */
*((ogg_int64_t*)(op.packet+36)) = fp->start_granule; /* start granule */
*((ogg_uint32_t*)(op.packet+44)) = fp->preroll; /* preroll, for theora its 0 */
*(op.packet+48) = fp->granule_shift; /* granule shift */
memcpy((op.packet+FISBONE_SIZE), fp->message_header_fields, fp->current_header_size);
op.b_o_s = 0;
op.e_o_s = 0;
op.bytes = packet_size; /* size of the packet in bytes */
return op;
}
int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp) {
ogg_packet op;
op = ogg_from_fishead(fp);
ogg_stream_packetin(os, &op);
_ogg_free(op.packet);
return 0;
}
int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp) {
ogg_packet op;
op = ogg_from_fisbone(fp);
ogg_stream_packetin(os, &op);
_ogg_free(op.packet);
return 0;
}
int add_eos_packet_to_stream(ogg_stream_state *os) {
ogg_packet op;
memset (&op, 0, sizeof(op));
op.e_o_s = 1;
ogg_stream_packetin(os, &op);
}
int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out) {
ogg_page og;
int result, ret;
while((result = ogg_stream_flush(os, &og)))
{
if(!result) break;
result = oe_write_page(&og, out);
if(result != og.header_len + og.body_len)
return 1;
}
}
\ No newline at end of file
/*
* skeleton.h
* author: Tahseen Mohammad
*/
#ifndef _SKELETON_H
#define _SKELETON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <ogg/ogg.h>
#define SKELETON_VERSION_MAJOR 3
#define SKELETON_VERSION_MINOR 0
#define FISHEAD_IDENTIFIER "fishead\0"
#define FISBONE_IDENTIFIER "fisbone\0"
#define FISHEAD_SIZE 64
#define FISBONE_SIZE 52
#define FISBONE_MESSAGE_HEADER_OFFSET 44
/* fishead_packet holds a fishead header packet. */
typedef struct {
/* Start time of the presentation.
* For a new stream presentationtime & basetime is same. */
ogg_int64_t ptime_n; /* presentation time numerator */
ogg_int64_t ptime_d; /* presentation time denominator */
ogg_int64_t btime_n; /* basetime numerator */
ogg_int64_t btime_d; /* basetime denominator */
/* will holds the time of origin of the stream, a 20 bit field. */
unsigned char UTC[20];
} fishead_packet;
/* fisbone_packet holds a fisbone header packet. */
typedef struct {
ogg_uint32_t serial_no; /* serial no of the corresponding stream */
ogg_uint32_t nr_header_packet; /* number of header packets */
/* granule rate is the temporal resolution of the logical bitstream */
ogg_int64_t granule_rate_n; /* granule rate numerator */
ogg_int64_t granule_rate_d; /* granule rate denominator */
ogg_int64_t start_granule; /* start granule value */
ogg_uint32_t preroll; /* preroll */
unsigned char granule_shift; // a 8-bit field /* 1 byte value holding the granule shift */
char *message_header_fields; /* holds all the message header fields */
/* current total size of the message header fields, for realloc purpose, initially zero */
ogg_uint32_t current_header_size;
} fisbone_packet;
extern int add_message_header_field(fisbone_packet *fp, char *header_key, char *header_value);
/* remember to deallocate the returned ogg_packet properly */
extern ogg_packet ogg_from_fishead(fishead_packet *fp);
extern ogg_packet ogg_from_fisbone(fisbone_packet *fp);
extern int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp);
extern int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp);
extern int add_eos_packet_to_stream(ogg_stream_state *os);
extern int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out);
#ifdef __cplusplus
}
#endif
#endif /* _SKELETON_H */
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment