/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * mov_encoder.cc * * Sat Feb 19 14:13:19 CET 2005 * Copyright 2005 Bent Bisballe * deva@aasimon.org ****************************************************************************/ /* * Originally from: * RTVideoRec Realtime video recoder and encoder for Linux * * Copyright (C) 2004 B. Stultiens * Copyright (C) 2004 Koen Otter and Glenn van der Meyden */ /* * 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.29 2005/06/14 18:58:35 deva * *** empty log message *** * * Revision 1.28 2005/06/14 12:29:40 deva * Incorporated the use of the Info object everywhere... also using the log functionality. * * Revision 1.27 2005/06/09 11:00:03 deva * Added daemon code, and cleaned up using -Wall and -Werror * * Revision 1.26 2005/05/26 12:48:36 deva * *** empty log message *** * * Revision 1.25 2005/05/25 15:36:05 deva * Added mpeg4 encoding to the encoders, controllable by the config file. * * Revision 1.24 2005/05/22 15:49:22 deva * Added multithreaded encoding support. * * Revision 1.23 2005/05/19 14:10:22 deva * Multithreading rulez? * * Revision 1.22 2005/05/17 19:16:26 deva * Made new mpeg writer work, with proper file permissions. * * Revision 1.21 2005/05/17 14:30:56 deva * Added code, preparing threaded encoding. * * Revision 1.20 2005/05/16 16:00:57 deva * Lots of stuff! * * Revision 1.19 2005/05/16 13:25:52 deva * Moved video setting to configuration file. * Fine tuned setting for 2.4ghz server * * Revision 1.18 2005/05/16 11:13:24 deva * Optimized some encoding parameters. * * Revision 1.17 2005/05/16 10:45:10 deva * Added new entries to fame parameter struct (bitrate omongst other things) * * Revision 1.16 2005/05/09 19:54:15 deva * *** empty log message *** * * Revision 1.15 2005/05/09 16:40:20 deva * Added optimize yuv conversion code * * Revision 1.14 2005/05/07 10:56:18 deva * Changed print outs * * Revision 1.13 2005/05/07 10:25:34 deva * Removed ffmpeg code from img_encoder and corrected decoding errors in mov_encoder * * Revision 1.12 2005/05/05 20:41:38 deva * Removed the last pieces of ffmpeg... replaced it with libfame... * Not quite working yet, but all the major code is in place! * * Revision 1.11 2005/05/03 17:12:53 deva * Fixed a bug (forgot to set the frametime). * * Revision 1.10 2005/05/03 08:31:59 deva * Removed the error object, and replaced it with a more generic info object. * * Revision 1.9 2005/05/02 10:59:44 deva * Reverted to mpg file format (accidentally used avi with mpeg2) * * Revision 1.8 2005/05/01 09:56:26 deva * Added Id and Log tags to all files */ #include "mov_encoder.h" #include #include "miav_config.h" #include "debug.h" MovEncoder::MovEncoder(sem_t *r_sem, FrameVectorQueue *in, sem_t *in_sem, pthread_mutex_t *in_mutex, FramePriorityQueue *out, sem_t *out_sem, pthread_mutex_t *out_mutex, Info *i) { 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); running = true; inputqueue = in; outputqueue = out; input_sem = in_sem; output_sem = out_sem; read_sem = r_sem; input_mutex = in_mutex; output_mutex = out_mutex; } 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! } // this runs in a thread void MovEncoder::run() { info->info("MovEncoder::run"); FrameVector *item; Frame *in_frame; Frame *out_frame; while(running) { sem_wait(input_sem); // Lock inout mutex pthread_mutex_lock(input_mutex); if(inputqueue->size() == 0) { info->error("Dammit... Empty queue in MovEncoder."); pthread_mutex_unlock(input_mutex); continue; } item = inputqueue->front(); inputqueue->pop(); pthread_mutex_unlock(input_mutex); // Unlock input mutex if(!item) { info->warn("Empty block detected."); continue; } 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; delete in_frame; // Lock output mutex pthread_mutex_lock(output_mutex); outputqueue->push(out_frame); pthread_mutex_unlock(output_mutex); // Unlock output mutex } delete item; sem_post(read_sem); sem_post(output_sem); } info->info("MovEncoder::stop"); }