Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/gameengine/VideoTexture/VideoFFmpeg.cpp')
-rw-r--r--source/gameengine/VideoTexture/VideoFFmpeg.cpp1392
1 files changed, 0 insertions, 1392 deletions
diff --git a/source/gameengine/VideoTexture/VideoFFmpeg.cpp b/source/gameengine/VideoTexture/VideoFFmpeg.cpp
deleted file mode 100644
index defc64d7558..00000000000
--- a/source/gameengine/VideoTexture/VideoFFmpeg.cpp
+++ /dev/null
@@ -1,1392 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright (c) 2007 The Zdeno Ash Miklas
- *
- * This source file is part of VideoTexture library
- *
- * Contributor(s):
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file gameengine/VideoTexture/VideoFFmpeg.cpp
- * \ingroup bgevideotex
- */
-
-
-#ifdef WITH_FFMPEG
-
-// INT64_C fix for some linux machines (C99ism)
-#ifndef __STDC_CONSTANT_MACROS
-#define __STDC_CONSTANT_MACROS
-#ifdef __STDC_CONSTANT_MACROS /* quiet warning */
-#endif
-#endif
-
-#include <stdint.h>
-
-
-#include "MEM_guardedalloc.h"
-#include "PIL_time.h"
-
-#include <string>
-
-#include "VideoFFmpeg.h"
-#include "Exception.h"
-
-
-// default framerate
-const double defFrameRate = 25.0;
-
-// macro for exception handling and logging
-#define CATCH_EXCP catch (Exception & exp) \
-{ exp.report(); m_status = SourceError; }
-
-// class RenderVideo
-
-// constructor
-VideoFFmpeg::VideoFFmpeg (HRESULT * hRslt) : VideoBase(),
-m_codec(NULL), m_formatCtx(NULL), m_codecCtx(NULL),
-m_frame(NULL), m_frameDeinterlaced(NULL), m_frameRGB(NULL), m_imgConvertCtx(NULL),
-m_deinterlace(false), m_preseek(0), m_videoStream(-1), m_baseFrameRate(25.0),
-m_lastFrame(-1), m_eof(false), m_externTime(false), m_curPosition(-1), m_startTime(0),
-m_captWidth(0), m_captHeight(0), m_captRate(0.f), m_isImage(false),
-m_isThreaded(false), m_isStreaming(false), m_stopThread(false), m_cacheStarted(false)
-{
- // set video format
- m_format = RGB24;
- // force flip because ffmpeg always return the image in the wrong orientation for texture
- setFlip(true);
- // construction is OK
- *hRslt = S_OK;
- BLI_listbase_clear(&m_thread);
- pthread_mutex_init(&m_cacheMutex, NULL);
- BLI_listbase_clear(&m_frameCacheFree);
- BLI_listbase_clear(&m_frameCacheBase);
- BLI_listbase_clear(&m_packetCacheFree);
- BLI_listbase_clear(&m_packetCacheBase);
-}
-
-// destructor
-VideoFFmpeg::~VideoFFmpeg ()
-{
-}
-
-void VideoFFmpeg::refresh(void)
-{
- // a fixed image will not refresh because it is loaded only once at creation
- if (m_isImage)
- return;
- m_avail = false;
-}
-
-// release components
-bool VideoFFmpeg::release()
-{
- // release
- stopCache();
- if (m_codecCtx)
- {
- avcodec_close(m_codecCtx);
- m_codecCtx = NULL;
- }
- if (m_formatCtx)
- {
- avformat_close_input(&m_formatCtx);
- m_formatCtx = NULL;
- }
- if (m_frame)
- {
- av_free(m_frame);
- m_frame = NULL;
- }
- if (m_frameDeinterlaced)
- {
- MEM_freeN(m_frameDeinterlaced->data[0]);
- av_free(m_frameDeinterlaced);
- m_frameDeinterlaced = NULL;
- }
- if (m_frameRGB)
- {
- MEM_freeN(m_frameRGB->data[0]);
- av_free(m_frameRGB);
- m_frameRGB = NULL;
- }
- if (m_imgConvertCtx)
- {
- sws_freeContext(m_imgConvertCtx);
- m_imgConvertCtx = NULL;
- }
- m_codec = NULL;
- m_status = SourceStopped;
- m_lastFrame = -1;
- return true;
-}
-
-AVFrame *VideoFFmpeg::allocFrameRGB()
-{
- AVFrame *frame;
- frame = av_frame_alloc();
- if (m_format == RGBA32)
- {
- avpicture_fill((AVPicture*)frame,
- (uint8_t*)MEM_callocN(avpicture_get_size(
- AV_PIX_FMT_RGBA,
- m_codecCtx->width, m_codecCtx->height),
- "ffmpeg rgba"),
- AV_PIX_FMT_RGBA, m_codecCtx->width, m_codecCtx->height);
- } else
- {
- avpicture_fill((AVPicture*)frame,
- (uint8_t*)MEM_callocN(avpicture_get_size(
- AV_PIX_FMT_RGB24,
- m_codecCtx->width, m_codecCtx->height),
- "ffmpeg rgb"),
- AV_PIX_FMT_RGB24, m_codecCtx->width, m_codecCtx->height);
- }
- return frame;
-}
-
-// set initial parameters
-void VideoFFmpeg::initParams (short width, short height, float rate, bool image)
-{
- m_captWidth = width;
- m_captHeight = height;
- m_captRate = rate;
- m_isImage = image;
-}
-
-
-int VideoFFmpeg::openStream(const char *filename, AVInputFormat *inputFormat, AVDictionary **formatParams)
-{
- AVFormatContext *formatCtx = NULL;
- int i, videoStream;
- AVCodec *codec;
- AVCodecContext *codecCtx;
-
- if (avformat_open_input(&formatCtx, filename, inputFormat, formatParams)!=0)
- return -1;
-
- if (avformat_find_stream_info(formatCtx, NULL) < 0)
- {
- avformat_close_input(&formatCtx);
- return -1;
- }
-
- /* Find the first video stream */
- videoStream=-1;
- for (i=0; i<formatCtx->nb_streams; i++)
- {
- if (formatCtx->streams[i] &&
- get_codec_from_stream(formatCtx->streams[i]) &&
- (get_codec_from_stream(formatCtx->streams[i])->codec_type==AVMEDIA_TYPE_VIDEO))
- {
- videoStream=i;
- break;
- }
- }
-
- if (videoStream==-1)
- {
- avformat_close_input(&formatCtx);
- return -1;
- }
-
- codecCtx = get_codec_from_stream(formatCtx->streams[videoStream]);
-
- /* Find the decoder for the video stream */
- codec=avcodec_find_decoder(codecCtx->codec_id);
- if (codec==NULL)
- {
- avformat_close_input(&formatCtx);
- return -1;
- }
- codecCtx->workaround_bugs = 1;
- if (avcodec_open2(codecCtx, codec, NULL) < 0)
- {
- avformat_close_input(&formatCtx);
- return -1;
- }
-
-#ifdef FFMPEG_OLD_FRAME_RATE
- if (codecCtx->frame_rate>1000 && codecCtx->frame_rate_base==1)
- codecCtx->frame_rate_base=1000;
- m_baseFrameRate = (double)codecCtx->frame_rate / (double)codecCtx->frame_rate_base;
-#else
- m_baseFrameRate = av_q2d(av_get_r_frame_rate_compat(formatCtx, formatCtx->streams[videoStream]));
-#endif
- if (m_baseFrameRate <= 0.0)
- m_baseFrameRate = defFrameRate;
-
- m_codec = codec;
- m_codecCtx = codecCtx;
- m_formatCtx = formatCtx;
- m_videoStream = videoStream;
- m_frame = av_frame_alloc();
- m_frameDeinterlaced = av_frame_alloc();
-
- // allocate buffer if deinterlacing is required
- avpicture_fill((AVPicture*)m_frameDeinterlaced,
- (uint8_t*)MEM_callocN(avpicture_get_size(
- m_codecCtx->pix_fmt,
- m_codecCtx->width, m_codecCtx->height),
- "ffmpeg deinterlace"),
- m_codecCtx->pix_fmt, m_codecCtx->width, m_codecCtx->height);
-
- // check if the pixel format supports Alpha
- if (m_codecCtx->pix_fmt == AV_PIX_FMT_RGB32 ||
- m_codecCtx->pix_fmt == AV_PIX_FMT_BGR32 ||
- m_codecCtx->pix_fmt == AV_PIX_FMT_RGB32_1 ||
- m_codecCtx->pix_fmt == AV_PIX_FMT_BGR32_1)
- {
- // allocate buffer to store final decoded frame
- m_format = RGBA32;
- // allocate sws context
- m_imgConvertCtx = sws_getContext(
- m_codecCtx->width,
- m_codecCtx->height,
- m_codecCtx->pix_fmt,
- m_codecCtx->width,
- m_codecCtx->height,
- AV_PIX_FMT_RGBA,
- SWS_FAST_BILINEAR,
- NULL, NULL, NULL);
- } else
- {
- // allocate buffer to store final decoded frame
- m_format = RGB24;
- // allocate sws context
- m_imgConvertCtx = sws_getContext(
- m_codecCtx->width,
- m_codecCtx->height,
- m_codecCtx->pix_fmt,
- m_codecCtx->width,
- m_codecCtx->height,
- AV_PIX_FMT_RGB24,
- SWS_FAST_BILINEAR,
- NULL, NULL, NULL);
- }
- m_frameRGB = allocFrameRGB();
-
- if (!m_imgConvertCtx) {
- avcodec_close(m_codecCtx);
- m_codecCtx = NULL;
- avformat_close_input(&m_formatCtx);
- m_formatCtx = NULL;
- av_free(m_frame);
- m_frame = NULL;
- MEM_freeN(m_frameDeinterlaced->data[0]);
- av_free(m_frameDeinterlaced);
- m_frameDeinterlaced = NULL;
- MEM_freeN(m_frameRGB->data[0]);
- av_free(m_frameRGB);
- m_frameRGB = NULL;
- return -1;
- }
- return 0;
-}
-
-/*
- * This thread is used to load video frame asynchronously.
- * It provides a frame caching service.
- * The main thread is responsible for positioning the frame pointer in the
- * file correctly before calling startCache() which starts this thread.
- * The cache is organized in two layers: 1) a cache of 20-30 undecoded packets to keep
- * memory and CPU low 2) a cache of 5 decoded frames.
- * If the main thread does not find the frame in the cache (because the video has restarted
- * or because the GE is lagging), it stops the cache with StopCache() (this is a synchronous
- * function: it sends a signal to stop the cache thread and wait for confirmation), then
- * change the position in the stream and restarts the cache thread.
- */
-void *VideoFFmpeg::cacheThread(void *data)
-{
- VideoFFmpeg* video = (VideoFFmpeg*)data;
- // holds the frame that is being decoded
- CacheFrame *currentFrame = NULL;
- CachePacket *cachePacket;
- bool endOfFile = false;
- int frameFinished = 0;
- double timeBase = av_q2d(video->m_formatCtx->streams[video->m_videoStream]->time_base);
- int64_t startTs = video->m_formatCtx->streams[video->m_videoStream]->start_time;
-
- if (startTs == AV_NOPTS_VALUE)
- startTs = 0;
-
- while (!video->m_stopThread)
- {
- // packet cache is used solely by this thread, no need to lock
- // In case the stream/file contains other stream than the one we are looking for,
- // allow a bit of cycling to get rid quickly of those frames
- frameFinished = 0;
- while ( !endOfFile
- && (cachePacket = (CachePacket *)video->m_packetCacheFree.first) != NULL
- && frameFinished < 25)
- {
- // free packet => packet cache is not full yet, just read more
- if (av_read_frame(video->m_formatCtx, &cachePacket->packet)>=0)
- {
- if (cachePacket->packet.stream_index == video->m_videoStream)
- {
- // make sure fresh memory is allocated for the packet and move it to queue
- av_dup_packet(&cachePacket->packet);
- BLI_remlink(&video->m_packetCacheFree, cachePacket);
- BLI_addtail(&video->m_packetCacheBase, cachePacket);
- break;
- } else {
- // this is not a good packet for us, just leave it on free queue
- // Note: here we could handle sound packet
- av_free_packet(&cachePacket->packet);
- frameFinished++;
- }
-
- } else {
- if (video->m_isFile)
- // this mark the end of the file
- endOfFile = true;
- // if we cannot read a packet, no need to continue
- break;
- }
- }
- // frame cache is also used by main thread, lock
- if (currentFrame == NULL)
- {
- // no current frame being decoded, take free one
- pthread_mutex_lock(&video->m_cacheMutex);
- if ((currentFrame = (CacheFrame *)video->m_frameCacheFree.first) != NULL)
- BLI_remlink(&video->m_frameCacheFree, currentFrame);
- pthread_mutex_unlock(&video->m_cacheMutex);
- }
- if (currentFrame != NULL)
- {
- // this frame is out of free and busy queue, we can manipulate it without locking
- frameFinished = 0;
- while (!frameFinished && (cachePacket = (CachePacket *)video->m_packetCacheBase.first) != NULL)
- {
- BLI_remlink(&video->m_packetCacheBase, cachePacket);
- // use m_frame because when caching, it is not used in main thread
- // we can't use currentFrame directly because we need to convert to RGB first
- avcodec_decode_video2(video->m_codecCtx,
- video->m_frame, &frameFinished,
- &cachePacket->packet);
- if (frameFinished)
- {
- AVFrame * input = video->m_frame;
-
- /* This means the data wasnt read properly, this check stops crashing */
- if ( input->data[0]!=0 || input->data[1]!=0
- || input->data[2]!=0 || input->data[3]!=0)
- {
- if (video->m_deinterlace)
- {
- if (avpicture_deinterlace(
- (AVPicture*) video->m_frameDeinterlaced,
- (const AVPicture*) video->m_frame,
- video->m_codecCtx->pix_fmt,
- video->m_codecCtx->width,
- video->m_codecCtx->height) >= 0)
- {
- input = video->m_frameDeinterlaced;
- }
- }
- // convert to RGB24
- sws_scale(video->m_imgConvertCtx,
- input->data,
- input->linesize,
- 0,
- video->m_codecCtx->height,
- currentFrame->frame->data,
- currentFrame->frame->linesize);
- // move frame to queue, this frame is necessarily the next one
- video->m_curPosition = (long)((cachePacket->packet.dts-startTs) * (video->m_baseFrameRate*timeBase) + 0.5);
- currentFrame->framePosition = video->m_curPosition;
- pthread_mutex_lock(&video->m_cacheMutex);
- BLI_addtail(&video->m_frameCacheBase, currentFrame);
- pthread_mutex_unlock(&video->m_cacheMutex);
- currentFrame = NULL;
- }
- }
- av_free_packet(&cachePacket->packet);
- BLI_addtail(&video->m_packetCacheFree, cachePacket);
- }
- if (currentFrame && endOfFile)
- {
- // no more packet and end of file => put a special frame that indicates that
- currentFrame->framePosition = -1;
- pthread_mutex_lock(&video->m_cacheMutex);
- BLI_addtail(&video->m_frameCacheBase, currentFrame);
- pthread_mutex_unlock(&video->m_cacheMutex);
- currentFrame = NULL;
- // no need to stay any longer in this thread
- break;
- }
- }
- // small sleep to avoid unnecessary looping
- PIL_sleep_ms(10);
- }
- // before quitting, put back the current frame to queue to allow freeing
- if (currentFrame)
- {
- pthread_mutex_lock(&video->m_cacheMutex);
- BLI_addtail(&video->m_frameCacheFree, currentFrame);
- pthread_mutex_unlock(&video->m_cacheMutex);
- }
- return 0;
-}
-
-// start thread to cache video frame from file/capture/stream
-// this function should be called only when the position in the stream is set for the
-// first frame to cache
-bool VideoFFmpeg::startCache()
-{
- if (!m_cacheStarted && m_isThreaded)
- {
- m_stopThread = false;
- for (int i=0; i<CACHE_FRAME_SIZE; i++)
- {
- CacheFrame *frame = new CacheFrame();
- frame->frame = allocFrameRGB();
- BLI_addtail(&m_frameCacheFree, frame);
- }
- for (int i=0; i<CACHE_PACKET_SIZE; i++)
- {
- CachePacket *packet = new CachePacket();
- BLI_addtail(&m_packetCacheFree, packet);
- }
- BLI_threadpool_init(&m_thread, cacheThread, 1);
- BLI_threadpool_insert(&m_thread, this);
- m_cacheStarted = true;
- }
- return m_cacheStarted;
-}
-
-void VideoFFmpeg::stopCache()
-{
- if (m_cacheStarted)
- {
- m_stopThread = true;
- BLI_threadpool_end(&m_thread);
- // now delete the cache
- CacheFrame *frame;
- CachePacket *packet;
- while ((frame = (CacheFrame *)m_frameCacheBase.first) != NULL)
- {
- BLI_remlink(&m_frameCacheBase, frame);
- MEM_freeN(frame->frame->data[0]);
- av_free(frame->frame);
- delete frame;
- }
- while ((frame = (CacheFrame *)m_frameCacheFree.first) != NULL)
- {
- BLI_remlink(&m_frameCacheFree, frame);
- MEM_freeN(frame->frame->data[0]);
- av_free(frame->frame);
- delete frame;
- }
- while ((packet = (CachePacket *)m_packetCacheBase.first) != NULL)
- {
- BLI_remlink(&m_packetCacheBase, packet);
- av_free_packet(&packet->packet);
- delete packet;
- }
- while ((packet = (CachePacket *)m_packetCacheFree.first) != NULL)
- {
- BLI_remlink(&m_packetCacheFree, packet);
- delete packet;
- }
- m_cacheStarted = false;
- }
-}
-
-void VideoFFmpeg::releaseFrame(AVFrame *frame)
-{
- if (frame == m_frameRGB)
- {
- // this is not a frame from the cache, ignore
- return;
- }
- // this frame MUST be the first one of the queue
- pthread_mutex_lock(&m_cacheMutex);
- CacheFrame *cacheFrame = (CacheFrame *)m_frameCacheBase.first;
- assert (cacheFrame != NULL && cacheFrame->frame == frame);
- BLI_remlink(&m_frameCacheBase, cacheFrame);
- BLI_addtail(&m_frameCacheFree, cacheFrame);
- pthread_mutex_unlock(&m_cacheMutex);
-}
-
-// open video file
-void VideoFFmpeg::openFile (char *filename)
-{
- if (openStream(filename, NULL, NULL) != 0)
- return;
-
- if (m_codecCtx->gop_size)
- m_preseek = (m_codecCtx->gop_size < 25) ? m_codecCtx->gop_size+1 : 25;
- else if (m_codecCtx->has_b_frames)
- m_preseek = 25; // should determine gopsize
- else
- m_preseek = 0;
-
- // get video time range
- m_range[0] = 0.0;
- m_range[1] = (double)m_formatCtx->duration / AV_TIME_BASE;
-
- // open base class
- VideoBase::openFile(filename);
-
- if (
- // ffmpeg reports that http source are actually non stream
- // but it is really not desirable to seek on http file, so force streaming.
- // It would be good to find this information from the context but there are no simple indication
- !strncmp(filename, "http://", 7) ||
- !strncmp(filename, "rtsp://", 7) ||
- (m_formatCtx->pb && !m_formatCtx->pb->seekable)
- )
- {
- // the file is in fact a streaming source, treat as cam to prevent seeking
- m_isFile = false;
- // but it's not handled exactly like a camera.
- m_isStreaming = true;
- // for streaming it is important to do non blocking read
- m_formatCtx->flags |= AVFMT_FLAG_NONBLOCK;
- }
-
- if (m_isImage)
- {
- // the file is to be treated as an image, i.e. load the first frame only
- m_isFile = false;
- // in case of reload, the filename is taken from m_imageName, no need to change it
- if (m_imageName.Ptr() != filename)
- m_imageName = filename;
- m_preseek = 0;
- m_avail = false;
- play();
- }
- // check if we should do multi-threading?
- if (!m_isImage && BLI_system_thread_count() > 1)
- {
- // never thread image: there are no frame to read ahead
- // no need to thread if the system has a single core
- m_isThreaded = true;
- }
-}
-
-
-// open video capture device
-void VideoFFmpeg::openCam (char *file, short camIdx)
-{
- // open camera source
- AVInputFormat *inputFormat;
- AVDictionary *formatParams = NULL;
- char filename[28], rateStr[20];
-
-#ifdef WIN32
- // video capture on windows only through Video For Windows driver
- inputFormat = av_find_input_format("vfwcap");
- if (!inputFormat)
- // Video For Windows not supported??
- return;
- sprintf(filename, "%d", camIdx);
-#else
- // In Linux we support two types of devices: VideoForLinux and DV1394.
- // the user specify it with the filename:
- // [<device_type>][:<standard>]
- // <device_type> : 'v4l' for VideoForLinux, 'dv1394' for DV1394. By default 'v4l'
- // <standard> : 'pal', 'secam' or 'ntsc'. By default 'ntsc'
- // The driver name is constructed automatically from the device type:
- // v4l : /dev/video<camIdx>
- // dv1394: /dev/dv1394/<camIdx>
- // If you have different driver name, you can specify the driver name explicitly
- // instead of device type. Examples of valid filename:
- // /dev/v4l/video0:pal
- // /dev/ieee1394/1:ntsc
- // dv1394:secam
- // v4l:pal
- char *p;
-
- if (file && strstr(file, "1394") != NULL)
- {
- // the user specifies a driver, check if it is v4l or d41394
- inputFormat = av_find_input_format("dv1394");
- sprintf(filename, "/dev/dv1394/%d", camIdx);
- } else
- {
- const char *formats[] = {"video4linux2,v4l2", "video4linux2", "video4linux"};
- int i, formatsCount = sizeof(formats) / sizeof(char*);
- for (i = 0; i < formatsCount; i++) {
- inputFormat = av_find_input_format(formats[i]);
- if (inputFormat)
- break;
- }
- sprintf(filename, "/dev/video%d", camIdx);
- }
- if (!inputFormat)
- // these format should be supported, check ffmpeg compilation
- return;
- if (file && strncmp(file, "/dev", 4) == 0)
- {
- // user does not specify a driver
- strncpy(filename, file, sizeof(filename));
- filename[sizeof(filename)-1] = 0;
- if ((p = strchr(filename, ':')) != 0)
- *p = 0;
- }
- if (file && (p = strchr(file, ':')) != NULL) {
- av_dict_set(&formatParams, "standard", p+1, 0);
- }
-#endif
- //frame rate
- if (m_captRate <= 0.f)
- m_captRate = defFrameRate;
- sprintf(rateStr, "%f", m_captRate);
-
- av_dict_set(&formatParams, "framerate", rateStr, 0);
-
- if (m_captWidth > 0 && m_captHeight > 0) {
- char video_size[64];
- BLI_snprintf(video_size, sizeof(video_size), "%dx%d", m_captWidth, m_captHeight);
- av_dict_set(&formatParams, "video_size", video_size, 0);
- }
-
- if (openStream(filename, inputFormat, &formatParams) != 0)
- return;
-
- // for video capture it is important to do non blocking read
- m_formatCtx->flags |= AVFMT_FLAG_NONBLOCK;
- // open base class
- VideoBase::openCam(file, camIdx);
- // check if we should do multi-threading?
- if (BLI_system_thread_count() > 1)
- {
- // no need to thread if the system has a single core
- m_isThreaded = true;
- }
-
- av_dict_free(&formatParams);
-}
-
-// play video
-bool VideoFFmpeg::play (void)
-{
- try
- {
- // if object is able to play
- if (VideoBase::play())
- {
- // set video position
- setPositions();
-
- if (m_isStreaming)
- {
- av_read_play(m_formatCtx);
- }
-
- // return success
- return true;
- }
- }
- CATCH_EXCP;
- return false;
-}
-
-
-// pause video
-bool VideoFFmpeg::pause (void)
-{
- try
- {
- if (VideoBase::pause())
- {
- if (m_isStreaming)
- {
- av_read_pause(m_formatCtx);
- }
- return true;
- }
- }
- CATCH_EXCP;
- return false;
-}
-
-// stop video
-bool VideoFFmpeg::stop (void)
-{
- try
- {
- VideoBase::stop();
- // force restart when play
- m_lastFrame = -1;
- return true;
- }
- CATCH_EXCP;
- return false;
-}
-
-
-// set video range
-void VideoFFmpeg::setRange (double start, double stop)
-{
- try
- {
- // set range
- if (m_isFile)
- {
- VideoBase::setRange(start, stop);
- // set range for video
- setPositions();
- }
- }
- CATCH_EXCP;
-}
-
-// set framerate
-void VideoFFmpeg::setFrameRate (float rate)
-{
- VideoBase::setFrameRate(rate);
-}
-
-
-// image calculation
-// load frame from video
-void VideoFFmpeg::calcImage (unsigned int texId, double ts)
-{
- if (m_status == SourcePlaying)
- {
- // get actual time
- double startTime = PIL_check_seconds_timer();
- double actTime;
- // timestamp passed from audio actuators can sometimes be slightly negative
- if (m_isFile && ts >= -0.5)
- {
- // allow setting timestamp only when not streaming
- actTime = ts;
- if (actTime * actFrameRate() < m_lastFrame)
- {
- // user is asking to rewind, force a cache clear to make sure we will do a seek
- // note that this does not decrement m_repeat if ts didn't reach m_range[1]
- stopCache();
- }
- }
- else
- {
- if (m_lastFrame == -1 && !m_isFile)
- m_startTime = startTime;
- actTime = startTime - m_startTime;
- }
- // if video has ended
- if (m_isFile && actTime * m_frameRate >= m_range[1])
- {
- // in any case, this resets the cache
- stopCache();
- // if repeats are set, decrease them
- if (m_repeat > 0)
- --m_repeat;
- // if video has to be replayed
- if (m_repeat != 0)
- {
- // reset its position
- actTime -= (m_range[1] - m_range[0]) / m_frameRate;
- m_startTime += (m_range[1] - m_range[0]) / m_frameRate;
- }
- // if video has to be stopped, stop it
- else
- {
- m_status = SourceStopped;
- return;
- }
- }
- // actual frame
- long actFrame = (m_isImage) ? m_lastFrame+1 : long(actTime * actFrameRate());
- // if actual frame differs from last frame
- if (actFrame != m_lastFrame)
- {
- AVFrame* frame;
- // get image
- if ((frame = grabFrame(actFrame)) != NULL)
- {
- if (!m_isFile && !m_cacheStarted)
- {
- // streaming without cache: detect synchronization problem
- double execTime = PIL_check_seconds_timer() - startTime;
- if (execTime > 0.005)
- {
- // exec time is too long, it means that the function was blocking
- // resynchronize the stream from this time
- m_startTime += execTime;
- }
- }
- // save actual frame
- m_lastFrame = actFrame;
- // init image, if needed
- init(short(m_codecCtx->width), short(m_codecCtx->height));
- // process image
- process((BYTE*)(frame->data[0]));
- // finished with the frame, release it so that cache can reuse it
- releaseFrame(frame);
- // in case it is an image, automatically stop reading it
- if (m_isImage)
- {
- m_status = SourceStopped;
- // close the file as we don't need it anymore
- release();
- }
- } else if (m_isStreaming)
- {
- // we didn't get a frame and we are streaming, this may be due to
- // a delay in the network or because we are getting the frame too fast.
- // In the later case, shift time by a small amount to compensate for a drift
- m_startTime += 0.001;
- }
- }
- }
-}
-
-
-// set actual position
-void VideoFFmpeg::setPositions (void)
-{
- // set video start time
- m_startTime = PIL_check_seconds_timer();
- // if file is played and actual position is before end position
- if (!m_eof && m_lastFrame >= 0 && (!m_isFile || m_lastFrame < m_range[1] * actFrameRate()))
- // continue from actual position
- m_startTime -= double(m_lastFrame) / actFrameRate();
- else {
- m_startTime -= m_range[0];
- // start from beginning, stop cache just in case
- stopCache();
- }
-}
-
-// position pointer in file, position in second
-AVFrame *VideoFFmpeg::grabFrame(long position)
-{
- AVPacket packet;
- int frameFinished;
- int posFound = 1;
- bool frameLoaded = false;
- int64_t targetTs = 0;
- CacheFrame *frame;
- int64_t dts = 0;
-
- if (m_cacheStarted)
- {
- // when cache is active, we must not read the file directly
- do {
- pthread_mutex_lock(&m_cacheMutex);
- frame = (CacheFrame *)m_frameCacheBase.first;
- pthread_mutex_unlock(&m_cacheMutex);
- // no need to remove the frame from the queue: the cache thread does not touch the head, only the tail
- if (frame == NULL)
- {
- // no frame in cache, in case of file it is an abnormal situation
- if (m_isFile)
- {
- // go back to no threaded reading
- stopCache();
- break;
- }
- return NULL;
- }
- if (frame->framePosition == -1)
- {
- // this frame mark the end of the file (only used for file)
- // leave in cache to make sure we don't miss it
- m_eof = true;
- return NULL;
- }
- // for streaming, always return the next frame,
- // that's what grabFrame does in non cache mode anyway.
- if (m_isStreaming || frame->framePosition == position)
- {
- return frame->frame;
- }
- // for cam, skip old frames to keep image realtime.
- // There should be no risk of clock drift since it all happens on the same CPU
- if (frame->framePosition > position)
- {
- // this can happen after rewind if the seek didn't find the first frame
- // the frame in the buffer is ahead of time, just leave it there
- return NULL;
- }
- // this frame is not useful, release it
- pthread_mutex_lock(&m_cacheMutex);
- BLI_remlink(&m_frameCacheBase, frame);
- BLI_addtail(&m_frameCacheFree, frame);
- pthread_mutex_unlock(&m_cacheMutex);
- } while (true);
- }
- double timeBase = av_q2d(m_formatCtx->streams[m_videoStream]->time_base);
- int64_t startTs = m_formatCtx->streams[m_videoStream]->start_time;
- if (startTs == AV_NOPTS_VALUE)
- startTs = 0;
-
- // come here when there is no cache or cache has been stopped
- // locate the frame, by seeking if necessary (seeking is only possible for files)
- if (m_isFile)
- {
- // first check if the position that we are looking for is in the preseek range
- // if so, just read the frame until we get there
- if (position > m_curPosition + 1
- && m_preseek
- && position - (m_curPosition + 1) < m_preseek)
- {
- while (av_read_frame(m_formatCtx, &packet)>=0)
- {
- if (packet.stream_index == m_videoStream)
- {
- avcodec_decode_video2(
- m_codecCtx,
- m_frame, &frameFinished,
- &packet);
- if (frameFinished)
- {
- m_curPosition = (long)((packet.dts-startTs) * (m_baseFrameRate*timeBase) + 0.5);
- }
- }
- av_free_packet(&packet);
- if (position == m_curPosition+1)
- break;
- }
- }
- // if the position is not in preseek, do a direct jump
- if (position != m_curPosition + 1)
- {
- int64_t pos = (int64_t)((position - m_preseek) / (m_baseFrameRate*timeBase));
-
- if (pos < 0)
- pos = 0;
-
- pos += startTs;
-
- if (position <= m_curPosition || !m_eof)
- {
-#if 0
- // Tried to make this work but couldn't: seeking on byte is ignored by the
- // format plugin and it will generally continue to read from last timestamp.
- // Too bad because frame seek is not always able to get the first frame
- // of the file.
- if (position <= m_preseek)
- {
- // we can safely go the beginning of the file
- if (av_seek_frame(m_formatCtx, m_videoStream, 0, AVSEEK_FLAG_BYTE) >= 0)
- {
- // binary seek does not reset the timestamp, must do it now
- av_update_cur_dts(m_formatCtx, m_formatCtx->streams[m_videoStream], startTs);
- m_curPosition = 0;
- }
- }
- else
-#endif
- {
- // current position is now lost, guess a value.
- if (av_seek_frame(m_formatCtx, m_videoStream, pos, AVSEEK_FLAG_BACKWARD) >= 0)
- {
- // current position is now lost, guess a value.
- // It's not important because it will be set at this end of this function
- m_curPosition = position - m_preseek - 1;
- }
- }
- }
- // this is the timestamp of the frame we're looking for
- targetTs = (int64_t)(position / (m_baseFrameRate * timeBase)) + startTs;
-
- posFound = 0;
- avcodec_flush_buffers(m_codecCtx);
- }
- } else if (m_isThreaded)
- {
- // cache is not started but threading is possible
- // better not read the stream => make take some time, better start caching
- if (startCache())
- return NULL;
- // Abnormal!!! could not start cache, fall back on direct read
- m_isThreaded = false;
- }
-
- // find the correct frame, in case of streaming and no cache, it means just
- // return the next frame. This is not quite correct, may need more work
- while (av_read_frame(m_formatCtx, &packet) >= 0)
- {
- if (packet.stream_index == m_videoStream)
- {
- AVFrame *input = m_frame;
- short counter = 0;
-
- /* If m_isImage, while the data is not read properly (png, tiffs, etc formats may need several pass), else don't need while loop*/
- do {
- avcodec_decode_video2(m_codecCtx, m_frame, &frameFinished, &packet);
- counter++;
- } while ((input->data[0] == 0 && input->data[1] == 0 && input->data[2] == 0 && input->data[3] == 0) && counter < 10 && m_isImage);
-
- // remember dts to compute exact frame number
- dts = packet.dts;
- if (frameFinished && !posFound)
- {
- if (dts >= targetTs)
- {
- posFound = 1;
- }
- }
-
- if (frameFinished && posFound == 1)
- {
- AVFrame * input = m_frame;
-
- /* This means the data wasnt read properly,
- * this check stops crashing */
- if ( input->data[0]==0 && input->data[1]==0
- && input->data[2]==0 && input->data[3]==0)
- {
- av_free_packet(&packet);
- break;
- }
-
- if (m_deinterlace)
- {
- if (avpicture_deinterlace(
- (AVPicture*) m_frameDeinterlaced,
- (const AVPicture*) m_frame,
- m_codecCtx->pix_fmt,
- m_codecCtx->width,
- m_codecCtx->height) >= 0)
- {
- input = m_frameDeinterlaced;
- }
- }
- // convert to RGB24
- sws_scale(m_imgConvertCtx,
- input->data,
- input->linesize,
- 0,
- m_codecCtx->height,
- m_frameRGB->data,
- m_frameRGB->linesize);
- av_free_packet(&packet);
- frameLoaded = true;
- break;
- }
- }
- av_free_packet(&packet);
- }
- m_eof = m_isFile && !frameLoaded;
- if (frameLoaded)
- {
- m_curPosition = (long)((dts-startTs) * (m_baseFrameRate*timeBase) + 0.5);
- if (m_isThreaded)
- {
- // normal case for file: first locate, then start cache
- if (!startCache())
- {
- // Abnormal!! could not start cache, return to non-cache mode
- m_isThreaded = false;
- }
- }
- return m_frameRGB;
- }
- return NULL;
-}
-
-
-// python methods
-
-
-// cast Image pointer to VideoFFmpeg
-inline VideoFFmpeg * getVideoFFmpeg (PyImage *self)
-{ return static_cast<VideoFFmpeg*>(self->m_image); }
-
-
-// object initialization
-static int VideoFFmpeg_init(PyObject *pySelf, PyObject *args, PyObject *kwds)
-{
- PyImage *self = reinterpret_cast<PyImage*>(pySelf);
- // parameters - video source
- // file name or format type for capture (only for Linux: video4linux or dv1394)
- char * file = NULL;
- // capture device number
- short capt = -1;
- // capture width, only if capt is >= 0
- short width = 0;
- // capture height, only if capt is >= 0
- short height = 0;
- // capture rate, only if capt is >= 0
- float rate = 25.f;
-
- static const char *kwlist[] = {"file", "capture", "rate", "width", "height", NULL};
-
- // get parameters
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|hfhh",
- const_cast<char**>(kwlist), &file, &capt, &rate, &width, &height))
- return -1;
-
- try
- {
- // create video object
- Video_init<VideoFFmpeg>(self);
-
- // set thread usage
- getVideoFFmpeg(self)->initParams(width, height, rate);
-
- // open video source
- Video_open(getVideo(self), file, capt);
- }
- catch (Exception & exp)
- {
- exp.report();
- return -1;
- }
- // initialization succeded
- return 0;
-}
-
-static PyObject *VideoFFmpeg_getPreseek(PyImage *self, void *closure)
-{
- return Py_BuildValue("h", getFFmpeg(self)->getPreseek());
-}
-
-// set range
-static int VideoFFmpeg_setPreseek(PyImage *self, PyObject *value, void *closure)
-{
- // check validity of parameter
- if (value == NULL || !PyLong_Check(value))
- {
- PyErr_SetString(PyExc_TypeError, "The value must be an integer");
- return -1;
- }
- // set preseek
- getFFmpeg(self)->setPreseek(PyLong_AsLong(value));
- // success
- return 0;
-}
-
-// get deinterlace
-static PyObject *VideoFFmpeg_getDeinterlace(PyImage *self, void *closure)
-{
- if (getFFmpeg(self)->getDeinterlace())
- Py_RETURN_TRUE;
- else
- Py_RETURN_FALSE;
-}
-
-// set flip
-static int VideoFFmpeg_setDeinterlace(PyImage *self, PyObject *value, void *closure)
-{
- // check parameter, report failure
- if (value == NULL || !PyBool_Check(value))
- {
- PyErr_SetString(PyExc_TypeError, "The value must be a bool");
- return -1;
- }
- // set deinterlace
- getFFmpeg(self)->setDeinterlace(value == Py_True);
- // success
- return 0;
-}
-
-// methods structure
-static PyMethodDef videoMethods[] =
-{ // methods from VideoBase class
- {"play", (PyCFunction)Video_play, METH_NOARGS, "Play (restart) video"},
- {"pause", (PyCFunction)Video_pause, METH_NOARGS, "pause video"},
- {"stop", (PyCFunction)Video_stop, METH_NOARGS, "stop video (play will replay it from start)"},
- {"refresh", (PyCFunction)Video_refresh, METH_VARARGS, "Refresh video - get its status"},
- {NULL}
-};
-// attributes structure
-static PyGetSetDef videoGetSets[] =
-{ // methods from VideoBase class
- {(char*)"status", (getter)Video_getStatus, NULL, (char*)"video status", NULL},
- {(char*)"range", (getter)Video_getRange, (setter)Video_setRange, (char*)"replay range", NULL},
- {(char*)"repeat", (getter)Video_getRepeat, (setter)Video_setRepeat, (char*)"repeat count, -1 for infinite repeat", NULL},
- {(char*)"framerate", (getter)Video_getFrameRate, (setter)Video_setFrameRate, (char*)"frame rate", NULL},
- // attributes from ImageBase class
- {(char*)"valid", (getter)Image_valid, NULL, (char*)"bool to tell if an image is available", NULL},
- {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
- {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
- {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbor)", NULL},
- {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
- {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
- {(char*)"preseek", (getter)VideoFFmpeg_getPreseek, (setter)VideoFFmpeg_setPreseek, (char*)"nb of frames of preseek", NULL},
- {(char*)"deinterlace", (getter)VideoFFmpeg_getDeinterlace, (setter)VideoFFmpeg_setDeinterlace, (char*)"deinterlace image", NULL},
- {NULL}
-};
-
-// python type declaration
-PyTypeObject VideoFFmpegType =
-{
- PyVarObject_HEAD_INIT(NULL, 0)
- "VideoTexture.VideoFFmpeg", /*tp_name*/
- sizeof(PyImage), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Image_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- &imageBufferProcs, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
- "FFmpeg video source", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- videoMethods, /* tp_methods */
- 0, /* tp_members */
- videoGetSets, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)VideoFFmpeg_init, /* tp_init */
- 0, /* tp_alloc */
- Image_allocNew, /* tp_new */
-};
-
-// object initialization
-static int ImageFFmpeg_init(PyObject *pySelf, PyObject *args, PyObject *kwds)
-{
- PyImage *self = reinterpret_cast<PyImage*>(pySelf);
- // parameters - video source
- // file name or format type for capture (only for Linux: video4linux or dv1394)
- char * file = NULL;
-
- // get parameters
- if (!PyArg_ParseTuple(args, "s:ImageFFmpeg", &file))
- return -1;
-
- try
- {
- // create video object
- Video_init<VideoFFmpeg>(self);
-
- getVideoFFmpeg(self)->initParams(0, 0, 1.0, true);
-
- // open video source
- Video_open(getVideo(self), file, -1);
- }
- catch (Exception & exp)
- {
- exp.report();
- return -1;
- }
- // initialization succeded
- return 0;
-}
-
-static PyObject *Image_reload(PyImage *self, PyObject *args)
-{
- char * newname = NULL;
- if (!PyArg_ParseTuple(args, "|s:reload", &newname))
- return NULL;
- if (self->m_image != NULL)
- {
- VideoFFmpeg* video = getFFmpeg(self);
- // check type of object
- if (!newname)
- newname = video->getImageName();
- if (!newname) {
- // if not set, retport error
- PyErr_SetString(PyExc_RuntimeError, "No image file name given");
- return NULL;
- }
- // make sure the previous file is cleared
- video->release();
- // open the new file
- video->openFile(newname);
- }
- Py_RETURN_NONE;
-}
-
-// methods structure
-static PyMethodDef imageMethods[] =
-{ // methods from VideoBase class
- {"refresh", (PyCFunction)Video_refresh, METH_VARARGS, "Refresh image, i.e. load it"},
- {"reload", (PyCFunction)Image_reload, METH_VARARGS, "Reload image, i.e. reopen it"},
- {NULL}
-};
-// attributes structure
-static PyGetSetDef imageGetSets[] =
-{ // methods from VideoBase class
- {(char*)"status", (getter)Video_getStatus, NULL, (char*)"video status", NULL},
- // attributes from ImageBase class
- {(char*)"valid", (getter)Image_valid, NULL, (char*)"bool to tell if an image is available", NULL},
- {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
- {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
- {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbor)", NULL},
- {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
- {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
- {NULL}
-};
-
-// python type declaration
-PyTypeObject ImageFFmpegType =
-{
- PyVarObject_HEAD_INIT(NULL, 0)
- "VideoTexture.ImageFFmpeg", /*tp_name*/
- sizeof(PyImage), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Image_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- &imageBufferProcs, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
- "FFmpeg image source", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- imageMethods, /* tp_methods */
- 0, /* tp_members */
- imageGetSets, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)ImageFFmpeg_init, /* tp_init */
- 0, /* tp_alloc */
- Image_allocNew, /* tp_new */
-};
-
-#endif //WITH_FFMPEG
-
-