From 91e9b782cc9ea0252ab2b211b15e8da4a3043d5f Mon Sep 17 00:00:00 2001 From: deva Date: Sat, 2 Jul 2005 11:39:51 +0000 Subject: Added some audiocode. Moved libfame code out of mov_encoder --- src/Makefile.am | 4 + src/libfame_wrapper.cc | 265 +++++++++++++++++++++++++ src/libfame_wrapper.h | 80 ++++++++ src/liblame_wrapper.cc | 158 +++++++++++++++ src/liblame_wrapper.h | 77 ++++++++ src/miav_config.cc | 489 ++++++++++++++++++++++++++++++++++------------ src/miav_config.h | 42 +++- src/mov_encoder.cc | 251 ++---------------------- src/mov_encoder.h | 35 +--- src/mov_encoder_thread.cc | 6 +- 10 files changed, 1018 insertions(+), 389 deletions(-) create mode 100644 src/libfame_wrapper.cc create mode 100644 src/libfame_wrapper.h create mode 100644 src/liblame_wrapper.cc create mode 100644 src/liblame_wrapper.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 0afb24a..a611de5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,8 @@ miav_SOURCES = $(shell if [ $QT_CXXFLAGS ] ; then ../tools/MocList cc; fi ) \ info.cc \ info_console.cc \ info_gui.cc \ + libfame_wrapper.cc \ + liblame_wrapper.cc \ mainwindow.cc \ messagebox.cc \ miav.cc \ @@ -52,6 +54,8 @@ EXTRA_DIST = \ info.h \ info_console.h \ info_gui.h \ + libfame_wrapper.h \ + liblame_wrapper.h \ mainwindow.h \ messagebox.h \ miav.h \ diff --git a/src/libfame_wrapper.cc b/src/libfame_wrapper.cc new file mode 100644 index 0000000..ab60b36 --- /dev/null +++ b/src/libfame_wrapper.cc @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * libfame_wrapper.cc + * + * Sat Jul 2 11:11:31 CEST 2005 + * Copyright 2005 Bent Bisballe + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of MIaV. + * + * MIaV 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 of the License, or + * (at your option) any later version. + * + * MIaV 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 MIaV; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + * $Id$ + */ + +/* + * $Log$ + * Revision 1.1 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * + */ + +#include +#include "libfame_wrapper.h" + +#include + +#include "miav_config.h" +#include "frame.h" + +LibFAMEWrapper::LibFAMEWrapper(Info *i) +{ + info = i; + + // FIXME: Hmmm... should this be detected somewhere?! + int w = 720; + int h = 576; + + // Initialize yuv structure. + yuv.w = w; + yuv.h = h; + yuv.p = w; + yuv.y = new unsigned char [w*h * 2]; + yuv.u = new unsigned char [w*h];// [w*h/4] + yuv.v = new unsigned char [w*h];// [w*h/4] + + ////////////LIBDV STUFF/////////////// + + dvdecoder = NULL; // Initialize in encode method + + /////////LIBFAME STUFF/////////// + + // Allocate the output buffer. + +// fame_buffer = new unsigned char [FAME_BUFFER_SIZE]; + + /* + // Open output file + f=fopen(filename, "wb"); + if(!f) { + fprintf(stderr, "Failed to open output file [%s] due to the following error: %s", filename, strerror(errno)); + return; + } + */ + // Open a new session of the fame library. + // (If initialization was successful, it returns a non-null context which + // can then be used for subsequent library calls.) + fame_context = fame_open(); + if(!fame_context) { + info->error("Unable to open FAME context, due to the following error: %s", strerror(errno)); + return; + } + + /* + typedef struct _fame_parameters_ { + int width; // width of the video sequence + int height; // height of the video sequence + char const *coding; // coding sequence + int quality; // video quality + int slices_per_frame; // number of slices per frame + unsigned int frames_per_sequence; // number of frames per sequence + int frame_rate_num; // numerator of frames per second + int frame_rate_den; // denominator of frames per second + unsigned int shape_quality; // binary shape quality + unsigned int search_range; // motion estimation search range + unsigned char verbose; // verbosity + } fame_parameters_t; + */ + // width and height specify the size of each frames of the video sequence. + // Both must be multiple of 16. width and height must be less than 4096x4096 + fame_par.width = 720; + fame_par.height = 576; + + // coding is a string of I, P or B characters representing the sequence of + // frames the encoder must produce. I frames are intra-coded frames (similar + // to JPEG), whereas P and B frames are motion compressed, respectively + // predicted from past reference (I or P) frame, or bidirectionally predicted + // from past and future reference frame. + fame_par.coding = config->readString("frame_sequence")->c_str(); + + // quality is a percentage, which controls compression versus quality. + fame_par.quality = config->readInt("frame_quality"); + + // Bitrate + fame_par.bitrate = 0; // video bitrate (0=VBR) + + // slices_per_frame is the number of frame slices per frame. More slices provide + // better error recovery. There must be at least one slice per frame, and at most + // height / 16 + fame_par.slices_per_frame = 1;//fame_par.height / 16; + + // frames_per_sequence is the maximum number of frames contained in a video + // sequence. + fame_par.frames_per_sequence = 0xffffffff; // Unlimited length + + // frame_rate_num/frame_rate_den specify the number of frames per second for + // playback. + fame_par.frame_rate_num = 25; // 25 / 1 fps = 25 fps + fame_par.frame_rate_den = 1; + + // shape_quality is percentage determing the average binary shape accuracy in + // video with arbitrary shape. + fame_par.shape_quality = 100; // Original shape + + // search_range specifies the motion estimation search range in pixel unit. + // Small search ranges work best with slow motion videos, whereas larger search + // ranges are rather for fast motion videos. + fame_par.search_range = 0; // Adaptive search range + + // verbose when set to 1 outputs information on copyright, modules used and + // current frame on standard error. + fame_par.verbose = 0; + + static const char profilename[] = "MIaV\0"; + fame_par.profile = profilename; // profile name + fame_par.total_frames = 0; // total number of frames + + if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg4") == 0) { + + info->info("Using mpeg4 compression."); + fame_object_t *object; + + object = fame_get_object(fame_context, "profile/mpeg4/simple"); + if(object) fame_register(fame_context, "profile", object); + + } else if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg1") == 0) { + + info->info("Using mpeg1 compression."); + fame_object_t *object; + + object = fame_get_object(fame_context, "profile/mpeg1"); + if(object) fame_register(fame_context, "profile", object); + + } else if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg1") == 0) { + } else { + info->info("Using default (mpeg1) compression."); + } + + fame_init(fame_context, &fame_par, fame_buffer, FAME_BUFFER_SIZE); +} + +LibFAMEWrapper::~LibFAMEWrapper() +{ + delete [] yuv.y; + delete [] yuv.u; + delete [] yuv.v; +} + +Frame *LibFAMEWrapper::encode(Frame *dvframe) +{ + // if(!f) return; // The file was not opened. + + // Decode DV Frame to YUV422 + int w = 720; + int h = 576; + + unsigned char *pixels[3]; + int pitches[3]; + + if(!dvdecoder) { + dvdecoder = dv_decoder_new(FALSE/*this value is unused*/, FALSE, FALSE); + dvdecoder->quality = DV_QUALITY_BEST; + + dv_parse_header(dvdecoder, dvframe->data); + //dv_parse_packs(decoder, frame->data); // Not needed anyway! + + dvdecoder->system = e_dv_system_625_50; // PAL lines, PAL framerate + dvdecoder->sampling = e_dv_sample_422; // 4 bytes y, 2 bytes u, 2 bytes v + dvdecoder->std = e_dv_std_iec_61834; + dvdecoder->num_dif_seqs = 12; + } + + pixels[ 0 ] = picture; // We use this as the output buffer + pitches[ 0 ] = w * 2; + + dv_decode_full_frame(dvdecoder, + dvframe->data, + e_dv_color_yuv, + pixels, + pitches); + + // Convert YUV422 to YUV420p + int w2 = w / 2; + uint8_t *y = yuv.y; + uint8_t *cb = yuv.u; + uint8_t *cr = yuv.v; + uint8_t *p = picture; + + for ( int i = 0; i < h; i += 2 ) { + // process two scanlines (one from each field, interleaved) + for ( int j = 0; j < w2; j++ ) { + // packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] + *( y++ ) = *( p++ ); + *( cb++ ) = *( p++ ); + *( y++ ) = *( p++ ); + *( cr++ ) = *( p++ ); + } + + // process next two scanlines (one from each field, interleaved) + for ( int j = 0; j < w2; j++ ) { + // skip every second line for U and V + *( y++ ) = *( p++ ); + p++; + *( y++ ) = *( p++ ); + p++; + } + } + + // Allocate a new frame for the output + Frame *output = new Frame(NULL, FAME_BUFFER_SIZE); + output->size = 0; + unsigned char* pt = output->data; + + // Encode YUV frame and write it to disk. + fame_start_frame(fame_context, &yuv, 0); + int written; + + while((written = fame_encode_slice(fame_context))) { + // fwrite(fame_buffer, written, 1, f); + memcpy(pt, fame_buffer, written); + pt += written; + output->size += written; + } + fame_end_frame(fame_context,0); + + return output; +} + diff --git a/src/libfame_wrapper.h b/src/libfame_wrapper.h new file mode 100644 index 0000000..9f3e0e8 --- /dev/null +++ b/src/libfame_wrapper.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * libfame_wrapper.h + * + * Sat Jul 2 11:11:31 CEST 2005 + * Copyright 2005 Bent Bisballe + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of MIaV. + * + * MIaV 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 of the License, or + * (at your option) any later version. + * + * MIaV 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 MIaV; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + * $Id$ + */ + +/* + * $Log$ + * Revision 1.1 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * + */ + +#include +#ifndef __MIAV_LIBFAME_WRAPPER_H__ +#define __MIAV_LIBFAME_WRAPPER_H__ + +// Use libfame +#include + +// Use libdv +#include +#include + +#include "frame.h" +#include "info.h" + +// size specifies the length of the buffer. +#define FAME_BUFFER_SIZE (1024*1024) // FIXME: One size fits all... + +class LibFAMEWrapper { +public: + LibFAMEWrapper(Info *info); + ~LibFAMEWrapper(); + + Frame *encode(Frame *dvframe); + +private: + Info* info; + + // libFAME encoder + // unsigned char *fame_buffer; + fame_parameters_t fame_par; + fame_context_t *fame_context; + fame_yuv_t yuv; + unsigned char fame_buffer[FAME_BUFFER_SIZE]; + + // libdv decoder + dv_decoder_t *dvdecoder; + + unsigned char picture[FAME_BUFFER_SIZE]; +}; + +#endif/*__MIAV_LIBFAME_WRAPPER_H__*/ diff --git a/src/liblame_wrapper.cc b/src/liblame_wrapper.cc new file mode 100644 index 0000000..751c41b --- /dev/null +++ b/src/liblame_wrapper.cc @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * liblame_wrapper.cc + * + * Sat Jul 2 11:11:34 CEST 2005 + * Copyright 2005 Bent Bisballe + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of MIaV. + * + * MIaV 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 of the License, or + * (at your option) any later version. + * + * MIaV 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 MIaV; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + * $Id$ + */ + +/* + * $Log$ + * Revision 1.1 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * + */ + +#include +#include "liblame_wrapper.h" + +LibLAMEWrapper::LibLAMEWrapper(Info *i) +{ + info = i; + + // Init library. + lamegf = lame_init(); + if(!lamegf) { + info->error("LAME initialization failed (due to malloc failure!)"); + } + + lame_init_params(lamegf); + + decoder = NULL; + + audio_buffer[0] = new int16_t[AUDIO_BUFFER_SIZE]; + audio_buffer[1] = new int16_t[AUDIO_BUFFER_SIZE]; + +} + +LibLAMEWrapper::~LibLAMEWrapper() +{ + delete audio_buffer[0]; + delete audio_buffer[1]; + +} + +Frame *LibLAMEWrapper::encode(Frame *dvframe) +{ + + if(!decoder) { + decoder = dv_decoder_new(FALSE/*this value is unused*/, FALSE, FALSE); + decoder->quality = DV_QUALITY_BEST; + + dv_parse_header(decoder, dvframe->data); + + decoder->system = e_dv_system_625_50; // PAL lines, PAL framerate + decoder->sampling = e_dv_sample_422; // 4 bytes y, 2 bytes u, 2 bytes v + decoder->std = e_dv_std_iec_61834; + decoder->num_dif_seqs = 12; + } + + // See + // http://www.koders.com/cpp/fidE614E999154E2B4A813DA272C4421633063C78CA.aspx + // line 769 + + /** + * Decode audio using libdv + */ + // int n, i; + // int16_t* s = ( int16_t * ) sound; + dv_decode_full_audio( decoder, dvframe->data, audio_buffer ); + /* + for ( n = 0; n < SAMPLES; ++n ) + for ( i = 0; i < CHANNELS; i++ ) + *s++ = audio_buffer[ i ][ n ]; + */ + +/* + * input pcm data, output (maybe) mp3 frames. + * This routine handles all buffering, resampling and filtering for you. + * + * The required mp3buf_size can be computed from num_samples, + * samplerate and encoding rate, but here is a worst case estimate: + * + * mp3buf_size in bytes = 1.25*num_samples + 7200 + * + * I think a tighter bound could be: (mt, March 2000) + * MPEG1: + * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 + * MPEG2: + * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 + * + * but test first if you use that! + * + * set mp3buf_size = 0 and LAME will not check if mp3buf_size is + * large enough. + * + * NOTE: + * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels + * will be averaged into the L channel before encoding only the L channel + * This will overwrite the data in buffer_l[] and buffer_r[]. + * +*/ + + Frame* audio_frame = new Frame(NULL, (int)(1.25 * SAMPLES + 7200)); + + const short int *buffer_l = audio_buffer[0]; // PCM data for left channel + const short int *buffer_r = audio_buffer[0]; // PCM data for right channel + const int nsamples = SAMPLES; // number of samples per channel + unsigned char* mp3buf = audio_frame->data; // pointer to encoded MP3 stream + const int mp3buf_size = audio_frame->size; // number of valid octets in this + + int val = lame_encode_buffer(lamegf, buffer_l, buffer_r, nsamples, mp3buf, mp3buf_size); + + if(val < 0) { + switch(val) { + case -1: // mp3buf was too small + info->error("Lame encoding failed, mp3buf was too small."); + break; + case -2: // malloc() problem + info->error("Lame encoding failed, due to malloc() problem."); + break; + case -3: // lame_init_params() not called + info->error("Lame encoding failed, lame_init_params() not called."); + break; + case -4: // psycho acoustic problems + info->error("Lame encoding failed, due to psycho acoustic problems."); + break; + default: + info->error("Lame encoding failed, due to unknown error."); + break; + } + } + + return audio_frame; +} diff --git a/src/liblame_wrapper.h b/src/liblame_wrapper.h new file mode 100644 index 0000000..2d3a769 --- /dev/null +++ b/src/liblame_wrapper.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * liblame_wrapper.h + * + * Sat Jul 2 11:11:34 CEST 2005 + * Copyright 2005 Bent Bisballe + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of MIaV. + * + * MIaV 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 of the License, or + * (at your option) any later version. + * + * MIaV 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 MIaV; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + * $Id$ + */ + +/* + * $Log$ + * Revision 1.1 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * + */ + +#include +#ifndef __MIAV_LIBLAME_WRAPPER_H__ +#define __MIAV_LIBLAME_WRAPPER_H__ + +// Use libdv +#include +#include + +// Use liblame +#include + +#include "frame.h" +#include "info.h" + +#define CHANNELS 2 +#define SAMPLES 44100 +#define AUDIO_BUFFER_SIZE SAMPLES/25*sizeof(int16_t) + +class LibLAMEWrapper { +public: + LibLAMEWrapper(Info *info); + ~LibLAMEWrapper(); + + Frame *encode(Frame *dvframe); + +private: + Info *info; + + // LAME stuff + lame_global_flags *lamegf; + + // libdv stuff + dv_decoder_t *decoder; + int16_t *audio_buffer[2]; + +}; + +#endif/*__MIAV_LIBLAME_WRAPPER_H__*/ diff --git a/src/miav_config.cc b/src/miav_config.cc index f7d56ce..f59b6b3 100644 --- a/src/miav_config.cc +++ b/src/miav_config.cc @@ -31,6 +31,10 @@ /* * $Log$ + * Revision 1.11 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * * Revision 1.10 2005/06/14 12:29:40 deva * Incorporated the use of the Info object everywhere... also using the log functionality. * @@ -65,7 +69,7 @@ MiavConfig::MiavConfig(char *file, Info *i) return; } fseek(fp, 0, SEEK_END); - int fsz = ftell(fp); + int fsz = ftell(fp) + 1; fseek(fp, 0, SEEK_SET); char *raw = (char*)calloc(fsz, 1); @@ -73,7 +77,8 @@ MiavConfig::MiavConfig(char *file, Info *i) fclose(fp); - parse(raw); + configs = parse(raw); + free(raw); } @@ -93,173 +98,384 @@ MiavConfig::~MiavConfig() /** * Prints a reasonable error message when a parse error occurres. */ -_cfg *MiavConfig::parseError(char* msg, char* line) +void MiavConfig::parseError(char* msg, _cfg* cfg) { - if(info) info->error("Error parsing file %s at line:\n\t%s\n\t%s\n", filename.c_str(), line, msg); - else fprintf(stderr, "Error parsing file %s at line:\n\t%s\n\t%s\n", filename.c_str(), line, msg); - return NULL; + if(info) info->error("Error parsing file %s at line %d:\n\t%s\n\t%s\n", + filename.c_str(), + cfg->line, + cfg->orig, + msg); + else fprintf(stderr, "Error parsing file %s at line %d:\n\t%s\n\t%s\n", + filename.c_str(), + cfg->line, + cfg->orig, + msg); } -/** - * Adds one configuration entry, from a single zero terminated line. - */ -_cfg *MiavConfig::addConfig(_cfg *parent, char* conf) +_cfg* MiavConfig::readLines(char* raw) { - // Check for wellformed input: - // Check for = - if(strstr(conf, "=") == 0) return parseError("Missing '='", conf); - /* - if(strstr(conf, "\"")) { - if(strstr(conf, "=") > strstr(conf, "\"")) - return parseError("Missing '=', first occurrence inside string", conf); - } - */ + int line = 1; + + _cfg *first = (_cfg*)calloc(1, sizeof(_cfg)); + _cfg *current = first; + _cfg *next = NULL; - // Check for nonempty left side - if(strstr(conf, "=") == conf) return parseError("Empty left side", conf); + char *nl = strchr(raw, '\n'); - // Check for nonempty right side - if(strstr(conf, "=") == conf + strlen(conf) - 1) return parseError("Empty right side.", conf); + while(nl != NULL) { + int len = nl - raw; - // Parse this wellformed input. - _cfg *cfg; + current->line = line; - cfg = (_cfg*) malloc(sizeof(_cfg)); - if(!parent) configs = cfg; + current->orig = (char*) calloc(len + 1, 1); + strncpy(current->orig, raw, len); - int namelen = strchr(conf, '=') - conf; - char* name = (char*)calloc(namelen + 1, 1); - strncpy(name, conf, namelen); + // Find next newline + raw = nl+1; + nl = strchr(raw, '\n'); - int vallen = conf + strlen(conf) - (strchr(conf, '=') + 1); - char* val = (char*)calloc(vallen + 1, 1); - strncpy(val, conf + strlen(conf) - vallen, vallen); + line++; - // TODO: Check valid rightside (true, false, number or "..") + // Add _cfg + if(nl != NULL) { + next = (_cfg*)calloc(1, sizeof(_cfg)); + current->next = next; + current = next; + } else { + current->next = NULL; + } + } - cfg->name = new string((const char*)name); - free(name); + return first; +} - cfg->stringval = new string((const char*)val); - cfg->intval = atoi(val); - cfg->floatval = atof(val); - cfg->boolval = atoi(val) != 0; - free(val); +_cfg* MiavConfig::parseLines(_cfg *cfg) +{ + if(cfg == NULL) return NULL; - cfg->next = NULL; + char *l = cfg->left = (char*)calloc(1, strlen(cfg->orig)); + char *r = cfg->right = (char*)calloc(1, strlen(cfg->orig)); - if(parent) parent->next = cfg; - return cfg; -} + char *p = cfg->orig; -/** - * Main parse function, iterates the lines of the file. - */ -int MiavConfig::parse(char* raw) -{ - // Strip the string - char *conf = strip(raw); - char *conf_end = conf + strlen(conf); - char *start = conf; - char *p; - - _cfg *cfg = NULL; - - // Iterate the lines in the string - for(p = conf; p < conf_end; p++) { - if(*p == '\n') { - *p = '\0'; - if(!(cfg = addConfig(cfg, start))) return 1; - start = p+1; + // Skip leftmost whitespace + while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { + p++; + } + + // Empty line, with whitespaces + if(p == cfg->orig + strlen(cfg->orig)) { + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + // Parse left side + while(p < cfg->orig + strlen(cfg->orig) && !strchr("\t ", *p)) { + if(strchr("#", *p)) { + if(l != cfg->left) parseError("Incomplete line.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + if(strchr("=", *p)) break; + + if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_", *p)) { + *l = *p; + l++; + } else { + char buf[256]; + sprintf(buf, "Invalid left hand side character at [%s].", p); + parseError(buf, cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); } + + p++; } - // Allocated in strip - free(conf); - return 0; -} -/** - * Strip all unwanted data from the string, initial to parsing. - */ -char* MiavConfig::strip(char* conf) -{ - // Freed in parse!!! - char *stripped = (char*)calloc(strlen(conf) + 2, 1); - char *r; - char *w = stripped; - - bool instring = false; - bool incomment = false; - - // Iterate over the characters in the input string. - for(r = conf; r < conf + strlen(conf); r++) { - if(strchr("#", *r)) incomment = true; - if(strchr("\n", *r)) incomment = false; - - if(!incomment) { - if(instring) { - // When in a string, accept anything, except ". - if(*r != '\"') { - *w = *r; - w++; - } - } else { - // Only copy valid characters - if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_,.=", *r)) { - // Change comma into dot - if(*r == ',') *r = '.'; - *w = *r; - w++; - } - // We don't want double newlines and initial newline! - if((*r == '\n') && (*(w-1) != '\n') && (w != stripped)) { - *w = *r; - w++; - } + // Skip whitespace + while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { + p++; + } + + if(*p != '=') { + parseError("Expected '='.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + p++; // Get past the '=' + + // Skip whitespace + while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { + p++; + } + + // Parse right hand side + int instring = 0; + while(p < cfg->orig + strlen(cfg->orig) && !(strchr("\t ", *p) && instring != 1)) { + if(*p == '\"') instring++; + if(instring > 2) { + parseError("Too many '\"'.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + if(instring == 1) { + // Accept all chars + *r= *p; + r++; + } else { + // Accept only those chars valid for the data types. + if(strchr("truefalseyesnoTRUEFALSEYESNO1234567890\",.-", *p)) { + if(*p == ',') *r= '.'; + *r = *p; + r++; + } else if(!strchr("\n", *p)) { + char buf[256]; + sprintf(buf, "Invalid right hand side character at [%s].", p); + parseError(buf, cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); } + if(*p == '#') break; } - if(strchr("\"", *r)) instring = !instring; + p++; + } + + // Skip whitespace + while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { + p++; + } + + // Detect if whitespace ocurred inside righthand value. + if(p != cfg->orig + strlen(cfg->orig)) { + parseError("Invalid use of whitespace.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + // Check for instring (string not ended) + if(instring == 1) { + parseError("String not closed.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + // Check for empty line + if(l == cfg->left && r == cfg->right) { + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); + } + + // Check for empty left side. + if(l == cfg->left) { + parseError("Empty left side.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); } - // If we are not ending on a newline, we better append one - if(*(w-1) != '\n') { - *w = '\n'; - w++; + // Check for empty right side. + if(r == cfg->right) { + parseError("Empty right side.", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return parseLines(next); } - // Make sure we are nullterminated. - *w = '\0'; + cfg->next = parseLines(cfg->next); + return cfg; +} + + +_cfg *MiavConfig::createSemantics(_cfg *cfg) { + if(cfg == NULL) return NULL; + + cfg->type = CONFIG_UNKNOWN; + + // Boolean - true + if(strcasecmp(cfg->right, "yes") == 0 || + strcasecmp(cfg->right, "true") == 0) { + cfg->type = CONFIG_BOOL; + cfg->boolval = true; + } + + // Boolean - false + if(strcasecmp(cfg->right, "no") == 0 || + strcasecmp(cfg->right, "false") == 0) { + cfg->type = CONFIG_BOOL; + cfg->boolval = false; + } - return stripped; + // String + if(cfg->right[0] == '\"') { + cfg->type = CONFIG_STRING; + cfg->right[strlen(cfg->right) - 1] = '\0'; + cfg->stringval = new string(cfg->right + 1); + + } + + // Number + bool number = true; + char *p = cfg->right; + while(p < cfg->right + strlen(cfg->right)) { + if(!strchr("01234567890.-", *p)) number = false; + p++; + } + + // Integer + if(number && strstr(cfg->right, ".") == NULL ) { + cfg->type = CONFIG_INT; + cfg->intval = atoi(cfg->right); + } + + // Float + if(number && strstr(cfg->right, ".") != NULL) { + cfg->type = CONFIG_FLOAT; + cfg->floatval = atof(cfg->right); + } + + if(cfg->type == CONFIG_UNKNOWN) { + parseError("Unknown type (see 'man miav.conf' for valid right hand sides).", cfg); + _cfg* next = cfg->next; + free(cfg->orig); + free(cfg->left); + free(cfg->right); + free(cfg); + return createSemantics(next); + } + + // Create name + cfg->name = new string(cfg->left); + + cfg->next = createSemantics(cfg->next); + return cfg; +} + + +_cfg* MiavConfig::parse(char* raw) +{ + _cfg *first = readLines(raw); + first = parseLines(first); + + first = createSemantics(first); + + /* + _cfg* cfg = first; + while(cfg) { + printf("Node:\n"); + printf("\tLine: [%d]\n", cfg->line); + printf("\tOrig: [%s]\n", cfg->orig); + printf("\tLeft: [%s]\n", cfg->left); + printf("\tRight: [%s]\n", cfg->right); + + switch(cfg->type) { + case CONFIG_INT: + printf("\tInt value: %d\n", cfg->intval); + break; + case CONFIG_BOOL: + printf("\tBool value: %d\n", cfg->boolval); + break; + case CONFIG_FLOAT: + printf("\tFloat value: %f\n", cfg->floatval); + break; + case CONFIG_STRING: + printf("\tString value: %s\n", cfg->stringval->c_str()); + break; + case CONFIG_UNKNOWN: + printf("\tUnknown type: %s\n", cfg->right); + break; + } + + cfg= cfg->next; + } + */ + return first; } int MiavConfig::readInt(char *node) { _cfg* n = findNode(node); - if(n) return n->intval; - else return 0; + if(n) { + if(n->type == CONFIG_INT) return n->intval; + parseError("Expected integer.", n); + } + return 0; } bool MiavConfig::readBool(char *node) { - _cfg* n = findNode(node); - if(n) return n->boolval; - else return false; + _cfg* n = findNode(node); + if(n) { + if(n->type == CONFIG_BOOL) return n->boolval; + if(n->type == CONFIG_INT) return (n->intval != 0); + parseError("Expected boolean.", n); + } + return false; } string *MiavConfig::readString(char *node) { _cfg* n = findNode(node); - if(n) return n->stringval; - else return &emptyString; + if(n) { + if(n->type == CONFIG_STRING) return n->stringval; + parseError("Expected string.", n); + } + return &emptyString; } float MiavConfig::readFloat(char *node) { _cfg* n = findNode(node); - if(n) return n->floatval; - else return 0.0f; + if(n) { + if(n->type == CONFIG_FLOAT) return n->floatval; + if(n->type == CONFIG_INT) return (float)n->intval; + parseError("Expected float.", n); + } + return 0.0f; } _cfg *MiavConfig::findNode(char* node) @@ -270,8 +486,31 @@ _cfg *MiavConfig::findNode(char* node) if(!strcmp(node, cfg->name->c_str())) return cfg; cfg = cfg->next; } - if(info) info->error("Request for nonexisting node \"%s\"!\n", node); - else fprintf(stderr, "Request for nonexisting node \"%s\"!\n", node); + if(info) info->error("Missing line in configuration file: \"%s\"!\n", node); + else fprintf(stderr, "Missing line in configuration file: \"%s\"!\n", node); return NULL; } + +#ifdef __TEST_MIAV_CONFIG + +int main(int argc, char *argv[]) { + if(argc < 2) { + fprintf(stderr, "usage:\n\tmiav_config [filename]\n"); + return 1; + } + + MiavConfig cfg(argv[1]); + printf("Server user: [%s]\n", cfg.readString("server_user")->c_str()); + printf("Resolution: [%f]\n", cfg.readFloat("screensize")); + printf("Resolution (as int): [%d]\n", cfg.readInt("screensize")); + printf("Width: [%d]\n", cfg.readInt("pixel_width")); + printf("Width (as float): [%f]\n", cfg.readFloat("pixel_width")); + printf("Frame quality: [%d]\n", cfg.readInt("frame_quality")); + printf("Skip frames: [%d]\n", cfg.readBool("player_skip_frames")); + printf("Skip frames (as int): [%d]\n", cfg.readInt("player_skip_frames")); + printf("Frame quality (as bool): [%d]\n", cfg.readBool("frame_quality")); + +} + +#endif/* __TEST_MIAV_CONFIG*/ diff --git a/src/miav_config.h b/src/miav_config.h index 4bbcc59..a4c432a 100644 --- a/src/miav_config.h +++ b/src/miav_config.h @@ -31,6 +31,10 @@ /* * $Log$ + * Revision 1.8 2005/07/02 11:39:51 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * * Revision 1.7 2005/06/14 12:29:40 deva * Incorporated the use of the Info object everywhere... also using the log functionality. * @@ -52,18 +56,36 @@ using namespace std; // Cyclic include :( class Info; +typedef enum { + CONFIG_UNKNOWN, + CONFIG_INT, + CONFIG_BOOL, + CONFIG_FLOAT, + CONFIG_STRING +} ConfigType; + + typedef struct __cfg { + // For parsing + char* orig; + int line; + char* left; + char* right; + + // For traversal string *name; - bool boolval; + ConfigType type; int intval; - string *stringval; + bool boolval; float floatval; + string *stringval; + struct __cfg* next; } _cfg; class MiavConfig { public: - MiavConfig(char *file, Info *info); + MiavConfig(char *file, Info *info = NULL); ~MiavConfig(); int readInt(char *node); @@ -73,13 +95,21 @@ public: private: Info *info; + string filename; + _cfg *createSemantics(_cfg *cfg); + _cfg* readLines(char* raw); + _cfg* parseLines(_cfg *cfg); + _cfg *parse(char* raw); string emptyString; - string filename; + + +#if 0 _cfg *addConfig(_cfg *parent, char* conf); - int parse(char* conf); char *strip(char* conf); - _cfg *parseError(char* msg, char* line); +#endif + + void parseError(char* msg, _cfg *cfg); _cfg *findNode(char* node); _cfg *configs; }; diff --git a/src/mov_encoder.cc b/src/mov_encoder.cc index 3aa7a49..38622c6 100644 --- a/src/mov_encoder.cc +++ b/src/mov_encoder.cc @@ -39,6 +39,10 @@ /* * $Log$ + * Revision 1.33 2005/07/02 11:39:52 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * * Revision 1.32 2005/06/19 20:04:43 deva * ImgEncoder now uses the file class for output, through jpeg_mem_dest. * @@ -140,131 +144,8 @@ MovEncoder::MovEncoder(sem_t *r_sem, info = i; info->info("MovEncoder"); - // FIXME: Hmmm... should this be detected somewhere?! - int w = 720; - int h = 576; - - // Initialize yuv strucutre. - yuv.w = w; - yuv.h = h; - yuv.p = w; - yuv.y = new unsigned char [w*h * 2]; - yuv.u = new unsigned char [w*h];// [w*h/4] - yuv.v = new unsigned char [w*h];// [w*h/4] - - ////////////LIBDV STUFF/////////////// - - dvdecoder = NULL; // Initialize in encode method - - /////////LIBFAME STUFF/////////// - - // Allocate the output buffer. - -// fame_buffer = new unsigned char [FAME_BUFFER_SIZE]; - - /* - // Open output file - f=fopen(filename, "wb"); - if(!f) { - fprintf(stderr, "Failed to open output file [%s] due to the following error: %s", filename, strerror(errno)); - return; - } - */ - // Open a new session of the fame library. - // (If initialization was successful, it returns a non-null context which - // can then be used for subsequent library calls.) - fame_context = fame_open(); - if(!fame_context) { - info->error("Unable to open FAME context, due to the following error: %s", strerror(errno)); - return; - } - - /* - typedef struct _fame_parameters_ { - int width; // width of the video sequence - int height; // height of the video sequence - char const *coding; // coding sequence - int quality; // video quality - int slices_per_frame; // number of slices per frame - unsigned int frames_per_sequence; // number of frames per sequence - int frame_rate_num; // numerator of frames per second - int frame_rate_den; // denominator of frames per second - unsigned int shape_quality; // binary shape quality - unsigned int search_range; // motion estimation search range - unsigned char verbose; // verbosity - } fame_parameters_t; - */ - // width and height specify the size of each frames of the video sequence. - // Both must be multiple of 16. width and height must be less than 4096x4096 - fame_par.width = 720; - fame_par.height = 576; - - // coding is a string of I, P or B characters representing the sequence of - // frames the encoder must produce. I frames are intra-coded frames (similar - // to JPEG), whereas P and B frames are motion compressed, respectively - // predicted from past reference (I or P) frame, or bidirectionally predicted - // from past and future reference frame. - fame_par.coding = config->readString("frame_sequence")->c_str(); - - // quality is a percentage, which controls compression versus quality. - fame_par.quality = config->readInt("frame_quality"); - - // Bitrate - fame_par.bitrate = 0; // video bitrate (0=VBR) - - // slices_per_frame is the number of frame slices per frame. More slices provide - // better error recovery. There must be at least one slice per frame, and at most - // height / 16 - fame_par.slices_per_frame = 1;//fame_par.height / 16; - - // frames_per_sequence is the maximum number of frames contained in a video - // sequence. - fame_par.frames_per_sequence = 0xffffffff; // Unlimited length - - // frame_rate_num/frame_rate_den specify the number of frames per second for - // playback. - fame_par.frame_rate_num = 25; // 25 / 1 fps = 25 fps - fame_par.frame_rate_den = 1; - - // shape_quality is percentage determing the average binary shape accuracy in - // video with arbitrary shape. - fame_par.shape_quality = 100; // Original shape - - // search_range specifies the motion estimation search range in pixel unit. - // Small search ranges work best with slow motion videos, whereas larger search - // ranges are rather for fast motion videos. - fame_par.search_range = 0; // Adaptive search range - - // verbose when set to 1 outputs information on copyright, modules used and - // current frame on standard error. - fame_par.verbose = 0; - - static const char profilename[] = "MIaV\0"; - fame_par.profile = profilename; // profile name - fame_par.total_frames = 0; // total number of frames - - if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg4") == 0) { - - info->info("Using mpeg4 compression."); - fame_object_t *object; - - object = fame_get_object(fame_context, "profile/mpeg4/simple"); - if(object) fame_register(fame_context, "profile", object); - - } else if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg1") == 0) { - - info->info("Using mpeg1 compression."); - fame_object_t *object; - - object = fame_get_object(fame_context, "profile/mpeg1"); - if(object) fame_register(fame_context, "profile", object); - - } else if(strcmp(config->readString("encoding_codec")->c_str(), "mpeg1") == 0) { - } else { - info->info("Using default (mpeg1) compression."); - } - - fame_init(fame_context, &fame_par, fame_buffer, FAME_BUFFER_SIZE); + fame = new LibFAMEWrapper(info); + lame = new LibLAMEWrapper(info); running = true; @@ -283,108 +164,9 @@ MovEncoder::MovEncoder(sem_t *r_sem, MovEncoder::~MovEncoder() { info->info("~MovEncoder"); - /* - if(f) { // The file was opened. - int written = fame_close(fame_context); - fwrite(fame_buffer, written, 1, f); - fclose(f); - } - */ - // delete [] fame_buffer; - delete [] yuv.y; - delete [] yuv.u; - delete [] yuv.v; -} - -Frame *MovEncoder::encode(Frame *dvframe) -{ - return encode_video(dvframe); - // encode_audio(dvframe); -} - -Frame *MovEncoder::encode_video(Frame *dvframe) -{ - // if(!f) return; // The file was not opened. - - // Decode DV Frame to YUV422 - int w = 720; - int h = 576; - - unsigned char *pixels[3]; - int pitches[3]; - - if(!dvdecoder) { - dvdecoder = dv_decoder_new(FALSE/*this value is unused*/, FALSE, FALSE); - dvdecoder->quality = DV_QUALITY_BEST; - - dv_parse_header(dvdecoder, dvframe->data); - //dv_parse_packs(decoder, frame->data); // Not needed anyway! - - dvdecoder->system = e_dv_system_625_50; // PAL lines, PAL framerate - dvdecoder->sampling = e_dv_sample_422; // 4 bytes y, 2 bytes u, 2 bytes v - dvdecoder->std = e_dv_std_iec_61834; - dvdecoder->num_dif_seqs = 12; - } - - pixels[ 0 ] = picture; // We use this as the output buffer - pitches[ 0 ] = w * 2; - - dv_decode_full_frame(dvdecoder, - dvframe->data, - e_dv_color_yuv, - pixels, - pitches); - - // Convert YUV422 to YUV420p - int w2 = w / 2; - uint8_t *y = yuv.y; - uint8_t *cb = yuv.u; - uint8_t *cr = yuv.v; - uint8_t *p = picture; - - for ( int i = 0; i < h; i += 2 ) { - // process two scanlines (one from each field, interleaved) - for ( int j = 0; j < w2; j++ ) { - // packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] - *( y++ ) = *( p++ ); - *( cb++ ) = *( p++ ); - *( y++ ) = *( p++ ); - *( cr++ ) = *( p++ ); - } - - // process next two scanlines (one from each field, interleaved) - for ( int j = 0; j < w2; j++ ) { - // skip every second line for U and V - *( y++ ) = *( p++ ); - p++; - *( y++ ) = *( p++ ); - p++; - } - } - - // Allocate a new frame for the output - Frame *output = new Frame(NULL, FAME_BUFFER_SIZE); - output->size = 0; - unsigned char* pt = output->data; - - // Encode YUV frame and write it to disk. - fame_start_frame(fame_context, &yuv, 0); - int written; - while((written = fame_encode_slice(fame_context))) { - // fwrite(fame_buffer, written, 1, f); - memcpy(pt, fame_buffer, written); - pt += written; - output->size += written; - } - fame_end_frame(fame_context,0); - - return output; -} - -void MovEncoder::encode_audio(Frame *dvframe) -{ - // TODO: Do some audio stuff here sometime! + delete fame; + delete lame; } // this runs in a thread @@ -398,10 +180,10 @@ void MovEncoder::thread_main() // Run with slightly lower priority than MovEncoderWriter nice(1); - FrameVector *item; Frame *in_frame; - Frame *out_frame; + Frame *out_v_frame; + Frame *out_a_frame; while(running) { sem_wait(input_sem); @@ -419,14 +201,21 @@ void MovEncoder::thread_main() if(item) { for(unsigned int cnt = 0; cnt < item->size(); cnt++) { in_frame = item->at(cnt); - out_frame = encode(in_frame); - out_frame->number = in_frame->number; + + // Encode video + out_v_frame = fame->encode(in_frame); + out_v_frame->number = in_frame->number+1; + // Encode audio + out_a_frame = lame->encode(in_frame); + out_a_frame->number = in_frame->number; + delete in_frame; // Lock output mutex pthread_mutex_lock(output_mutex); - outputqueue->push(out_frame); + outputqueue->push(out_v_frame); + outputqueue->push(out_a_frame); outsize = outputqueue->size(); diff --git a/src/mov_encoder.h b/src/mov_encoder.h index 76f0c7a..a54b145 100644 --- a/src/mov_encoder.h +++ b/src/mov_encoder.h @@ -36,6 +36,10 @@ /* * $Log$ + * Revision 1.14 2005/07/02 11:39:52 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * * Revision 1.13 2005/06/16 21:54:22 deva * *** empty log message *** * @@ -77,14 +81,6 @@ #include #include - -// Use libfame -#include - -// Use libdv -#include -#include - #include using namespace std; @@ -96,8 +92,8 @@ using namespace std; #include "info.h" -// size specifies the length of the buffer. -#define FAME_BUFFER_SIZE (1024*1024) // FIXME: One size fits all... +#include "libfame_wrapper.h" +#include "liblame_wrapper.h" class MovEncoder : public Thread { public: @@ -106,13 +102,15 @@ public: FramePriorityQueue *out, sem_t *out_sem, pthread_mutex_t *out_mutex, Info *info); ~MovEncoder(); - Frame* encode(Frame *frame); void thread_main(); volatile bool running; private: + LibFAMEWrapper *fame; + LibLAMEWrapper *lame; + Info *info; // Input/Output queues @@ -127,21 +125,6 @@ private: pthread_mutex_t *input_mutex; pthread_mutex_t *output_mutex; - - Frame *encode_video(Frame *frame); - void encode_audio(Frame *frame); - - // libFAME encoder - // unsigned char *fame_buffer; - fame_parameters_t fame_par; - fame_context_t *fame_context; - fame_yuv_t yuv; - unsigned char fame_buffer[FAME_BUFFER_SIZE]; - - // libdv decoder - dv_decoder_t *dvdecoder; - - unsigned char picture[FAME_BUFFER_SIZE]; }; #endif diff --git a/src/mov_encoder_thread.cc b/src/mov_encoder_thread.cc index 49ba24c..d24e3f3 100644 --- a/src/mov_encoder_thread.cc +++ b/src/mov_encoder_thread.cc @@ -31,6 +31,10 @@ /* * $Log$ + * Revision 1.13 2005/07/02 11:39:52 deva + * Added some audiocode. + * Moved libfame code out of mov_encoder + * * Revision 1.12 2005/06/16 21:28:57 deva * Rewrote thread object * Fixed bug in mov_encoder (pushed read_sem too many times, whihc lead to @@ -203,5 +207,5 @@ void MovEncoderThread::encode(Frame* frame) block = new FrameVector; } - frame_number ++; + frame_number += 2; } -- cgit v1.2.3