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: */