HttpEncoder.C

Go to the documentation of this file.
00001 /*!@file Media/HttpEncoder.C Low-level class for using http to output display */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Lior Elazary
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Media/HttpEncoder.C $
00035 // $Id: HttpEncoder.C 13505 2010-05-30 16:08:43Z lior $
00036 //
00037 
00038 #ifdef INVT_HAVE_AVCODEC
00039 
00040 #include "Media/HttpEncoder.H"
00041 
00042 #include "Image/Image.H"
00043 #include "Image/Pixels.H"
00044 #include "Image/JPEGUtil.H"
00045 #include "Image/color_conversions.H" // for rgb24_to_yv12_c()
00046 #include "Raster/GenericFrame.H"
00047 #include "Util/log.H"
00048 #include "Video/FfmpegFrame.H"
00049 #include "Video/VideoFrame.H"
00050 #include "rutz/arrays.h"
00051 #include "rutz/trace.h"
00052 
00053 void * HttpEncoder_run(void* r0)
00054 {
00055         HttpEncoder* r = (HttpEncoder*)r0;
00056         r->run();
00057         return NULL;
00058 }
00059 
00060 // ######################################################################
00061 HttpEncoder::HttpEncoder(nub::soft_ref<HttpServer> httpServer,
00062                 const std::string& fname,
00063                 const std::string& codecname,
00064                 const int bitrate,
00065                 const int framerate,
00066                 const int frameratebase,
00067                 const Dims& dims,
00068                 const int bufsz)
00069 :
00070         itsHttpServer(httpServer),
00071         itsFile(0),
00072         itsContext(),
00073         itsFormatContext(0),
00074         itsFrameNumber(0),
00075         itsOutbufSize(bufsz),
00076         itsFrameSizeRange(),
00077         itsFname(fname),
00078         itsCodecname(codecname),
00079         itsBitrate(bitrate),
00080         itsFramerate(framerate),
00081         itsFrameratebase(frameratebase),
00082         itsDims(dims),
00083         itsThreadRunning(true)
00084 {
00085         pthread_mutex_init(&itsFrameLock, NULL);
00086         pthread_mutex_init(&itsImgFrameLock, NULL);
00087         pthread_mutex_init(&itsFrameNumLock, NULL);
00088         initEncoder();
00089         //Start the thread to listen to connections
00090         pthread_create(&itsRunThread, NULL, &HttpEncoder_run, (void *)this);
00091 }
00092 
00093 void HttpEncoder::initEncoder()
00094 {
00095         GVX_TRACE(__PRETTY_FUNCTION__);
00096 
00097         // no need to guard these functions for being called multiple times;
00098         // they all have internal guards
00099         av_register_all();
00100         avcodec_init();
00101         avcodec_register_all();
00102 
00103         AVOutputFormat* oformat = NULL;
00104         if (itsCodecname.compare("List") == 0) { // list available codecs
00105                 LINFO("##### Available output codecs (not all may work for video):");
00106                 for(AVOutputFormat* f = first_oformat; f != NULL; f = f->next)
00107                         LINFO("%s: %s %d", f->name, f->long_name, f->flags);
00108                 LFATAL("Please select a codec from this list");
00109         } else { // format is given
00110                 // no av_find_output_format()?? let's do it by hand...
00111                 for(AVOutputFormat* f = first_oformat; f != NULL; f = f->next)
00112                         if (itsCodecname.compare(f->name) == 0)
00113                         { oformat = f; break; }
00114         }
00115 
00116         if (oformat == 0)
00117                 LFATAL("No such video codec '%s';\n"
00118                                 "try re-running with --output-codec=List to see a list\n"
00119                                 "of available codecs", itsCodecname.c_str());
00120 
00121         char ext[100]; ext[0] = '.'; uint i;
00122         for (i = 0; i < strlen(oformat->extensions); i ++)
00123                 if (oformat->extensions[i] == ',') break;
00124                 else ext[i+1] = oformat->extensions[i];
00125         ext[i+1] = '\0';
00126         LINFO("Using output format '%s' (%s), extension %s", oformat->name,
00127                         oformat->long_name, ext);
00128 
00129 #ifdef INVT_FFMPEG_HAS_FORMATCONTEXT_FUNCTIONS
00130         itsFormatContext = av_alloc_format_context();
00131         if (!itsFormatContext)
00132                 LFATAL("Cannot allocate format context");
00133         itsFormatContext->oformat = oformat;
00134 
00135         itsAVStream = av_new_stream(itsFormatContext, 0);
00136         if (!itsAVStream)
00137                 LFATAL("Can not allocate AVStream");
00138 #else
00139         LFATAL("Need a new version of ffmpeg libs for this option");
00140         itsFormatContext = NULL;
00141 #endif
00142 
00143         AVCodec* const codec = avcodec_find_encoder(oformat->video_codec);
00144         if (codec == NULL)  LFATAL("codec not found");
00145 
00146 #if defined(INVT_FFMPEG_HAS_DEFAULTS_FUNCTIONS)
00147         avcodec_get_context_defaults(&itsContext);
00148 #else
00149         {
00150                 AVCodecContext* const tmp = avcodec_alloc_context();
00151                 memcpy(&itsContext, tmp, sizeof(AVCodecContext));
00152                 free(tmp);
00153         }
00154 #endif
00155 
00156         itsContext.bit_rate = itsBitrate;
00157 
00158         // Be sure to set itsContext.pix_fmt -- it may occasionally
00159         // appear to work to leave pix_fmt unset, because the value we want,
00160         // PIX_FMT_YUV420P, has the enum value of 0, so if the uninitialized
00161         // memory for pix_fmt happens to have the value 0, then we'll slip
00162         // through without setting it explicitly.
00163         itsContext.pix_fmt = PIX_FMT_YUV420P;
00164 
00165         /* resolution must be a multiple of two */
00166         itsContext.width = itsDims.w();
00167         itsContext.height = itsDims.h();
00168 #if defined(INVT_FFMPEG_AVCODECCONTEXT_HAS_TIME_BASE)
00169         AVRational time_base = { itsFrameratebase, itsFramerate };
00170         itsContext.time_base = time_base;
00171         const int frb = itsFrameratebase;
00172 #elif LIBAVCODEC_VERSION_INT >= 0x000406 && LIBAVCODEC_BUILD > 4665
00173         itsContext.frame_rate = itsFramerate;
00174         const int frb = itsFrameratebase;
00175         itsContext.frame_rate_base = frb;
00176 #else
00177         itsContext.frame_rate = itsFramerate;
00178         const int frb = FRAME_RATE_BASE;
00179 #endif
00180         itsContext.gop_size = 10; /* emit one intra frame every ten frames */
00181 
00182         if(codec->id != CODEC_ID_MPEG4 &&
00183                         codec->id != CODEC_ID_MPEG1VIDEO &&
00184                         codec->id != CODEC_ID_MPEG2VIDEO)
00185                 itsContext.max_b_frames = 0;
00186         else
00187                 itsContext.max_b_frames = 1;
00188 
00189         itsFrameNumber = 0;
00190 
00191         LINFO("using max_b_frames=%i bitrate=%u width=%u height=%u framerate=%u frameratebase=%u",
00192                         itsContext.max_b_frames, itsContext.bit_rate, itsContext.width, itsContext.height, itsFramerate, frb);
00193 
00194         if (avcodec_open(&itsContext, codec) < 0)
00195                 LFATAL("could not open codec\n");
00196 
00197 #ifdef INVT_FFMPEG_HAS_FORMATCONTEXT_FUNCTIONS
00198         AVCodecContext *c = itsAVStream->codec;
00199         c->codec_id = itsContext.codec_id;
00200         c->codec_type = CODEC_TYPE_VIDEO;
00201 
00202         /* put sample parameters */
00203         c->bit_rate = itsContext.bit_rate;
00204         /* resolution must be a multiple of two */
00205         c->width = itsContext.width;
00206         c->height = itsContext.height;
00207         /* time base: this is the fundamental unit of time (in seconds) in terms
00208                  of which frame timestamps are represented. for fixed-fps content,
00209                  timebase should be 1/framerate and timestamp increments should be
00210                  identically 1. */
00211 #if defined(INVT_FFMPEG_AVCODECCONTEXT_HAS_TIME_BASE)
00212         c->time_base.den = itsContext.time_base.den;
00213         c->time_base.num = itsContext.time_base.num;
00214 #endif
00215         c->gop_size = 12; /* emit one intra frame every twelve frames at most */
00216         c->pix_fmt = itsContext.pix_fmt;
00217 
00218         /* set the output parameters (must be done even if no
00219                  parameters). */
00220         if (av_set_parameters(itsFormatContext, NULL) < 0)
00221                 LFATAL("Invalid output format parameters");
00222 
00223         openBuffer();
00224 
00225 
00226         /* write the stream header, if any */
00227         av_write_header(itsFormatContext);
00228 #else
00229         LFATAL("Need a new version of FFMPEG for this option");
00230 #endif
00231 
00232         LINFO("EnCoder Inited");
00233 
00234         //Send the stream header to the client
00235         unsigned char *pb_buffer;
00236 #if defined(INVT_FFMPEG_AVFORMATCONTEXT_BYTEIO_ISPOINTER)
00237         int len = url_close_dyn_buf(itsFormatContext->pb,
00238                         (unsigned char **)(&pb_buffer));
00239 #else
00240         int len = url_close_dyn_buf(&itsFormatContext->pb,
00241                         (unsigned char **)(&pb_buffer));
00242 #endif
00243 
00244         pthread_mutex_lock(&itsFrameLock);
00245         itsEncodedHeaderSize = len;
00246         itsEncodedHeader = new unsigned char[len];
00247         memcpy(itsEncodedHeader, pb_buffer, len);
00248         pthread_mutex_unlock(&itsFrameLock);
00249 }
00250 
00251 
00252 void HttpEncoder::openBuffer()
00253 {
00254 #if defined(INVT_FFMPEG_URL_OPEN_FUNC_TAKES_SINGLE_POINTER)
00255 
00256 #if defined(INVT_FFMPEG_AVFORMATCONTEXT_BYTEIO_ISPOINTER)
00257         //if (url_fopen(itsFormatContext->pb, oname.c_str(), URL_WRONLY) < 0)
00258         if(url_open_dyn_buf(itsFormatContext->pb))
00259                 LFATAL("Could not open stream");
00260 #else
00261         //if (url_fopen(&itsFormatContext->pb, oname.c_str(), URL_WRONLY) < 0)
00262         if(url_open_dyn_buf(&itsFormatContext->pb))
00263                 LFATAL("Could not open stream");
00264 #endif
00265 
00266 #else
00267 
00268 #if defined(INVT_FFMPEG_AVFORMATCONTEXT_BYTEIO_ISPOINTER)
00269         //if (url_fopen(&itsFormatContext->pb, oname.c_str(), URL_WRONLY) < 0)
00270         if (url_open_dyn_buf(&itsFormatContext->pb))
00271                 LFATAL("Could not open stream");
00272 #else
00273         LFATAL("Could not open stream");
00274 #endif
00275 
00276 #endif //INVT_FFMPEG_URL_OPEN_FUNC_TAKES_SINGLE_POINTER)
00277 }
00278 
00279 void HttpEncoder::closeFrameBuffer()
00280 {
00281         //Send the stream header to the client
00282         unsigned char *pb_buffer;
00283 
00284         pthread_mutex_lock(&itsFrameLock);
00285 #if defined(INVT_FFMPEG_AVFORMATCONTEXT_BYTEIO_ISPOINTER)
00286         int len = url_close_dyn_buf(itsFormatContext->pb,
00287                         (unsigned char **)(&pb_buffer));
00288 #else
00289         int len = url_close_dyn_buf(&itsFormatContext->pb,
00290                         (unsigned char **)(&pb_buffer));
00291 #endif
00292 
00293         itsEncodedFrameBufferSize = len;
00294         itsEncodedFrameBuffer = pb_buffer;
00295         pthread_mutex_unlock(&itsFrameLock);
00296 }
00297 
00298 HttpEncoder::~HttpEncoder()
00299 {
00300         close();
00301 
00302         itsThreadRunning = false;
00303 
00304         LINFO("Wait for thread");
00305         if (0 != pthread_join(itsRunThread, NULL))
00306                 LERROR("Pthread__join() failed");
00307         LINFO("Disconnecting from stream");
00308 
00309 }
00310 
00311 int HttpEncoder::close()
00312 {
00313         GVX_TRACE(__PRETTY_FUNCTION__);
00314 
00315         //if we went through this function already, then all the memory is freed
00316         if (itsFormatContext  == NULL)
00317                 return 0;
00318 
00319         // (1) write any "delayed frames"
00320         {
00321                 byte* const outbuf = (byte*) calloc(itsOutbufSize, 1);
00322 
00323                 if (outbuf != 0)
00324                 {
00325                         while (true)
00326                         {
00327                                 LINFO("pre  frame number %d", itsContext.frame_number);
00328 
00329                                 const int out_size =
00330                                         avcodec_encode_video(&itsContext, outbuf,
00331                                                         itsOutbufSize, NULL);
00332 
00333                                 if (out_size <= 0)
00334                                         break;
00335 
00336                                 itsFrameSizeRange.merge(out_size);
00337 
00338 #ifdef INVT_FFMPEG_HAS_FORMATCONTEXT_FUNCTIONS
00339                                 if (out_size > 0)
00340                                 {
00341                                         AVPacket pkt;
00342                                         av_init_packet(&pkt);
00343 
00344 #if defined(INVT_FFMPEG_AVCODECCONTEXT_HAS_TIME_BASE)
00345                                         pkt.pts= av_rescale_q(itsContext.coded_frame->pts,
00346                                                         itsContext.time_base, itsAVStream->time_base);
00347 #endif
00348                                         if(itsContext.coded_frame->key_frame)
00349                                                 pkt.flags |= PKT_FLAG_KEY;
00350                                         pkt.stream_index= itsAVStream->index;
00351                                         pkt.data= outbuf;
00352                                         pkt.size= out_size;
00353 
00354                                         /* write the compressed frame in the media file */
00355                                         openBuffer();
00356                                         av_write_frame(itsFormatContext, &pkt);
00357                                         closeFrameBuffer();
00358                                 }
00359 #else
00360                                 LFATAL("Need a new version of ffmpeg for this option");
00361 #endif
00362 
00363                                 LINFO("post frame number %d", itsContext.frame_number);
00364                                 LINFO("delayed frame (out_size=%d)", out_size);
00365                         }
00366 
00367                         free(outbuf);
00368                 }
00369         }
00370 
00371         LINFO("end encoder: wrote %d frames, itsFrameSizeRange=[%d..%d]",
00372                         itsFrameNumber, itsFrameSizeRange.min(), itsFrameSizeRange.max());
00373 
00374 #ifdef INVT_FFMPEG_HAS_FORMATCONTEXT_FUNCTIONS
00375         avcodec_close(&itsContext);
00376 
00377         openBuffer();
00378         av_write_trailer(itsFormatContext);
00379         closeFrameBuffer();
00380 
00381         /* free the streams */
00382         for(uint i = 0; i < (uint)itsFormatContext->nb_streams; i++) {
00383                 av_freep(&itsFormatContext->streams[i]->codec);
00384                 av_freep(&itsFormatContext->streams[i]);
00385         }
00386 
00387         av_free(itsFormatContext);
00388         itsFormatContext = NULL;
00389 #else
00390         LFATAL("Need a new version of ffmpeg for this option");
00391 #endif
00392 
00393         return 0;
00394 }
00395 
00396 void HttpEncoder::writeRawFrame(const AVFrame* picture)
00397 {
00398         GVX_TRACE(__PRETTY_FUNCTION__);
00399 
00400         // FIXME We'd like to have a way to either (1) compute what the
00401         // maximum necessary itsOutbufSize would be for our given
00402         // framerate+bitrate, or (2) get a chance to retry writing a given
00403         // frame if it is truncated. However, we have no programmatic way of
00404         // knowing whether a given frame gets truncated (all we see is that
00405         // ffmpeg prints "encoded frame too large" on stderr), but even then
00406         // the return value from avcodec_encode_video() is less than our
00407         // itsOutbufSize (although for a "too large" frame we can see
00408         // that the return value is clearly higher than usual). Also, it's
00409         // hard to determine a hard upper limit on the bufsize, even given
00410         // the framerate and bitrate, because the bitrate is only achieved
00411         // on /average/ -- so, any particular frame might be much larger
00412         // (e.g., 10x or 100x) than the average frame size. So, given all
00413         // that, our current approach is just to leave the buffer size up to
00414         // the user via the --output-mpeg-bufsize command-line option.
00415 
00416         // NOTE: it might seem extravagent to allocate+deallocate these
00417         // buffers (outbuf, and picture_buf in writeRGB()) for every single
00418         // frame that is written; however, profiling shows that this
00419         // accounts for only about 2% of the total time spent in
00420         // writeFrame(). The alternatives, both with their own
00421         // disadvantages, would be (1) have separate buffers allocated once
00422         // per object; however this would be expensive in overall memory
00423         // usage if we had multiple mpeg streams open at once; or (2) have
00424         // static buffers shared by all objects; however, this would require
00425         // some form of between-object synchronization in the case of
00426         // multi-threading which could be cpu-expensive both for the
00427         // locking+unlocking and would also waste time waiting to acquire
00428         // the lock for access to the shared buffers.
00429 
00430         rutz::fixed_block<byte> outbuf(itsOutbufSize);
00431 
00432         const int out_size = avcodec_encode_video(&itsContext,
00433                         &outbuf[0],
00434                         outbuf.size(),
00435                         picture);
00436 
00437         if (out_size < 0)
00438                 LFATAL("error during avcodec_encode_video()");
00439 
00440         if (out_size > 0)
00441         {
00442                 itsFrameSizeRange.merge(out_size);
00443 
00444 #ifdef INVT_FFMPEG_HAS_FORMATCONTEXT_FUNCTIONS
00445                 AVPacket pkt;
00446                 av_init_packet(&pkt);
00447 
00448                 pkt.pts= av_rescale_q(itsContext.coded_frame->pts,
00449                                 itsContext.time_base, itsAVStream->time_base);
00450                 if(itsContext.coded_frame->key_frame)
00451                         pkt.flags |= PKT_FLAG_KEY;
00452                 pkt.stream_index= itsAVStream->index;
00453                 pkt.data= &outbuf[0];
00454                 pkt.size= out_size;
00455 
00456                 /* write the compressed frame in the media file */
00457 
00458                 openBuffer();
00459                 av_write_frame(itsFormatContext, &pkt);
00460                 closeFrameBuffer();
00461 
00462 #else
00463                 LFATAL("New a new version of ffmpeg for this option");
00464 #endif
00465         }
00466 
00467         LDEBUG("itsOutbufSize=%d, out_size=%d, frameSizeRange=[%d..%d]",
00468                         itsOutbufSize, out_size,
00469                         itsFrameSizeRange.min(), itsFrameSizeRange.max());
00470 
00471         LDEBUG("encoded frame [zero-based] %d (%d delayed frames pending)",
00472                         itsFrameNumber,
00473                         // to compute the number of pending "delayed frames", we
00474                         // subtract the AVCodecContext's frame number from our own,
00475                         // except that there is an offset of 2 -- one because
00476                         // AVCodecContext counts from 1, while we count from zero, and
00477                         // another because AVCodecContext's counter reports the number
00478                         // of the NEXT frame to be written, while itsFrameNumber is
00479                         // the number of the frame that has just been written
00480                         itsFrameNumber - (itsContext.frame_number - 2));
00481 
00482 
00483         pthread_mutex_lock(&itsFrameNumLock);
00484         ++itsFrameNumber;
00485         pthread_mutex_unlock(&itsFrameNumLock);
00486 }
00487 
00488 void HttpEncoder::writeRGB(const Image<PixRGB<byte> >& img)
00489 {
00490         GVX_TRACE(__PRETTY_FUNCTION__);
00491 
00492         ASSERT(PIX_FMT_YUV420P == itsContext.pix_fmt);
00493 
00494         pthread_mutex_lock(&itsImgFrameLock);
00495         itsCurrentFrame = img;
00496         pthread_mutex_unlock(&itsImgFrameLock);
00497 
00498         const int size = itsContext.width * itsContext.height;
00499         const int size4 =
00500                 ((itsContext.width+1)/2) * (itsContext.height/2);
00501 
00502         rutz::fixed_block<byte> picture_buf(size + 2*size4); /* size for YUV 420 */
00503 
00504         AVFrame picture;
00505 #if defined(INVT_FFMPEG_HAS_DEFAULTS_FUNCTIONS)
00506         avcodec_get_frame_defaults(&picture);
00507 #else
00508         {
00509                 AVFrame* tmp = avcodec_alloc_frame();
00510                 memcpy(&picture, tmp, sizeof(AVFrame));
00511                 free(tmp);
00512         }
00513 #endif
00514 
00515         picture.data[0] = &picture_buf[0];
00516         picture.data[1] = &picture_buf[0] + size;
00517         picture.data[2] = &picture_buf[0] + size + size4;
00518         picture.linesize[0] = itsContext.width;
00519         picture.linesize[1] = (itsContext.width+1) / 2;
00520         picture.linesize[2] = (itsContext.width+1) / 2;
00521 
00522         if (img.getWidth() != itsContext.width ||
00523                         img.getHeight() != itsContext.height)
00524         {
00525                 LFATAL("wrong size mpeg output frame "
00526                                 "(expected %dx%d, got %dx%d)",
00527                                 itsContext.width, itsContext.height,
00528                                 img.getWidth(), img.getHeight());
00529         }
00530 
00531         rgb24_to_yv12_c(img,
00532                         picture.data[0],
00533                         picture.data[1],
00534                         picture.data[2]);
00535 
00536         this->writeRawFrame(&picture);
00537 }
00538 
00539 void HttpEncoder::writeVideoFrame(const VideoFrame& frame)
00540 {
00541         GVX_TRACE(__PRETTY_FUNCTION__);
00542 
00543         if (frame.getDims().w() != itsContext.width ||
00544                         frame.getDims().h() != itsContext.height)
00545         {
00546                 LFATAL("wrong size mpeg output frame "
00547                                 "(expected %dx%d, got %dx%d)",
00548                                 itsContext.width, itsContext.height,
00549                                 frame.getDims().w(), frame.getDims().h());
00550         }
00551 
00552         AVFrame picture;
00553 #if defined(INVT_FFMPEG_HAS_DEFAULTS_FUNCTIONS)
00554         avcodec_get_frame_defaults(&picture);
00555 #else
00556         {
00557                 AVFrame* tmp = avcodec_alloc_frame();
00558                 memcpy(&picture, tmp, sizeof(AVFrame));
00559                 free(tmp);
00560         }
00561 #endif
00562 
00563         if (convertVideoFrameToAVFrame(frame,
00564                                 itsContext.pix_fmt,
00565                                 &picture))
00566         {
00567                 this->writeRawFrame(&picture);
00568         }
00569         else
00570         {
00571                 // OK, we couldn't do a direct conversion from
00572                 // VideoFrame->AVFrame (probably the pixel formats didn't
00573                 // match), so let's just fall back to RGB instead:
00574                 this->writeRGB(frame.toRgb());
00575         }
00576 }
00577 
00578 void HttpEncoder::writeFrame(const GenericFrame& f)
00579 {
00580         if (f.nativeType() == GenericFrame::VIDEO)
00581         {
00582                 this->writeVideoFrame(f.asVideo());
00583         }
00584         else
00585         {
00586                 this->writeRGB(f.asRgb());
00587         }
00588 }
00589 
00590 void HttpEncoder::sendEncodedFrames(const int clientFd)
00591 {
00592 
00593         //Send http header
00594         std::string httpHeader = "HTTP/1.1 200 OK\r\n"
00595                 "CONTENT-TYPE: application/octet-stream\r\n"
00596                 "CONNECTION: close\r\n"
00597                 "\r\n";
00598         itsHttpServer->writeData(clientFd, httpHeader);
00599 
00600         ///send header
00601         ssize_t bytes_written = ::write(clientFd, itsEncodedHeader, itsEncodedHeaderSize);
00602         if(bytes_written != itsEncodedHeaderSize)
00603                 LERROR("Only %d/%d header bytes were written.", (int)bytes_written, itsEncodedHeaderSize);
00604 
00605         usleep(10000);
00606 
00607         int ret = 1;
00608         int lastFrameNumber = 0;
00609         while(ret > 0 && itsThreadRunning)
00610         {
00611                 //Send data
00612 
00613                 pthread_mutex_lock(&itsFrameNumLock);
00614                 int frameNumber = itsFrameNumber;
00615                 pthread_mutex_unlock(&itsFrameNumLock);
00616 
00617                 if (frameNumber > lastFrameNumber)
00618                 {
00619                         pthread_mutex_lock(&itsFrameLock);
00620                         ret = ::write(clientFd, itsEncodedFrameBuffer, itsEncodedFrameBufferSize);
00621                         pthread_mutex_unlock(&itsFrameLock);
00622                         lastFrameNumber = frameNumber;
00623                 }
00624 
00625                 usleep(10000);
00626         }
00627 }
00628 
00629 void HttpEncoder::sendMJPGFrames(const int clientFd)
00630 {
00631 
00632         //Send http header
00633 
00634   std::string httpHeader = "HTTP/1.1 200 OK\r\n"
00635     "Server: INVT-HTTP-Output/0.2\r\n"
00636     "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n"
00637     "Pragma: no-cache\r\n"
00638     "Content-Type: multipart/x-mixed-replace;boundary=boundarydonotcross\r\n"
00639     "\r\n";
00640         itsHttpServer->writeData(clientFd, httpHeader);
00641         usleep(10000);
00642 
00643   std::string jpegHeader =  "--boundarydonotcross\r\n"
00644                 "Content-Type: image/jpeg\r\n"
00645                 "\r\n";
00646 
00647         int ret = 1;
00648         int lastFrameNumber = 0;
00649         while(ret > 0 && itsThreadRunning)
00650         {
00651                 //Send data
00652 
00653                 pthread_mutex_lock(&itsFrameNumLock);
00654                 int frameNumber = itsFrameNumber;
00655                 pthread_mutex_unlock(&itsFrameNumLock);
00656 
00657                 if (frameNumber > lastFrameNumber)
00658                 {
00659                         pthread_mutex_lock(&itsImgFrameLock);
00660                         Image<PixRGB<byte> > tmp = itsCurrentFrame.deepcopy(); 
00661                         pthread_mutex_unlock(&itsImgFrameLock);
00662 
00663         
00664                         JPEGCompressor jpegCompressor;
00665       std::vector<unsigned char> jpgRawData = jpegCompressor.compressImage(tmp);
00666 
00667                         if (jpgRawData.size() > 0)
00668                         {
00669                                 std::vector<unsigned char> jpgData;
00670                                 for(uint i=0; i<jpegHeader.size(); i++)
00671                                         jpgData.push_back(jpegHeader[i]);
00672 
00673                                 for(uint i=0; i<jpgRawData.size(); i++)
00674                                         jpgData.push_back(jpgRawData[i]);
00675 
00676                                 ret = ::write(clientFd, &jpgData[0], jpgData.size());
00677                                 usleep(100000);
00678                         }
00679 
00680                         lastFrameNumber = frameNumber;
00681                 }
00682 
00683                 usleep(10000);
00684         }
00685 }
00686 void HttpEncoder::run()
00687 {
00688         while(itsThreadRunning)
00689         {
00690                 int clientFd = itsHttpServer->acceptConn();
00691                 if (clientFd > 0)
00692                 {
00693                         LINFO("New client, send data %i", clientFd);
00694                         //sendEncodedFrames(clientFd);
00695                         sendMJPGFrames(clientFd);
00696 			::close(clientFd);
00697                 }
00698                 usleep(10000);
00699         }
00700 }
00701 
00702 #endif // HAVE_FFMPEG_AVCODEC_H
00703 
00704 // ######################################################################
00705 /* So things look consistent in everyone's emacs... */
00706 /* Local Variables: */
00707 /* mode: c++ */
00708 /* indent-tabs-mode: nil */
00709 /* End: */
Generated on Sun May 8 08:41:01 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3