svvitch
digital signage player
|
00001 #ifdef USE_FFMPEG 00002 00003 #include "FFVideoDecoder.h" 00004 #include "PerformanceTimer.h" 00005 00006 #include <Poco/format.h> 00007 #include <Poco/UnicodeConverter.h> 00008 00009 00010 FFVideoDecoder::FFVideoDecoder(Renderer& renderer, AVFormatContext* ic, const int streamNo): FFBaseDecoder(renderer, ic, streamNo), 00011 _outFrame(NULL), _buffer(NULL), _diFrame(NULL), _diBuffer(NULL), _fx(NULL), _worker(NULL), _swsCtx(NULL) 00012 { 00013 } 00014 00015 FFVideoDecoder::‾FFVideoDecoder() { 00016 Poco::ScopedLock<Poco::FastMutex> lock(_startLock); 00017 _worker = NULL; 00018 _thread.join(); 00019 00020 clearAllFrames(); 00021 if (_fx) { 00022 //_log.information("release effect"); 00023 SAFE_RELEASE(_fx); 00024 } 00025 } 00026 00027 bool FFVideoDecoder::isReady() { 00028 return _worker != NULL; 00029 } 00030 00034 void FFVideoDecoder::clearAllFrames() { 00035 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00036 int count = 0; 00037 while (!_usedFrames.empty()) { 00038 SAFE_DELETE(_usedFrames.front()); 00039 _usedFrames.pop(); 00040 count++; 00041 } 00042 while (!_frames.empty()) { 00043 SAFE_DELETE(_frames.front()); 00044 _frames.pop(); 00045 count++; 00046 } 00047 //_log.information(Poco::format("release video frames: %d", count)); 00048 } 00049 00050 void FFVideoDecoder::start() { 00051 Poco::ScopedLock<Poco::FastMutex> lock(_startLock); 00052 AVCodecContext* avctx = _ic->streams[_streamNo]->codec; 00053 // avctx->thread_count = 4; 00054 // int res = avcodec_thread_init(avctx, 4); 00055 // _log.information(Poco::format("thread: %d", res)); 00056 int w = avctx->width; 00057 int h = avctx->height; 00058 int flags = SWS_FAST_BILINEAR; 00059 string type; 00060 bool changeFormat = false; 00061 switch (avctx->pix_fmt) { 00062 case PIX_FMT_NONE: 00063 _log.warning("failed pix format NONE"); 00064 type = "NONE"; 00065 break; 00066 case PIX_FMT_YUV420P: 00067 type = "YUV420P"; 00068 _fx = _renderer.createEffect("fx/conversion_yuv2rgb.fx"); 00069 if (_fx) { 00070 changeFormat = false; 00071 } else { 00072 // エフェクトが生成できない場合 00073 changeFormat = true; 00074 flags = SWS_FAST_BILINEAR; 00075 } 00076 break; 00077 case PIX_FMT_YUYV422: 00078 type = "YUYV422"; 00079 changeFormat = true; 00080 flags = SWS_FAST_BILINEAR; 00081 break; 00082 case PIX_FMT_RGB24: 00083 type = "RGB24"; 00084 changeFormat = true; 00085 break; 00086 case PIX_FMT_BGR24: 00087 type = "BGR24"; 00088 changeFormat = true; 00089 break; 00090 case PIX_FMT_ARGB: 00091 type = "RGB32"; 00092 changeFormat = true; 00093 break; 00094 case PIX_FMT_YUVJ420P: 00095 type = "YUVJ420P"; 00096 _fx = _renderer.createEffect("fx/conversion_yuv2rgb.fx"); 00097 if (_fx) { 00098 changeFormat = false; 00099 } else { 00100 // エフェクトが生成できない場合 00101 changeFormat = true; 00102 flags = SWS_FAST_BILINEAR; 00103 } 00104 break; 00105 case PIX_FMT_YUVJ422P: 00106 type = "YUVJ422P"; 00107 // changeFormat = true; 00108 // flags = SWS_FAST_BILINEAR; 00109 _fx = _renderer.createEffect("fx/conversion_yuv2rgb.fx"); 00110 if (_fx) { 00111 changeFormat = false; 00112 } else { 00113 // エフェクトが生成できない場合 00114 changeFormat = true; 00115 flags = SWS_FAST_BILINEAR; 00116 } 00117 break; 00118 case PIX_FMT_YUVJ444P: 00119 type = "YUVJ444P"; 00120 changeFormat = true; 00121 break; 00122 default: 00123 type = Poco::format("unknown format(%d)", (int)avctx->pix_fmt); 00124 } 00125 00126 string size; 00127 if (avctx->sample_aspect_ratio.num) { 00128 _dw = w * avctx->sample_aspect_ratio.num; 00129 _dh = h * avctx->sample_aspect_ratio.den; 00130 size = Poco::format("size(%dx%d DAR %d:%d)", w, h, _dw, _dh); 00131 } else { 00132 _dw = w; 00133 _dh = h; 00134 size = Poco::format("size(%dx%d)", w, h); 00135 } 00136 _log.information(Poco::format("video stream: %s format(%s) %d %s", size, type, avctx->ticks_per_frame, string(avctx->hwaccel?"H/W Acceleted":""))); 00137 00138 if (changeFormat) { 00139 _outFrame = avcodec_alloc_frame(); 00140 if (_outFrame) { 00141 int bytes = avpicture_get_size(PIX_FMT_BGRA, w, h); 00142 _buffer = (uint8_t*)av_malloc(bytes * sizeof(uint8_t)); 00143 if (_buffer) { 00144 avpicture_fill((AVPicture*)_outFrame, _buffer, PIX_FMT_BGRA, w, h); 00145 _swsCtx = sws_getContext(w, h, avctx->pix_fmt, w, h, PIX_FMT_BGRA, flags, NULL, NULL, NULL); 00146 if (_swsCtx) { 00147 _log.information(Poco::format("change format by sws: %s -> RGB32", type)); 00148 } else { 00149 _log.warning(Poco::format("failed not create sws context: %s -> RGB32", type)); 00150 av_free(_outFrame); 00151 av_free(_buffer); 00152 } 00153 } else { 00154 _log.warning("failed not allocate buffer"); 00155 av_free(_outFrame); 00156 } 00157 } else { 00158 _log.warning("failed not allocate out frame"); 00159 } 00160 } else { 00161 _swsCtx = NULL; 00162 } 00163 00164 if ((!changeFormat && avctx->pix_fmt != PIX_FMT_NONE) || (changeFormat && _swsCtx)) { 00165 _worker = this; 00166 _thread.start(*_worker); 00167 } 00168 } 00169 00170 const float FFVideoDecoder::getDisplayAspectRatio() const { 00171 return F(_dw) / _dh; 00172 } 00173 00174 const UINT FFVideoDecoder::bufferedFrames() { 00175 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00176 //return _frames.size() + bufferedPackets(); 00177 return _frames.size(); 00178 } 00179 00180 void FFVideoDecoder::run() { 00181 _log.information("video decoder thread start"); 00182 PerformanceTimer timer; 00183 00184 AVFrame* frame = avcodec_alloc_frame(); 00185 int gotPicture = 0; 00186 _readCount = 0; 00187 _avgTime = 0; 00188 00189 AVPacketList* packetList = NULL; 00190 while (_worker) { 00191 Poco::Thread::sleep(0); 00192 packetList = popPacket(); 00193 if (!packetList) { 00194 //Poco::Thread::sleep(10); 00195 continue; 00196 } 00197 timer.start(); 00198 AVCodecContext* avctx = _ic->streams[packetList->pkt.stream_index]->codec; 00199 int bytes = avcodec_decode_video2(avctx, frame, &gotPicture, &packetList->pkt); 00200 if (gotPicture) { 00201 const uint8_t* src[4] = {frame->data[0], frame->data[1], frame->data[2], frame->data[3]}; 00202 int pos = avctx->frame_number; 00203 int w = avctx->width; 00204 int h = avctx->height; 00205 bool interlaced = frame->interlaced_frame != 0; 00206 PixelFormat format = avctx->pix_fmt; 00207 VideoFrame* vf = popUsedFrame(); 00208 if (PIX_FMT_BGRA == format) { 00209 if (_swsCtx) { 00210 if (sws_scale(_swsCtx, src, frame->linesize, 0, h, _outFrame->data, _outFrame->linesize) >= 0) { 00211 if (!vf || !vf->equals(w, h, D3DFMT_A8R8G8B8)) { 00212 SAFE_DELETE(vf); 00213 vf = new VideoFrame(_renderer, w, h, _outFrame->linesize, D3DFMT_A8R8G8B8); 00214 } 00215 vf->write(_outFrame); 00216 } else { 00217 _log.warning("failed sws_scale"); 00218 SAFE_DELETE(vf); 00219 } 00220 00221 } else { 00222 if (!vf || !vf->equals(w, h, D3DFMT_A8R8G8B8)) { 00223 SAFE_DELETE(vf); 00224 vf = new VideoFrame(_renderer, w, h, frame->linesize, D3DFMT_A8R8G8B8); 00225 } 00226 vf->write(frame); 00227 } 00228 } else { 00229 if (_swsCtx) { 00230 if (sws_scale(_swsCtx, src, frame->linesize, 0, h, _outFrame->data, _outFrame->linesize) >= 0) { 00231 if (!vf || !vf->equals(w, h, D3DFMT_X8R8G8B8)) { 00232 SAFE_DELETE(vf); 00233 vf = new VideoFrame(_renderer, w, h, _outFrame->linesize, D3DFMT_X8R8G8B8); 00234 } 00235 vf->write(_outFrame); 00236 } else { 00237 _log.warning("failed sws_scale"); 00238 SAFE_DELETE(vf); 00239 } 00240 00241 } else { 00242 if (interlaced) { 00243 // deinterlace 00244 if (!_diFrame) { 00245 // deinterlace用のバッファ 00246 int size = avpicture_get_size(avctx->pix_fmt, frame->linesize[0], h); 00247 _diBuffer = (uint8_t*)av_malloc(size * sizeof(uint8_t)); 00248 if (_diBuffer) { 00249 ZeroMemory(_diBuffer, size); 00250 _diFrame = avcodec_alloc_frame(); 00251 if (_diFrame) { 00252 avpicture_fill((AVPicture*)_diFrame, _diBuffer, avctx->pix_fmt, frame->linesize[0], h); 00253 _log.information(Poco::format("created deinterlace buffer: %dx%d %d", frame->linesize[0], h, frame->interlaced_frame)); 00254 } else { 00255 av_free(_diBuffer); 00256 _diBuffer = NULL; 00257 } 00258 } 00259 } 00260 if (_diFrame && avpicture_deinterlace((AVPicture*)_diFrame, (AVPicture*)frame, format, w, h) < 0) { 00261 _log.warning("failed deinterlace"); 00262 // movのMJPEGがinterlacedではないのにも関わらず、ここに来てしまう。 00263 if (!vf || !vf->equals(w, h, D3DFMT_L8)) { 00264 SAFE_DELETE(vf); 00265 switch (format) { 00266 case PIX_FMT_YUV420P: 00267 vf = new VideoFrame(_renderer, w, h, frame->linesize, h / 2, D3DFMT_L8, _fx); 00268 break; 00269 case PIX_FMT_YUVJ422P: 00270 vf = new VideoFrame(_renderer, w, h, frame->linesize, h, D3DFMT_L8, _fx); 00271 break; 00272 } 00273 } 00274 vf->write(frame); 00275 } else { 00276 //インタレ解除できた 00277 if (!vf || !vf->equals(w, h, D3DFMT_L8)) { 00278 SAFE_DELETE(vf); 00279 switch (format) { 00280 case PIX_FMT_YUV420P: 00281 vf = new VideoFrame(_renderer, w, h, frame->linesize, h / 2, D3DFMT_L8, _fx); 00282 break; 00283 case PIX_FMT_YUVJ422P: 00284 vf = new VideoFrame(_renderer, w, h, frame->linesize, h, D3DFMT_L8, _fx); 00285 break; 00286 } 00287 } 00288 vf->write(_diFrame); 00289 } 00290 } else { 00291 if (!vf || !vf->equals(w, h, D3DFMT_L8)) { 00292 SAFE_DELETE(vf); 00293 switch (format) { 00294 case PIX_FMT_YUV420P: 00295 vf = new VideoFrame(_renderer, w, h, frame->linesize, h / 2, D3DFMT_L8, _fx); 00296 break; 00297 case PIX_FMT_YUVJ422P: 00298 vf = new VideoFrame(_renderer, w, h, frame->linesize, h, D3DFMT_L8, _fx); 00299 break; 00300 } 00301 } 00302 vf->write(frame); 00303 } 00304 } 00305 } 00306 _readTime = timer.getTime(); 00307 _readCount++; 00308 if (_readCount > 0) _avgTime = F(_avgTime * (_readCount - 1) + _readTime) / _readCount; 00309 00310 if (vf) { 00311 //UINT textureMen = _renderer.getTextureMem() * 10 / 100; 00312 //UINT availableTextureMem = _renderer.getAvailableTextureMem(); 00313 while (_worker != NULL && _frames.size() >= 50) { 00314 //while (_worker != NULL && availableTextureMem < textureMen) { 00315 // キュー空き待ち 00316 Poco::Thread::sleep(10); 00317 //availableTextureMem = _renderer.getAvailableTextureMem(); 00318 } 00319 { 00320 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00321 _frames.push(vf); 00322 // std::wstring wfile; 00323 // string file = Poco::format("image%04?i.png", packet.dts); 00324 // Poco::UnicodeConverter::toUTF16(file, wfile); 00325 // D3DXSaveTextureToFile(wfile.c_str(), D3DXIFF_PNG, vf->texture[0], NULL); 00326 // _log.information("queue frame"); 00327 } 00328 } else { 00329 _log.warning("failed not created video frame"); 00330 } 00331 00332 } else { 00333 // _log.information(Poco::format("video decode not finished: %d", bytes)); 00334 } 00335 00336 av_free_packet(&packetList->pkt); 00337 av_freep(&packetList); 00338 } 00339 if (_swsCtx) { 00340 _log.information("release scaler"); 00341 av_free(_outFrame); 00342 _outFrame = NULL; 00343 av_free(_buffer); 00344 _buffer = NULL; 00345 sws_freeContext(_swsCtx); 00346 _swsCtx = NULL; 00347 } 00348 00349 if (_diFrame) { 00350 _log.information("release deinterlace buffer"); 00351 av_free(_diFrame); 00352 _diFrame = NULL; 00353 av_free(_diBuffer); 00354 _diBuffer = NULL; 00355 } 00356 00357 av_free(frame); 00358 _worker = NULL; 00359 _log.information("video decoder thread end"); 00360 } 00361 00362 VideoFrame* FFVideoDecoder::popUsedFrame() { 00363 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00364 if (_usedFrames.size() > 10) { 00365 VideoFrame* vf = _usedFrames.front(); 00366 _usedFrames.pop(); 00367 return vf; 00368 } 00369 return NULL; 00370 } 00371 00372 VideoFrame* FFVideoDecoder::popFrame() { 00373 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00374 if (_frames.size() > 0) { 00375 VideoFrame* vf = _frames.front(); 00376 _frames.pop(); 00377 return vf; 00378 } 00379 return NULL; 00380 } 00381 00382 void FFVideoDecoder::pushUsedFrame(VideoFrame* vf) { 00383 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00384 _usedFrames.push(vf); 00385 } 00386 00387 VideoFrame* FFVideoDecoder::frontFrame() { 00388 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00389 if (_frames.size() > 0) { 00390 VideoFrame* vf = _frames.front(); 00391 return vf; 00392 } 00393 return NULL; 00394 } 00395 00396 VideoFrame* FFVideoDecoder::viewFrame() { 00397 Poco::ScopedLock<Poco::FastMutex> lock(_lock); 00398 if (_frames.size() > 0) { 00399 VideoFrame* vf = _frames.back(); 00400 return vf; 00401 } 00402 return NULL; 00403 } 00404 00405 #endif