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 #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"
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
00090 pthread_create(&itsRunThread, NULL, &HttpEncoder_run, (void *)this);
00091 }
00092
00093 void HttpEncoder::initEncoder()
00094 {
00095 GVX_TRACE(__PRETTY_FUNCTION__);
00096
00097
00098
00099 av_register_all();
00100 avcodec_init();
00101 avcodec_register_all();
00102
00103 AVOutputFormat* oformat = NULL;
00104 if (itsCodecname.compare("List") == 0) {
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 {
00110
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
00159
00160
00161
00162
00163 itsContext.pix_fmt = PIX_FMT_YUV420P;
00164
00165
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;
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
00203 c->bit_rate = itsContext.bit_rate;
00204
00205 c->width = itsContext.width;
00206 c->height = itsContext.height;
00207
00208
00209
00210
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;
00216 c->pix_fmt = itsContext.pix_fmt;
00217
00218
00219
00220 if (av_set_parameters(itsFormatContext, NULL) < 0)
00221 LFATAL("Invalid output format parameters");
00222
00223 openBuffer();
00224
00225
00226
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
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
00258 if(url_open_dyn_buf(itsFormatContext->pb))
00259 LFATAL("Could not open stream");
00260 #else
00261
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
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
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
00316 if (itsFormatContext == NULL)
00317 return 0;
00318
00319
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
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
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
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
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
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
00474
00475
00476
00477
00478
00479
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);
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
00572
00573
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
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
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
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
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
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
00695 sendMJPGFrames(clientFd);
00696 ::close(clientFd);
00697 }
00698 usleep(10000);
00699 }
00700 }
00701
00702 #endif // HAVE_FFMPEG_AVCODEC_H
00703
00704
00705
00706
00707
00708
00709