/* OggEnc
**
** This program is distributed under the GNU General Public License, version 2.
** A copy of this license is included with this source.
**
** Copyright 2000-2002, Michael Smith <msmith@xiph.org>
**
** Portions from Vorbize, (c) Kenneth Arnold <kcarnold@yahoo.com>
** and libvorbis examples, (c) Monty <monty@xiph.org>
**/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "platform.h"
#include <vorbis/vorbisenc.h>
#include "encode.h"
#include "i18n.h"
#define READSIZE 1024
int oe_write_page(ogg_page *page, FILE *fp);
#define SETD(toset) \
do {\
if(sscanf(opts[i].val, "%lf", &dval) != 1)\
fprintf(stderr, "For option %s, couldn't read value %s as double\n",\
opts[i].arg, opts[i].val);\
else\
toset = dval;\
} while(0)
#define SETL(toset) \
do {\
if(sscanf(opts[i].val, "%ld", &lval) != 1)\
fprintf(stderr, "For option %s, couldn't read value %s as integer\n",\
opts[i].arg, opts[i].val);\
else\
toset = lval;\
} while(0)
static void set_advanced_encoder_options(adv_opt *opts, int count,
vorbis_info *vi)
{
int hard = 0;
int avg = 0;
struct ovectl_ratemanage_arg ai;
int i;
double dval;
long lval;
vorbis_encode_ctl(vi, OV_ECTL_RATEMANAGE_GET, &ai);
for(i=0; i < count; i++) {
fprintf(stderr, _("Setting advanced encoder option \"%s\" to %s\n"),
opts[i].arg, opts[i].val);
if(!strcmp(opts[i].arg, "bitrate_average_window")) {
SETD(ai.bitrate_av_window);
avg = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_average_window_center")) {
SETD(ai.bitrate_av_window_center);
avg = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_average_low")) {
SETL(ai.bitrate_av_lo);
avg = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_average_high")) {
SETL(ai.bitrate_av_hi);
avg = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_hard_min")) {
SETL(ai.bitrate_hard_min);
hard = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_hard_max")) {
SETL(ai.bitrate_hard_max);
hard = 1;
}
else if(!strcmp(opts[i].arg, "bitrate_hard_window")) {
SETD(ai.bitrate_hard_window);
hard = 1;
}
else if(!strcmp(opts[i].arg, "impulse_noisetune")) {
double val;
SETD(val);
vorbis_encode_ctl(vi, OV_ECTL_IBLOCK_SET, &val);
}
else if(!strcmp(opts[i].arg, "lowpass_frequency")) {
double prev, new;
SETD(new);
vorbis_encode_ctl(vi, OV_ECTL_LOWPASS_GET, &prev);
vorbis_encode_ctl(vi, OV_ECTL_LOWPASS_SET, &new);
fprintf(stderr, _("Changed lowpass frequency from %f kHz to %f kHz\n"), prev, new);
}
else {
fprintf(stderr, _("Unrecognised advanced option \"%s\"\n"),
opts[i].arg);
}
}
if(hard)
vorbis_encode_ctl(vi, OV_ECTL_RATEMANAGE_HARD, &ai);
if(avg)
vorbis_encode_ctl(vi, OV_ECTL_RATEMANAGE_AVG, &ai);
}
int oe_encode(oe_enc_opt *opt)
{
ogg_stream_state os;
ogg_page og;
ogg_packet op;
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_info vi;
long samplesdone=0;
int eos;
long bytes_written = 0, packetsdone=0;
double time_elapsed;
int ret=0;
TIMER *timer;
if(opt->channels > 255) {
fprintf(stderr, _("255 channels should be enough for anyone. (Sorry, vorbis doesn't support more)\n"));
return 1;
}
/* get start time. */
timer = timer_start();
if(!opt->managed && (opt->min_bitrate>=0 || opt->max_bitrate>=0)){
fprintf(stderr, _("Requesting a minimum or maximum bitrate requires --managed\n"));
return 1;
}
/* if we had no quality or bitrate spec at all from the user, use
the default quality with no management --Monty 20020711 */
if(opt->bitrate < 0 && opt->min_bitrate < 0 && opt->max_bitrate < 0){
opt->quality_set=1;
}
opt->start_encode(opt->infilename, opt->filename, opt->bitrate, opt->quality,
opt->quality_set, opt->managed, opt->min_bitrate, opt->max_bitrate);
/* 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"));
vorbis_info_clear(&vi);
return 1;
}
/* do we have optional hard quality restrictions? */
if(opt->max_bitrate > 0 || opt->min_bitrate > 0){
struct ovectl_ratemanage_arg ai;
vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_GET, &ai);
ai.bitrate_hard_min=opt->min_bitrate;
ai.bitrate_hard_max=opt->max_bitrate;
ai.management_active=1;
vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_SET, &ai);
}
}else {
if(vorbis_encode_setup_managed(&vi, opt->channels, opt->rate,
opt->max_bitrate>0?opt->max_bitrate*1000:-1,
opt->bitrate*1000,
opt->min_bitrate>0?opt->min_bitrate*1000:-1)){
fprintf(stderr, _("Mode initialisation failed: invalid parameters for bitrate\n"));
vorbis_info_clear(&vi);
return 1;
}
}
if(opt->managed && opt->bitrate < 0)
{
vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_AVG, NULL);
}
else if(!opt->managed)
{
/* Turn off management entirely (if it was turned on). */
vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_SET, NULL);
}
set_advanced_encoder_options(opt->advopt, opt->advopt_count, &vi);
vorbis_encode_setup_init(&vi);
/* Now, set up the analysis engine, stream encoder, and other
preparation before the encoding begins.
*/
vorbis_analysis_init(&vd,&vi);
vorbis_block_init(&vd,&vb);
ogg_stream_init(&os, opt->serialno);
/* Now, build the three header packets and send through to the stream
output stage (but defer actual file output until the main encode loop) */
{
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 */
ogg_stream_packetin(&os,&header_main);
ogg_stream_packetin(&os,&header_comments);
ogg_stream_packetin(&os,&header_codebooks);
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 */
}
}
}
eos = 0;
/* Main encode loop - continue until end of file */
while(!eos)
{
float **buffer = vorbis_analysis_buffer(&vd, READSIZE);
long samples_read = opt->read_samples(opt->readdata,
buffer, READSIZE);
if(samples_read ==0)
/* Tell the library that we wrote 0 bytes - signalling the end */
vorbis_analysis_wrote(&vd,0);
else
{
samplesdone += samples_read;
/* Call progress update every 40 pages */
if(packetsdone>=40)
{
double time;
packetsdone = 0;
time = timer_time(timer);
opt->progress_update(opt->filename, opt->total_samples_per_channel,
samplesdone, time);
}
/* Tell the library how many samples (per channel) we wrote
into the supplied buffer */
vorbis_analysis_wrote(&vd, samples_read);
}
/* While we can get enough data from the library to analyse, one
block at a time... */
while(vorbis_analysis_blockout(&vd,&vb)==1)
{
/* Do the main analysis, creating a packet */
vorbis_analysis(&vb, NULL);
vorbis_bitrate_addblock(&vb);
while(vorbis_bitrate_flushpacket(&vd, &op))
{
/* Add packet to bitstream */
ogg_stream_packetin(&os,&op);
packetsdone++;
/* If we've gone over a page boundary, we can do actual output,
so do so (for however many pages are available) */
while(!eos)
{
int result = ogg_stream_pageout(&os,&og);
if(!result) break;
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
opt->error(_("Failed writing data to output stream\n"));
ret = 1;
goto cleanup; /* Bail */
}
else
bytes_written += ret;
if(ogg_page_eos(&og))
eos = 1;
}
}
}
}
ret = 0; /* Success, set return value to 0 since other things reuse it
* for nefarious purposes. */
/* Cleanup time */
cleanup:
ogg_stream_clear(&os);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_info_clear(&vi);
time_elapsed = timer_time(timer);
opt->end_encode(opt->filename, time_elapsed, opt->rate, samplesdone, bytes_written);
timer_clear(timer);
return ret;
}
void update_statistics_full(char *fn, long total, long done, double time)
{
static char *spinner="|/-\\";
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);
fprintf(stderr, "\r");
fprintf(stderr, _("\t[%5.1f%%] [%2dm%.2ds remaining] %c"),
done*100.0/total, minutes, seconds, spinner[spinpoint++%4]);
}
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),
spinner[spinpoint++%4]);
}
int oe_write_page(ogg_page *page, FILE *fp)
{
int written;
written = fwrite(page->header,1,page->header_len, fp);
written += fwrite(page->body,1,page->body_len, fp);
return written;
}
void final_statistics(char *fn, double time, int rate, long samples, long bytes)
{
double speed_ratio;
if(fn)
fprintf(stderr, _("\n\nDone encoding file \"%s\"\n"), fn);
else
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 -
floor(samples/rate/60)*60);
fprintf(stderr, _("\tElapsed time: %dm %04.1fs\n"),
(int)(time/60),
time - floor(time/60)*60);
fprintf(stderr, _("\tRate: %.4f\n"), speed_ratio);
fprintf(stderr, _("\tAverage bitrate: %.1f kb/s\n\n"),
8./1000.*((double)bytes/((double)samples/(double)rate)));
}
void final_statistics_null(char *fn, double time, int rate, long samples,
long bytes)
{
/* Don't do anything, this is just a placeholder function for quiet mode */
}
void update_statistics_null(char *fn, long total, long done, double time)
{
/* So is this */
}
void encode_error(char *errmsg)
{
fprintf(stderr, "\n%s\n", errmsg);
}
static void print_brconstraints(int min, int max)
{
if(min > 0 && max > 0)
fprintf(stderr, "(min %d kbps, max %d kbps)", min,max);
else if(min > 0)
fprintf(stderr, "(min %d kbps, no max)", min);
else if(max > 0)
fprintf(stderr, "(no min, max %d kbps)", max);
else
fprintf(stderr, "(no min or max)");
}
void start_encode_full(char *fn, char *outfn, int bitrate, float quality, int qset,
int managed, int min, int max)
{
if(bitrate>0){
if(managed>0){
fprintf(stderr, _("Encoding %s%s%s to \n "
"%s%s%s \nat average bitrate %d kbps "),
fn?"\"":"", fn?fn:_("standard input"), fn?"\"":"",
outfn?"\"":"", outfn?outfn:_("standard output"), outfn?"\"":"",
bitrate);
print_brconstraints(min,max);
fprintf(stderr, ", \nusing full bitrate management engine\n");
} else {
fprintf(stderr, _("Encoding %s%s%s to \n %s%s%s \nat approximate bitrate %d kbps (VBR encoding enabled)\n"),
fn?"\"":"", fn?fn:_("standard input"), fn?"\"":"",
outfn?"\"":"", outfn?outfn:_("standard output"), outfn?"\"":"",
bitrate);
}
}else{
if(qset>0){
if(managed>0){
fprintf(stderr, _("Encoding %s%s%s to \n %s%s%s \nat quality level %2.2f using constrained VBR "),
fn?"\"":"", fn?fn:_("standard input"), fn?"\"":"",
outfn?"\"":"", outfn?outfn:_("standard output"), outfn?"\"":"",
quality * 10);
print_brconstraints(min,max);
fprintf(stderr, "\n");
}else{
fprintf(stderr, _("Encoding %s%s%s to \n %s%s%s \nat quality %2.2f\n"),
fn?"\"":"", fn?fn:_("standard input"), fn?"\"":"",
outfn?"\"":"", outfn?outfn:_("standard output"), outfn?"\"":"",
quality * 10);
}
}else{
fprintf(stderr, _("Encoding %s%s%s to \n %s%s%s \nusing bitrate management "),
fn?"\"":"", fn?fn:_("standard input"), fn?"\"":"",
outfn?"\"":"", outfn?outfn:_("standard output"), outfn?"\"":"");
print_brconstraints(min,max);
fprintf(stderr, "\n");
}
}
}
void start_encode_null(char *fn, char *outfn, int bitrate, float quality, int qset,
int managed, int min, int max)
{
}
|