From f66b7824aba0ffda17cb6a475763dbd2be28c008 Mon Sep 17 00:00:00 2001 From: deva Date: Sun, 30 Oct 2005 14:26:05 +0000 Subject: OCVS: ---------------------------------------------------------------------- --- src/libmplex_wrapper.cc | 637 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 src/libmplex_wrapper.cc (limited to 'src/libmplex_wrapper.cc') diff --git a/src/libmplex_wrapper.cc b/src/libmplex_wrapper.cc new file mode 100644 index 0000000..9b37c02 --- /dev/null +++ b/src/libmplex_wrapper.cc @@ -0,0 +1,637 @@ +/* -*- 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 +#ifdef HAVE_GETOPT_H +#include +#endif +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +//#include +#include +#include +#include + +#include +#include +#include +#include + + +using std::auto_ptr; + + +/************************************************************************* + Command line wrapper. Basically, all the command line and file (actually + pipe and FIFO is what would be more normal) I/O specific sub-classes + + Plus the top-level entry point. That's all!! + +*************************************************************************/ + + +#if !defined(HAVE_LROUND) +extern "C" { +long +lround(double x) +{ + long l = (long)ceil(x); + return(l); +} +}; +#endif + + + +class FrameOutputStream : public OutputStream +{ +public: + FrameOutputStream( const char *filename_pat ); + virtual int Open( ); + virtual void Close(); + virtual off_t SegmentSize( ); + virtual void NextSegment(); + virtual void Write(uint8_t *data, unsigned int len); + +private: + FILE *strm; + char filename_pat[MAXPATHLEN]; + char cur_filename[MAXPATHLEN]; + +}; + + + +FrameOutputStream::FrameOutputStream( const char *name_pat ) +{ + strncpy( filename_pat, name_pat, MAXPATHLEN ); + snprintf( cur_filename, MAXPATHLEN, filename_pat, segment_num ); +} + +int FrameOutputStream::Open() +{ + strm = fopen( cur_filename, "wb" ); + if( strm == NULL ) + { + mjpeg_error_exit1( "Could not open for writing: %s", cur_filename ); + } + + return 0; +} + +void FrameOutputStream::Close() +{ + fclose(strm); +} + + +off_t +FrameOutputStream::SegmentSize() +{ + struct stat stb; + fstat(fileno(strm), &stb); + off_t written = stb.st_size; + return written; +} + +void +FrameOutputStream::NextSegment( ) +{ + 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 ) +{ + if( fwrite( buf, 1, len, strm ) != len ) + { + mjpeg_error_exit1( "Failed write: %s", cur_filename ); + } +} + + + +/******************************** + * + * FrameInputStream - Input bit stream class for bit streams sourced + * from standard file I/O (this of course *includes* network sockets, + * fifo's, et al). + * + * OLAF: To hook into your PES reader/reconstructor you need to define + * a class like this one, where 'ReadStreamBytes' calls you code to + * generate the required number of bytes of ES data and transfer it + * to the specified buffer. The logical way to do this would be to + * inherit IBitStream as a base class of the top-level classes for the ES + * reconstructors. + * + ********************************/ + +class FrameInputStream : public IBitStream +{ +public: + FrameInputStream( const char *bs_filename, + unsigned int buf_size = BUFFER_SIZE); + ~FrameInputStream(); + +private: + FILE *fileh; + char *filename; + virtual size_t ReadStreamBytes( uint8_t *buf, size_t number ) + { + return fread(buf,sizeof(uint8_t), number, fileh ); + } + virtual bool EndOfStream() { return feof(fileh) != 0; } + +}; + + + +FrameInputStream::FrameInputStream( const char *bs_filename, + unsigned int buf_size) : + IBitStream() +{ + 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); + } + } +} + + +/** + Destructor: close the device containing the bit stream after a read + process +*/ +FrameInputStream::~FrameInputStream() +{ + if (fileh) + { + fclose(fileh); + delete filename; + } + fileh = 0; + Release(); +} + + +/******************************* + * + * Command line job class - sets up a Multiplex Job based on command + * line and File I/O... + * + ******************************/ + +class CmdLineMultiplexJob : public MultiplexJob +{ +public: + CmdLineMultiplexJob( unsigned int argc, char *argv[]); + +private: + void InputStreamsFromCmdLine (unsigned int argc, char* argv[] ); + void Usage(char *program_name); + bool ParseVideoOpt( const char *optarg ); + bool ParseLpcmOpt( const char *optarg ); + bool ParseWorkaroundOpt( const char *optarg ); + bool ParseTimeOffset( const char *optarg ); + + + static const char short_options[]; + +#if defined(HAVE_GETOPT_LONG) + static struct option long_options[]; +#endif + +}; + +const char CmdLineMultiplexJob::short_options[] = + "o:b:r:O:v:f:l:s:S:p:W:L:VMh"; +#if defined(HAVE_GETOPT_LONG) +struct option CmdLineMultiplexJob::long_options[] = +{ + { "verbose", 1, 0, 'v' }, + { "format", 1, 0, 'f' }, + { "mux-bitrate", 1, 0, 'r' }, + { "video-buffer", 1, 0, 'b' }, + { "lpcm-params", 1, 0, 'L' }, + { "output", 1, 0, 'o' }, + { "sync-offset", 1, 0, 'O' }, + { "vbr", 1, 0, 'V' }, + { "system-headers", 1, 0, 'h' }, + { "ignore-seqend-markers", 0, 0, 'M' }, + { "max-segment-size", 1, 0, 'S' }, + { "mux-limit", 1, 0, 'l' }, + { "packets-per-pack", 1, 0, 'p' }, + { "sector-size", 1, 0, 's' }, + { "workarounds", 1, 0, 'W' }, + { "help", 0, 0, '?' }, + { 0, 0, 0, 0 } +}; +#endif + + +CmdLineMultiplexJob::CmdLineMultiplexJob(unsigned int argc, char *argv[]) : + MultiplexJob() +{ + int n; + outfile_pattern = NULL; + +#if defined(HAVE_GETOPT_LONG) + while( (n=getopt_long(argc,argv,short_options,long_options, NULL)) != -1 ) +#else + while( (n=getopt(argc,argv,short_options)) != -1 ) +#endif + { + switch(n) + { + case 0 : + break; + case 'o' : + outfile_pattern = optarg; + break; + case 'v' : + verbose = atoi(optarg); + if( verbose < 0 || verbose > 2 ) + Usage(argv[0]); + break; + + case 'V' : + VBR = true; + break; + + case 'h' : + always_system_headers = true; + break; + + case 'b' : + if( ! ParseVideoOpt( optarg ) ) + { + mjpeg_error( "Illegal video decoder buffer size(s): %s", + optarg ); + Usage(argv[0]); + } + break; + case 'L': + if( ! ParseLpcmOpt( optarg ) ) + { + mjpeg_error( "Illegal LPCM option(s): %s", optarg ); + Usage(argv[0]); + } + break; + + case 'r': + data_rate = atoi(optarg); + if( data_rate < 0 ) + Usage(argv[0]); + /* Convert from kbit/sec (user spec) to 50B/sec units... */ + data_rate = (( data_rate * 1000 / 8 + 49) / 50 ) * 50; + break; + + case 'O': + if( ! ParseTimeOffset(optarg) ) + { + mjpeg_error( "Time offset units if specified must: ms|s|mpt" ); + Usage(argv[0]); + } + break; + + case 'l' : + max_PTS = atoi(optarg); + if( max_PTS < 1 ) + Usage(argv[0]); + break; + + case 'p' : + packets_per_pack = atoi(optarg); + if( packets_per_pack < 1 || packets_per_pack > 100 ) + Usage(argv[0]); + break; + + + case 'f' : + mux_format = atoi(optarg); + if( mux_format < MPEG_FORMAT_MPEG1 || mux_format > MPEG_FORMAT_LAST + ) + Usage(argv[0]); + break; + case 's' : + sector_size = atoi(optarg); + if( sector_size < 256 || sector_size > 16384 ) + Usage(argv[0]); + break; + case 'S' : + max_segment_size = atoi(optarg); + if( max_segment_size < 0 ) + Usage(argv[0]); + break; + case 'M' : + multifile_segment = true; + break; + case 'W' : + if( ! ParseWorkaroundOpt( optarg ) ) + { + Usage(argv[0]); + } + break; + case '?' : + default : + Usage(argv[0]); + break; + } + } + if (argc - optind < 1 || outfile_pattern == NULL) + { + Usage(argv[0]); + } + (void)mjpeg_default_handler_verbosity(verbose); + mjpeg_info( "mplex version %s (%s %s)",VERSION,MPLEX_VER,MPLEX_DATE ); + + InputStreamsFromCmdLine( argc-(optind-1), argv+optind-1); +} + +/************************************************************************* + Usage banner for the command line wrapper. +*************************************************************************/ + + +void CmdLineMultiplexJob::Usage(char *str) +{ + fprintf( stderr, + "mjpegtools mplex-2 version " VERSION " (" MPLEX_VER ")\n" + "Usage: %s [params] -o ... \n" + " %%d in the output file name is by segment count\n" + " where possible params are:\n" + "--verbose|-v num\n" + " Level of verbosity. 0 = quiet, 1 = normal 2 = verbose/debug\n" + "--format|-f fmt\n" + " Set defaults for particular MPEG profiles\n" + " [0 = Generic MPEG1, 1 = VCD, 2 = user-rate VCD, 3 = Generic MPEG2,\n" + " 4 = SVCD, 5 = user-rate SVCD\n" + " 6 = VCD Stills, 7 = SVCD Stills, 8 = DVD with NAV sectors, 9 = DVD]\n" + "--mux-bitrate|-r num\n" + " Specify data rate of output stream in kbit/sec\n" + " (default 0=Compute from source streams)\n" + "--video-buffer|-b num [, num...] \n" + " Specifies decoder buffers size in kB. [ 20...2000]\n" + "--lpcm-params | -L samppersec:chan:bits [, samppersec:chan:bits]\n" + "--mux-limit|-l num\n" + " Multiplex only num seconds of material (default 0=multiplex all)\n" + "--sync-offset|-O num ms|s|mpt\n" + " Specify offset of timestamps (video-audio) in mSec\n" + "--sector-size|-s num\n" + " Specify sector size in bytes for generic formats [256..16384]\n" + "--vbr|-V\n" + " Multiplex variable bit-rate video\n" + "--packets-per-pack|-p num\n" + " Number of packets per pack generic formats [1..100]\n" + "--system-headers|-h\n" + " Create System header in every pack in generic formats\n" + "--max-segment-size|-S size\n" + " Maximum size of output file(s) in Mbyte (default: 0) (no limit)\n" + "--ignore-seqend-markers|-M\n" + " Don't switch to a new output file if a sequence end marker\n" + " is encountered ithe input video.\n" + "--workaround|-W workaround [, workaround ]\n" + "--help|-?\n" + " Print this lot out!\n", str); + exit (1); +} + + +bool CmdLineMultiplexJob::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 CmdLineMultiplexJob::ParseWorkaroundOpt( const char *optarg ) +{ + char *endptr, *startptr; + endptr = const_cast(optarg); + struct { const char *longname; char shortname; bool *flag; } flag_table[] = + { + { 0, '\0', 0 } + }; + for(;;) + { + // Find start of next flag... + while( isspace(*endptr) || *endptr == ',' ) + ++endptr; + if( *endptr == '\0' ) + break; + startptr = endptr; + // Find end of current flag... + while( *endptr != ' ' && *endptr != ',' && *endptr != '\0' ) + ++endptr; + + size_t len = endptr - startptr; + + int flag = 0; + while( flag_table[flag].longname != 0 ) + { + if( (len == 1 && *startptr == flag_table[flag].shortname ) || + strncmp( startptr, flag_table[flag].longname, len ) == 0 ) + { + *flag_table[flag].flag = true; + break; + } + ++flag; + } + + if( flag_table[flag].longname == 0 ) + { + std::string message( "Illegal work-around option: not one of " ); + flag = 0; + char sep[] = ","; + do + { + message += flag_table[flag].longname; + message += sep; + message += flag_table[flag].shortname; + ++flag; + if( flag_table[flag].longname != 0 ) + message += sep; + } + while( flag_table[flag].longname != 0 ); + mjpeg_error( message.c_str() ); + return false; + } + + } + return true; +} + +bool CmdLineMultiplexJob::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; +} + +bool CmdLineMultiplexJob::ParseTimeOffset(const char *optarg) +{ + double f; + double persecond=1000.0; + char *e; + + f=strtod(optarg,&e); + if( *e ) { + while(isspace(*e)) e++; + if(!strcmp(e,"ms")) persecond=1000.0; + else if(!strcmp(e,"s")) persecond=1.0; + else if(!strcmp(e,"mpt")) persecond=90000.0; + else + return false; + } + video_offset = static_cast(f*CLOCKS/(persecond)); + if( video_offset < 0 ) + { + audio_offset = - video_offset; + video_offset = 0; + } + return true; +} + +void CmdLineMultiplexJob::InputStreamsFromCmdLine(unsigned int argc, char* argv[] ) +{ + vector inputs; + unsigned int i; + for( i = 1; i < argc; ++i ) + { + inputs.push_back( new FrameInputStream( argv[i] ) ); + } + SetupInputStreams( inputs ); +} + +LibMPlexWrapper::LibMPlexWrapper() +{ +} + +LibMPlexWrapper::~LibMPlexWrapper() +{ +} + +void LibMPlexWrapper::multiplex() +{ + CmdLineMultiplexJob job(0, NULL); + FrameOutputStream output( job.outfile_pattern ); + 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*/ + -- cgit v1.2.3