00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef MEDIA_UCBMPEGENCODER_C_DEFINED
00039 #define MEDIA_UCBMPEGENCODER_C_DEFINED
00040
00041 #include "Media/UcbMpegEncoder.H"
00042
00043 #include "Image/Image.H"
00044 #include "Image/Pixels.H"
00045 #include "Raster/GenericFrame.H"
00046 #include "Raster/PnmWriter.H"
00047 #include "Util/log.H"
00048 #include "Util/sformat.H"
00049 #include "Video/VideoFrame.H"
00050 #include "rutz/pipe.h"
00051 #include "rutz/trace.h"
00052
00053 #include <fstream>
00054
00055 UcbMpegParams UcbMpegParams::basic()
00056 {
00057 GVX_TRACE(__PRETTY_FUNCTION__);
00058 UcbMpegParams parms;
00059
00060 parms.PATTERN = "IBBPBBPBBPBBPBB";
00061 parms.GOP_SIZE = 30;
00062 parms.SLICES_PER_FRAME = 1;
00063 parms.PIXEL = "HALF";
00064 parms.RANGE = 10;
00065 parms.PSEARCH_ALG = "LOGARITHMIC";
00066 parms.BSEARCH_ALG = "CROSS2";
00067 parms.IQSCALE = 8;
00068 parms.PQSCALE = 10;
00069 parms.BQSCALE = 25;
00070 parms.REFERENCE_FRAME = "ORIGINAL";
00071
00072 return parms;
00073 }
00074
00075 UcbMpegParams UcbMpegParams::hq()
00076 {
00077 GVX_TRACE(__PRETTY_FUNCTION__);
00078 UcbMpegParams parms;
00079
00080 parms.PATTERN = "IBBPBBPBBPBBPBB";
00081 parms.GOP_SIZE = 30;
00082 parms.SLICES_PER_FRAME = 1;
00083 parms.PIXEL = "HALF";
00084 parms.RANGE = 10;
00085 parms.PSEARCH_ALG = "LOGARITHMIC";
00086 parms.BSEARCH_ALG = "CROSS2";
00087 parms.IQSCALE = 1;
00088 parms.PQSCALE = 1;
00089 parms.BQSCALE = 1;
00090 parms.REFERENCE_FRAME = "DECODED";
00091
00092 return parms;
00093 }
00094
00095 UcbMpegParams UcbMpegParams::superhq()
00096 {
00097 GVX_TRACE(__PRETTY_FUNCTION__);
00098 UcbMpegParams parms;
00099
00100 parms.PATTERN = "I";
00101 parms.GOP_SIZE = 1;
00102 parms.SLICES_PER_FRAME = 1;
00103 parms.PIXEL = "HALF";
00104 parms.RANGE = 10;
00105 parms.PSEARCH_ALG = "LOGARITHMIC";
00106 parms.BSEARCH_ALG = "CROSS2";
00107 parms.IQSCALE = 1;
00108 parms.PQSCALE = 1;
00109 parms.BQSCALE = 1;
00110 parms.REFERENCE_FRAME = "DECODED";
00111
00112 return parms;
00113 }
00114
00115 UcbMpegEncoder::UcbMpegEncoder(const std::string& exename,
00116 const std::string& outname,
00117 const UcbMpegParams& params,
00118 const double framerate) :
00119 itsExeName(exename),
00120 itsOutFname(outname),
00121 itsParams(params),
00122 itsParamFname(),
00123 itsFrameRate(framerate),
00124 itsVideoFormat(VIDFMT_AUTO),
00125 itsSubprocess(0),
00126 itsDims()
00127 {
00128 GVX_TRACE(__PRETTY_FUNCTION__);
00129 }
00130
00131 UcbMpegEncoder::~UcbMpegEncoder()
00132 {
00133 GVX_TRACE(__PRETTY_FUNCTION__);
00134 this->close();
00135
00136 delete itsSubprocess;
00137 }
00138
00139 int UcbMpegEncoder::close()
00140 {
00141 GVX_TRACE(__PRETTY_FUNCTION__);
00142 int result = 0;
00143
00144 if (itsSubprocess != 0)
00145 {
00146 itsSubprocess->close_out();
00147
00148 result = itsSubprocess->exit_status();
00149
00150 LINFO("%s exited with status %d", itsExeName.c_str(), result);
00151
00152 delete itsSubprocess;
00153 itsSubprocess = 0;
00154 }
00155
00156 if (itsParamFname.length() > 0)
00157 {
00158 remove(itsParamFname.c_str());
00159 itsParamFname = std::string();
00160 }
00161
00162 return result;
00163 }
00164
00165 void UcbMpegEncoder::makeParmsFile(const VideoFrame& f)
00166 {
00167 GVX_TRACE(__PRETTY_FUNCTION__);
00168 static int counter = 0;
00169
00170 if (f.getDims().w() % 16 != 0)
00171 LFATAL("%s requires the image width to be a multiple of 16, "
00172 "but the actual image width was %d",
00173 itsExeName.c_str(), f.getDims().w());
00174
00175 if (f.getDims().h() % 16 != 0)
00176 LFATAL("%s requires the image height to be a multiple of 16, "
00177 "but the actual image height was %d",
00178 itsExeName.c_str(), f.getDims().h());
00179
00180 itsDims = f.getDims();
00181
00182 itsParamFname = sformat("/tmp/mpegencode-params-%d-%d.txt",
00183 int(getpid()), counter++);
00184
00185 FILE* pf = fopen(itsParamFname.c_str(), "w");
00186
00187 if (pf == 0)
00188 LFATAL("couldn't open %s for writing", itsParamFname.c_str());
00189
00190 fprintf(pf, "PATTERN %s\n", itsParams.PATTERN);
00191 fprintf(pf, "GOP_SIZE %d\n", itsParams.GOP_SIZE);
00192 fprintf(pf, "SLICES_PER_FRAME %d\n", itsParams.SLICES_PER_FRAME);
00193 fprintf(pf, "PIXEL %s\n", itsParams.PIXEL);
00194 fprintf(pf, "RANGE %d\n", itsParams.RANGE);
00195 fprintf(pf, "PSEARCH_ALG %s\n", itsParams.PSEARCH_ALG);
00196 fprintf(pf, "BSEARCH_ALG %s\n", itsParams.BSEARCH_ALG);
00197 fprintf(pf, "IQSCALE %d\n", itsParams.IQSCALE);
00198 fprintf(pf, "PQSCALE %d\n", itsParams.PQSCALE);
00199 fprintf(pf, "BQSCALE %d\n", itsParams.BQSCALE);
00200 fprintf(pf, "REFERENCE_FRAME %s\n", itsParams.REFERENCE_FRAME);
00201
00202 fprintf(pf, "FORCE_ENCODE_LAST_FRAME 1\n");
00203 fprintf(pf, "FRAME_RATE %.2f\n", itsFrameRate);
00204 fprintf(pf, "OUTPUT %s\n", itsOutFname.c_str());
00205 fprintf(pf, "SIZE %dx%d\n", f.getDims().w(), f.getDims().h());
00206
00207 switch (f.getMode())
00208 {
00209 case VIDFMT_UYVY:
00210 case VIDFMT_YUV422:
00211 fprintf(pf, "BASE_FILE_FORMAT YUV\n");
00212 fprintf(pf, "YUV_FORMAT ABEKAS\n");
00213 itsVideoFormat = f.getMode();
00214 LINFO("writing frames in YUV/ABEKAS format");
00215 break;
00216
00217
00218
00219
00220 case VIDFMT_YUV420P:
00221 fprintf(pf, "BASE_FILE_FORMAT YUV\n");
00222 fprintf(pf, "YUV_FORMAT UCB\n");
00223 itsVideoFormat = f.getMode();
00224 LINFO("writing frames in YUV/UCB format");
00225 break;
00226
00227 default:
00228 fprintf(pf, "BASE_FILE_FORMAT PPM\n");
00229 itsVideoFormat = VIDFMT_AUTO;
00230 LINFO("writing frames in PPM format");
00231 break;
00232 }
00233
00234 fprintf(pf, "INPUT_DIR stdin\n");
00235 fprintf(pf, "INPUT_CONVERT *\n");
00236 fprintf(pf, "INPUT\n");
00237 fprintf(pf, "END_INPUT\n");
00238
00239 fclose(pf);
00240
00241
00242 {
00243 std::ifstream ifs(itsParamFname.c_str());
00244 int lineno = 0;
00245 std::string line;
00246 while (std::getline(ifs, line))
00247 {
00248 LDEBUG("params:%02d: %s", ++lineno, line.c_str());
00249 }
00250 }
00251 }
00252
00253 void UcbMpegEncoder::writeVideoFrame(const VideoFrame& f)
00254 {
00255 GVX_TRACE(__PRETTY_FUNCTION__);
00256
00257 if (itsSubprocess == 0)
00258 {
00259 this->makeParmsFile(f);
00260 itsSubprocess = new rutz::bidir_pipe;
00261
00262 itsSubprocess->block_child_sigint();
00263
00264 itsSubprocess->init(itsExeName.c_str(),
00265 "-realquiet",
00266 itsParamFname.c_str(),
00267 0);
00268
00269 LDEBUG("initialized subprocess '%s -realquiet %s'",
00270 itsExeName.c_str(), itsParamFname.c_str());
00271 }
00272
00273 if (f.getDims() != itsDims)
00274 LFATAL("invalid image dimensions (got %dx%d, expected %dx%d)",
00275 f.getDims().w(), f.getDims().h(), itsDims.w(), itsDims.h());
00276
00277 switch (itsVideoFormat)
00278 {
00279 case VIDFMT_UYVY:
00280 case VIDFMT_YUV422:
00281 case VIDFMT_YUV420P:
00282 if (f.getMode() != itsVideoFormat)
00283 LFATAL("input frame in wrong format (expected %s, got %s)",
00284 convertToString(itsVideoFormat).c_str(),
00285 convertToString(f.getMode()).c_str());
00286
00287 itsSubprocess->out_stream()
00288 .write(reinterpret_cast<const char*>(f.getBuffer()),
00289 f.getBufSize());
00290
00291 break;
00292
00293 default:
00294 {
00295 const Image<PixRGB<byte> > rgb = f.toRgb();
00296 PnmWriter::writeRGB(rgb, itsSubprocess->out_stream());
00297 }
00298 }
00299
00300 if (itsSubprocess->out_stream().fail())
00301 LFATAL("pipe stream error while sending frame");
00302 }
00303
00304 void UcbMpegEncoder::writeFrame(const GenericFrame& f)
00305 {
00306 this->writeVideoFrame(f.asVideo());
00307 }
00308
00309
00310
00311
00312
00313
00314
00315
00316 #endif // MEDIA_UCBMPEGENCODER_C_DEFINED