/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * libmplex_wrapper.cc * * Sun Oct 30 12:28:47 CET 2005 * Copyright 2005 Bent Bisballe Nyeng * 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. */ #include "config.h" #include "libmplex_wrapper.h" #include "miav_config.h" #ifdef WITH_LIBMPLEX #include #include #include #include #include #include #include /** * FrameOutputStream - Wraps the File object */ class FrameOutputStream : public OutputStream { public: FrameOutputStream( Info *info, File *outputfile ); int Open( ); void Close(); off_t SegmentSize( ); void NextSegment(); void Write(uint8_t *data, unsigned int len); private: Info *info; off_t written; File *file; }; FrameOutputStream::FrameOutputStream( Info *info, File *outputfile ) { this->info = info; file = outputfile; written = 0; info->info("FrameOutputStream - constructor"); } int FrameOutputStream::Open() { // info->info("FrameOutputStream::Open"); // Nothing to do here! return 0; } void FrameOutputStream::Close() { // info->info("FrameOutputStream::Close"); // Nothing to do here! } off_t FrameOutputStream::SegmentSize() { // info->info("FrameOutputStream::SegmentSize - return: %d", written); return written; /* struct stat stb; fstat(fileno(strm), &stb); off_t written = stb.st_size; return written; */ } void FrameOutputStream::NextSegment( ) { // info->info("FrameOutputStream::NextSegment"); // Nothing to do here! /* auto_ptr prev_filename_buf( new char[strlen(cur_filename)+1] ); char *prev_filename = prev_filename_buf.get(); fclose(strm); ++segment_num; strcpy( prev_filename, cur_filename ); snprintf( cur_filename, MAXPATHLEN, filename_pat, segment_num ); if( strcmp( prev_filename, cur_filename ) == 0 ) { mjpeg_error_exit1("Need to split output but there appears to be no %%d in the filename pattern %s", filename_pat ); } strm = fopen( cur_filename, "wb" ); if( strm == NULL ) { mjpeg_error_exit1( "Could not open for writing: %s", cur_filename ); } */ } void FrameOutputStream::Write( uint8_t *buf, unsigned int len ) { unsigned int write; write = file->Write(buf, len); written += write; // info->info("FrameOutputStream::Write - len: %d", len); } /** * FrameInputStream - Wraps the ThreadSafeQueuePriority objects, containing * the prosessed frames from libfame and liblame. */ class FrameInputStream : public IBitStream { public: FrameInputStream( Info *info, ThreadSafeQueuePriority *queue ); ~FrameInputStream(); private: Frame *getFrame(); size_t ReadStreamBytes( uint8_t *buf, size_t size ); bool EndOfStream(); Info *info; ThreadSafeQueuePriority *queue; bool seen_eof; Frame *frame; unsigned int read; }; FrameInputStream::FrameInputStream( Info *info, ThreadSafeQueuePriority *queue ) : IBitStream() { this->info = info; this->queue = queue; seen_eof = false; frame = NULL; read = 0; streamname = "MIaV Stream\0"; // info->info("FrameInputStream - constructor", seen_eof); /* if ((fileh = fopen(bs_filename, "rb")) == NULL) { mjpeg_error_exit1( "Unable to open file %s for reading.", bs_filename); } filename = strcpy( new char[strlen(bs_filename)+1], bs_filename ); streamname = filename; SetBufSize(buf_size); eobs = false; byteidx = 0; if (!ReadIntoBuffer()) { if (buffered==0) { mjpeg_error_exit1( "Unable to read from %s.", bs_filename); } } */ SetBufSize(BUFFER_SIZE); // SetBufSize(buf_size); eobs = false; byteidx = 0; if (!ReadIntoBuffer()) { if (buffered==0) { info->error( "Unable to read from %s.", streamname); } } // info->info("FrameInputStream - leaving constructor", seen_eof); } /** Destructor: close the device containing the bit stream after a read process */ FrameInputStream::~FrameInputStream() { // info->info("FrameInputStream - destructor", seen_eof); // Nothing to do here! /* if (fileh) { fclose(fileh); delete filename; } fileh = 0; */ Release(); // Hmmm.. wonder what this 'Release()' does!? } Frame *FrameInputStream::getFrame() { read = 0; return queue->pop(); } bool FrameInputStream::EndOfStream() { // info->info("FrameInputStream::EndOfStream - return: %d", seen_eof); return seen_eof; } size_t FrameInputStream::ReadStreamBytes( uint8_t *buf, size_t size ) { // info->info("FrameInputStream::ReadStreamBytes - size: %d", size); unsigned int copied = 0; while( copied < size ) { // If we read the entire frame, prepare to get a new one if(frame && read == frame->size) { delete frame; frame = NULL; } // If no frame is in the buffer, get one from the queue if(frame == NULL) frame = getFrame(); // check for end of stream if( frame->endOfFrameStream == true) { seen_eof = true; return copied; } // If a frame exists in the buffer copy it to the output buffer // (No frame ocurres when *running is set to false) if( frame ) { unsigned int doread = (size - copied) < (frame->size - read) ? size - copied : (frame->size - read); //info->info("Requested: %d. Read: %d. Doread: %d. In buffer %d", size, (*read), doread, (*frame)->size); memcpy(buf + copied, frame->data + read, doread); read += doread; copied += doread; } } return copied; } /******************************* * * Command line job class - sets up a Multiplex Job based on command * line and File I/O... * ******************************/ class MIaVMultiplexJob : public MultiplexJob { public: MIaVMultiplexJob(Info *info, ThreadSafeQueuePriority *video_queue, ThreadSafeQueuePriority *audio_queue); private: bool ParseVideoOpt( const char *optarg ); bool ParseLpcmOpt( const char *optarg ); }; MIaVMultiplexJob::MIaVMultiplexJob(Info *info, ThreadSafeQueuePriority *video_queue, ThreadSafeQueuePriority *audio_queue) : MultiplexJob() { // this->info = info; // info->info("MIaVMultiplexJob - constructor"); outfile_pattern = "/tmp/aaargh.mpg"; // Output file... or something verbose = 0; // Level of verbosity. 0 = quiet, 1 = normal 2 = verbose/debug VBR = config->readInt("video_bitrate") == 0; // Multiplex variable bit-rate video always_system_headers = true; // Create System header in every pack in generic formats // Specifies decoder buffers size in kB. [ 20...2000] if( ! ParseVideoOpt( "500" ) ) info->error( "Illegal video decoder buffer size(s): %s", "500" ); // --lpcm-params | -L samppersec:chan:bits [, samppersec:chan:bits] // if( ! ParseLpcmOpt( "48000:2:16" ) ) info->error( "Illegal LPCM option(s): %s", "48000:2:16" ); data_rate = 0; //Specify data rate of output stream in kbit/sec (default 0=Compute from source streams) // Convert from kbit/sec (user spec) to 50B/sec units... data_rate = (( data_rate * 1000 / 8 + 49) / 50 ) * 50; audio_offset = 0; //Specify offset of timestamps (video-audio) in mSec video_offset = 0; //Specify offset of timestamps (video-audio) in mSec max_PTS = 0; // Multiplex only num seconds of material (default 0=multiplex all) packets_per_pack = 5; //Number of packets per pack generic formats [1..100] mux_format = 3; // Set defaults for particular MPEG profiles: // 0 = Generic MPEG1 // 1 = VCD // 2 = user-rate VCD // 3 = Generic MPEG2 // 4 = SVCD // 5 = user-rate SVCD // 6 = VCD Stills // 7 = SVCD Stills // 8 = DVD with NAV sectors // 9 = DVD sector_size = 2042; // Specify sector size in bytes for generic formats [256..16384] //max_segment_size = 0; // Maximum size of output file(s) in Mbyte (default: 0) (no limit) multifile_segment = true; // Don't switch to a new output file if a sequence end marker // is encountered ithe input video (void)mjpeg_default_handler_verbosity(verbose); info->info( "mplex version %s (%s %s)", VERSION,MPLEX_VER, MPLEX_DATE ); // Connect streams vector inputs; if(video_queue) inputs.push_back( new FrameInputStream( info, video_queue ) ); if(audio_queue) inputs.push_back( new FrameInputStream( info, audio_queue ) ); SetupInputStreams( inputs ); } /************************************************************************* Usage banner for the command line wrapper. *************************************************************************/ /* mjpegtools mplex-2 version VERSION ( MPLEX_VER ) Usage: %s [params] -o ... %%d in the output file name is by segment count where possible params are: --verbose|-v num Level of verbosity. 0 = quiet, 1 = normal 2 = verbose/debug --format|-f fmt Set defaults for particular MPEG profiles [0 = Generic MPEG1, 1 = VCD, 2 = user-rate VCD, 3 = Generic MPEG2, 4 = SVCD, 5 = user-rate SVCD 6 = VCD Stills, 7 = SVCD Stills, 8 = DVD with NAV sectors, 9 = DVD] --mux-bitrate|-r num Specify data rate of output stream in kbit/sec (default 0=Compute from source streams) --video-buffer|-b num [, num...] Specifies decoder buffers size in kB. [ 20...2000] --lpcm-params | -L samppersec:chan:bits [, samppersec:chan:bits] --mux-limit|-l num Multiplex only num seconds of material (default 0=multiplex all) --sync-offset|-O num ms|s|mpt Specify offset of timestamps (video-audio) in mSec --sector-size|-s num Specify sector size in bytes for generic formats [256..16384] --vbr|-V Multiplex variable bit-rate video --packets-per-pack|-p num Number of packets per pack generic formats [1..100] --system-headers|-h Create System header in every pack in generic formats --max-segment-size|-S size Maximum size of output file(s) in Mbyte (default: 0) (no limit) --ignore-seqend-markers|-M Don't switch to a new output file if a sequence end marker is encountered ithe input video. --workaround|-W workaround [, workaround ] --help|-? Print this lot out! */ bool MIaVMultiplexJob::ParseLpcmOpt( const char *optarg ) { char *endptr, *startptr; unsigned int samples_sec; unsigned int channels; unsigned int bits_sample; endptr = const_cast(optarg); do { startptr = endptr; samples_sec = static_cast(strtol(startptr, &endptr, 10)); if( startptr == endptr || *endptr != ':' ) return false; startptr = endptr+1; channels = static_cast(strtol(startptr, &endptr, 10)); if(startptr == endptr || *endptr != ':' ) return false; startptr = endptr+1; bits_sample = static_cast(strtol(startptr, &endptr, 10)); if( startptr == endptr ) return false; LpcmParams *params = LpcmParams::Checked( samples_sec, channels, bits_sample ); if( params == 0 ) return false; lpcm_param.push_back(params); if( *endptr == ',' ) ++endptr; } while( *endptr != '\0' ); return true; } bool MIaVMultiplexJob::ParseVideoOpt( const char *optarg ) { char *endptr, *startptr; unsigned int buffer_size; endptr = const_cast(optarg); do { startptr = endptr; buffer_size = static_cast(strtol(startptr, &endptr, 10)); if( startptr == endptr ) return false; VideoParams *params = VideoParams::Checked( buffer_size ); if( params == 0 ) return false; video_param.push_back(params); if( *endptr == ',' ) ++endptr; } while( *endptr != '\0' ); return true; } LibMPlexWrapper::LibMPlexWrapper(Info *info, File *outputfile, ThreadSafeQueuePriority *video_queue, ThreadSafeQueuePriority *audio_queue) { this->info = info; this->outputfile = outputfile; this->video_queue = video_queue; this->audio_queue = audio_queue; } LibMPlexWrapper::~LibMPlexWrapper() { } void LibMPlexWrapper::multiplex() { // info->info("MPLEX!"); // sleep(10); // info->info("The road goes ever on and on..."); MIaVMultiplexJob job(info, video_queue, audio_queue); FrameOutputStream output( info, outputfile ); Multiplexor mux(job, output); mux.Multiplex(); } #ifdef LIBMPLEX_WRAPPER_TEST int main (int argc, char* argv[]) { LibMPlexWrapper mplex; mplex.multiplex(); return 0; } #endif/*LIBMPLEX_WRAPPER_TEST*/ #endif/*WITH_LIBMPLEX*/