2 -----------------------------------------------------------------------------
3 This source file is part of VideoTexture library
5 Copyright (c) 2007 The Zdeno Ash Miklas
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the Free Software
9 Foundation; either version 2 of the License, or (at your option) any later
12 This program is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
19 http://www.gnu.org/copyleft/lesser.txt.
20 -----------------------------------------------------------------------------
23 // INT64_C fix for some linux machines (C99ism)
24 #define __STDC_CONSTANT_MACROS
28 #include "MEM_guardedalloc.h"
33 #include "Exception.h"
34 #include "VideoFFmpeg.h"
39 const double defFrameRate = 25.0;
40 // time scale constant
41 const long timeScale = 1000;
43 // macro for exception handling and logging
44 #define CATCH_EXCP catch (Exception & exp) \
45 { exp.report(); m_status = SourceError; }
47 extern "C" void do_init_ffmpeg();
52 VideoFFmpeg::VideoFFmpeg (HRESULT * hRslt) : VideoBase(),
53 m_codec(NULL), m_formatCtx(NULL), m_codecCtx(NULL),
54 m_frame(NULL), m_frameDeinterlaced(NULL), m_frameRGB(NULL), m_imgConvertCtx(NULL),
55 m_deinterlace(false), m_preseek(0), m_videoStream(-1), m_baseFrameRate(25.0),
56 m_lastFrame(-1), m_curPosition(-1), m_startTime(0),
57 m_captWidth(0), m_captHeight(0), m_captRate(0.f), m_isImage(false)
61 // force flip because ffmpeg always return the image in the wrong orientation for texture
68 VideoFFmpeg::~VideoFFmpeg ()
74 bool VideoFFmpeg::release()
79 avcodec_close(m_codecCtx);
84 av_close_input_file(m_formatCtx);
92 if (m_frameDeinterlaced)
94 MEM_freeN(m_frameDeinterlaced->data[0]);
95 av_free(m_frameDeinterlaced);
96 m_frameDeinterlaced = NULL;
100 MEM_freeN(m_frameRGB->data[0]);
106 sws_freeContext(m_imgConvertCtx);
107 m_imgConvertCtx = NULL;
110 m_status = SourceStopped;
115 // set initial parameters
116 void VideoFFmpeg::initParams (short width, short height, float rate, bool image)
119 m_captHeight = height;
124 int VideoFFmpeg::openStream(const char *filename, AVInputFormat *inputFormat, AVFormatParameters *formatParams)
126 AVFormatContext *formatCtx;
129 AVCodecContext *codecCtx;
131 if(av_open_input_file(&formatCtx, filename, inputFormat, 0, formatParams)!=0)
134 if(av_find_stream_info(formatCtx)<0)
136 av_close_input_file(formatCtx);
140 /* Find the first video stream */
142 for(i=0; i<formatCtx->nb_streams; i++)
144 if(formatCtx->streams[i] &&
145 get_codec_from_stream(formatCtx->streams[i]) &&
146 (get_codec_from_stream(formatCtx->streams[i])->codec_type==CODEC_TYPE_VIDEO))
155 av_close_input_file(formatCtx);
159 codecCtx = get_codec_from_stream(formatCtx->streams[videoStream]);
161 /* Find the decoder for the video stream */
162 codec=avcodec_find_decoder(codecCtx->codec_id);
165 av_close_input_file(formatCtx);
168 codecCtx->workaround_bugs = 1;
169 if(avcodec_open(codecCtx, codec)<0)
171 av_close_input_file(formatCtx);
175 #ifdef FFMPEG_OLD_FRAME_RATE
176 if(codecCtx->frame_rate>1000 && codecCtx->frame_rate_base==1)
177 codecCtx->frame_rate_base=1000;
178 m_baseFrameRate = (double)codecCtx->frame_rate / (double)codecCtx->frame_rate_base;
180 m_baseFrameRate = av_q2d(formatCtx->streams[videoStream]->r_frame_rate);
182 if (m_baseFrameRate <= 0.0)
183 m_baseFrameRate = defFrameRate;
186 m_codecCtx = codecCtx;
187 m_formatCtx = formatCtx;
188 m_videoStream = videoStream;
189 m_frame = avcodec_alloc_frame();
190 m_frameDeinterlaced = avcodec_alloc_frame();
191 m_frameRGB = avcodec_alloc_frame();
194 // allocate buffer if deinterlacing is required
195 avpicture_fill((AVPicture*)m_frameDeinterlaced,
196 (uint8_t*)MEM_callocN(avpicture_get_size(
198 m_codecCtx->width, m_codecCtx->height),
199 "ffmpeg deinterlace"),
200 m_codecCtx->pix_fmt, m_codecCtx->width, m_codecCtx->height);
202 // allocate buffer to store final decoded frame
203 avpicture_fill((AVPicture*)m_frameRGB,
204 (uint8_t*)MEM_callocN(avpicture_get_size(
206 m_codecCtx->width, m_codecCtx->height),
208 PIX_FMT_RGB24, m_codecCtx->width, m_codecCtx->height);
209 // allocate sws context
210 m_imgConvertCtx = sws_getContext(
220 if (!m_imgConvertCtx) {
221 avcodec_close(m_codecCtx);
222 av_close_input_file(m_formatCtx);
224 MEM_freeN(m_frameDeinterlaced->data[0]);
225 av_free(m_frameDeinterlaced);
226 MEM_freeN(m_frameRGB->data[0]);
234 void VideoFFmpeg::openFile (char * filename)
238 if (openStream(filename, NULL, NULL) != 0)
241 if (m_codecCtx->gop_size)
242 m_preseek = (m_codecCtx->gop_size < 25) ? m_codecCtx->gop_size+1 : 25;
243 else if (m_codecCtx->has_b_frames)
244 m_preseek = 25; // should determine gopsize
248 // get video time range
250 m_range[1] = (double)m_formatCtx->duration / AV_TIME_BASE;
253 VideoBase::openFile(filename);
256 #ifdef FFMPEG_PB_IS_POINTER
257 m_formatCtx->pb && m_formatCtx->pb->is_streamed
259 m_formatCtx->pb.is_streamed
263 // the file is in fact a streaming source, prevent seeking
265 // for streaming it is important to do non blocking read
266 m_formatCtx->flags |= AVFMT_FLAG_NONBLOCK;
271 // the file is to be treated as an image, i.e. load the first frame only
273 // in case of reload, the filename is taken from m_imageName, no need to change it
274 if (m_imageName.Ptr() != filename)
275 m_imageName = filename;
283 // open video capture device
284 void VideoFFmpeg::openCam (char * file, short camIdx)
286 // open camera source
287 AVInputFormat *inputFormat;
288 AVFormatParameters formatParams;
289 AVRational frameRate;
290 char *p, filename[28], rateStr[20];
294 memset(&formatParams, 0, sizeof(formatParams));
296 // video capture on windows only through Video For Windows driver
297 inputFormat = av_find_input_format("vfwcap");
299 // Video For Windows not supported??
301 sprintf(filename, "%d", camIdx);
303 // In Linux we support two types of devices: VideoForLinux and DV1394.
304 // the user specify it with the filename:
305 // [<device_type>][:<standard>]
306 // <device_type> : 'v4l' for VideoForLinux, 'dv1394' for DV1394. By default 'v4l'
307 // <standard> : 'pal', 'secam' or 'ntsc'. By default 'ntsc'
308 // The driver name is constructed automatically from the device type:
309 // v4l : /dev/video<camIdx>
310 // dv1394: /dev/dv1394/<camIdx>
311 // If you have different driver name, you can specify the driver name explicitely
312 // instead of device type. Examples of valid filename:
313 // /dev/v4l/video0:pal
314 // /dev/ieee1394/1:ntsc
317 if (file && strstr(file, "1394") != NULL)
319 // the user specifies a driver, check if it is v4l or d41394
320 inputFormat = av_find_input_format("dv1394");
321 sprintf(filename, "/dev/dv1394/%d", camIdx);
324 inputFormat = av_find_input_format("video4linux");
325 sprintf(filename, "/dev/video%d", camIdx);
328 // these format should be supported, check ffmpeg compilation
330 if (file && strncmp(file, "/dev", 4) == 0)
332 // user does not specify a driver
333 strncpy(filename, file, sizeof(filename));
334 filename[sizeof(filename)-1] = 0;
335 if ((p = strchr(filename, ':')) != 0)
338 if (file && (p = strchr(file, ':')) != NULL)
339 formatParams.standard = p+1;
342 if (m_captRate <= 0.f)
343 m_captRate = defFrameRate;
344 sprintf(rateStr, "%f", m_captRate);
345 av_parse_video_frame_rate(&frameRate, rateStr);
346 // populate format parameters
347 // need to specify the time base = inverse of rate
348 formatParams.time_base.num = frameRate.den;
349 formatParams.time_base.den = frameRate.num;
350 formatParams.width = m_captWidth;
351 formatParams.height = m_captHeight;
353 if (openStream(filename, inputFormat, &formatParams) != 0)
356 // for video capture it is important to do non blocking read
357 m_formatCtx->flags |= AVFMT_FLAG_NONBLOCK;
359 VideoBase::openCam(file, camIdx);
363 bool VideoFFmpeg::play (void)
367 // if object is able to play
368 if (VideoBase::play())
370 // set video position
382 bool VideoFFmpeg::stop (void)
386 if (VideoBase::stop())
397 void VideoFFmpeg::setRange (double start, double stop)
402 VideoBase::setRange(start, stop);
403 // set range for video
410 void VideoFFmpeg::setFrameRate (float rate)
412 VideoBase::setFrameRate(rate);
417 void VideoFFmpeg::calcImage (unsigned int texId)
423 // load frame from video
424 void VideoFFmpeg::loadFrame (void)
427 double actTime = PIL_check_seconds_timer() - m_startTime;
428 // if video has ended
429 if (m_isFile && actTime * m_frameRate >= m_range[1])
431 // if repeats are set, decrease them
434 // if video has to be replayed
437 // reset its position
438 actTime -= (m_range[1] - m_range[0]) / m_frameRate;
439 m_startTime += (m_range[1] - m_range[0]) / m_frameRate;
441 // if video has to be stopped, stop it
443 m_status = SourceStopped;
445 // if video is playing
446 if (m_status == SourcePlaying)
449 long actFrame = m_isFile ? long(actTime * actFrameRate()) : m_lastFrame + 1;
450 // if actual frame differs from last frame
451 if (actFrame != m_lastFrame)
454 if(grabFrame(actFrame))
456 AVFrame* frame = getFrame();
458 m_lastFrame = actFrame;
459 // init image, if needed
460 init(short(m_codecCtx->width), short(m_codecCtx->height));
462 process((BYTE*)(frame->data[0]));
463 // in case it is an image, automatically stop reading it
466 m_status = SourceStopped;
467 // close the file as we don't need it anymore
476 // set actual position
477 void VideoFFmpeg::setPositions (void)
479 // set video start time
480 m_startTime = PIL_check_seconds_timer();
481 // if file is played and actual position is before end position
482 if (m_isFile && m_lastFrame >= 0 && m_lastFrame < m_range[1] * actFrameRate())
483 // continue from actual position
484 m_startTime -= double(m_lastFrame) / actFrameRate();
486 m_startTime -= m_range[0];
489 // position pointer in file, position in second
490 bool VideoFFmpeg::grabFrame(long position)
495 bool frameLoaded = false;
496 long long targetTs = 0;
498 // first check if the position that we are looking for is in the preseek range
499 // if so, just read the frame until we get there
500 if (position > m_curPosition + 1
502 && position - (m_curPosition + 1) < m_preseek)
504 while(av_read_frame(m_formatCtx, &packet)>=0)
506 if (packet.stream_index == m_videoStream)
508 avcodec_decode_video(
510 m_frame, &frameFinished,
511 packet.data, packet.size);
515 av_free_packet(&packet);
516 if (position == m_curPosition+1)
520 // if the position is not in preseek, do a direct jump
521 if (position != m_curPosition + 1) {
522 double timeBase = av_q2d(m_formatCtx->streams[m_videoStream]->time_base);
523 long long pos = (long long)
524 ((long long) (position - m_preseek) * AV_TIME_BASE / m_baseFrameRate);
525 long long startTs = m_formatCtx->streams[m_videoStream]->start_time;
530 if (startTs != AV_NOPTS_VALUE)
531 pos += (long long)(startTs * AV_TIME_BASE * timeBase);
533 av_seek_frame(m_formatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
534 // current position is now lost, guess a value.
535 // It's not important because it will be set at this end of this function
536 m_curPosition = position - m_preseek - 1;
537 // this is the timestamp of the frame we're looking for
538 targetTs = (long long)(((double) position) / m_baseFrameRate / timeBase);
539 if (startTs != AV_NOPTS_VALUE)
543 avcodec_flush_buffers(m_codecCtx);
546 while(av_read_frame(m_formatCtx, &packet)>=0)
548 if(packet.stream_index == m_videoStream)
550 avcodec_decode_video(m_codecCtx,
551 m_frame, &frameFinished,
552 packet.data, packet.size);
554 if (frameFinished && !posFound)
556 if (packet.dts >= targetTs)
560 if(frameFinished && posFound == 1)
562 AVFrame * input = m_frame;
564 /* This means the data wasnt read properly,
565 this check stops crashing */
566 if ( input->data[0]==0 && input->data[1]==0
567 && input->data[2]==0 && input->data[3]==0)
569 av_free_packet(&packet);
575 if (avpicture_deinterlace(
576 (AVPicture*) m_frameDeinterlaced,
577 (const AVPicture*) m_frame,
580 m_codecCtx->height) >= 0)
582 input = m_frameDeinterlaced;
586 sws_scale(m_imgConvertCtx,
592 m_frameRGB->linesize);
593 av_free_packet(&packet);
598 av_free_packet(&packet);
601 m_curPosition = position;
609 // cast Image pointer to VideoFFmpeg
610 inline VideoFFmpeg * getVideoFFmpeg (PyImage * self)
611 { return static_cast<VideoFFmpeg*>(self->m_image); }
614 // object initialization
615 static int VideoFFmpeg_init (PyObject * pySelf, PyObject * args, PyObject * kwds)
617 PyImage * self = reinterpret_cast<PyImage*>(pySelf);
618 // parameters - video source
619 // file name or format type for capture (only for Linux: video4linux or dv1394)
621 // capture device number
623 // capture width, only if capt is >= 0
625 // capture height, only if capt is >= 0
627 // capture rate, only if capt is >= 0
630 static char *kwlist[] = {"file", "capture", "rate", "width", "height", NULL};
633 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|hfhh", kwlist, &file, &capt,
634 &rate, &width, &height))
639 // create video object
640 Video_init<VideoFFmpeg>(self);
643 getVideoFFmpeg(self)->initParams(width, height, rate);
646 Video_open(getVideo(self), file, capt);
648 catch (Exception & exp)
653 // initialization succeded
657 PyObject * VideoFFmpeg_getPreseek (PyImage *self, void * closure)
659 return Py_BuildValue("h", getFFmpeg(self)->getPreseek());
663 int VideoFFmpeg_setPreseek (PyImage * self, PyObject * value, void * closure)
665 // check validity of parameter
666 if (value == NULL || !PyInt_Check(value))
668 PyErr_SetString(PyExc_TypeError, "The value must be an integer");
672 getFFmpeg(self)->setPreseek(PyInt_AsLong(value));
678 PyObject * VideoFFmpeg_getDeinterlace (PyImage * self, void * closure)
680 if (getFFmpeg(self)->getDeinterlace())
687 int VideoFFmpeg_setDeinterlace (PyImage * self, PyObject * value, void * closure)
689 // check parameter, report failure
690 if (value == NULL || !PyBool_Check(value))
692 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
696 getFFmpeg(self)->setDeinterlace(value == Py_True);
702 static PyMethodDef videoMethods[] =
703 { // methods from VideoBase class
704 {"play", (PyCFunction)Video_play, METH_NOARGS, "Play video"},
705 {"stop", (PyCFunction)Video_stop, METH_NOARGS, "Stop (pause) video"},
706 {"refresh", (PyCFunction)Video_refresh, METH_NOARGS, "Refresh video - get its status"},
709 // attributes structure
710 static PyGetSetDef videoGetSets[] =
711 { // methods from VideoBase class
712 {(char*)"status", (getter)Video_getStatus, NULL, (char*)"video status", NULL},
713 {(char*)"range", (getter)Video_getRange, (setter)Video_setRange, (char*)"replay range", NULL},
714 {(char*)"repeat", (getter)Video_getRepeat, (setter)Video_setRepeat, (char*)"repeat count, -1 for infinite repeat", NULL},
715 {(char*)"framerate", (getter)Video_getFrameRate, (setter)Video_setFrameRate, (char*)"frame rate", NULL},
716 // attributes from ImageBase class
717 {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
718 {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
719 {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbour)", NULL},
720 {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
721 {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
722 {(char*)"preseek", (getter)VideoFFmpeg_getPreseek, (setter)VideoFFmpeg_setPreseek, (char*)"nb of frames of preseek", NULL},
723 {(char*)"deinterlace", (getter)VideoFFmpeg_getDeinterlace, (setter)VideoFFmpeg_setDeinterlace, (char*)"deinterlace image", NULL},
727 // python type declaration
728 PyTypeObject VideoFFmpegType =
730 PyObject_HEAD_INIT(NULL)
732 "VideoTexture.VideoFFmpeg", /*tp_name*/
733 sizeof(PyImage), /*tp_basicsize*/
735 (destructor)Image_dealloc, /*tp_dealloc*/
742 0, /*tp_as_sequence*/
750 Py_TPFLAGS_DEFAULT, /*tp_flags*/
751 "FFmpeg video source", /* tp_doc */
754 0, /* tp_richcompare */
755 0, /* tp_weaklistoffset */
758 videoMethods, /* tp_methods */
760 videoGetSets, /* tp_getset */
763 0, /* tp_descr_get */
764 0, /* tp_descr_set */
765 0, /* tp_dictoffset */
766 (initproc)VideoFFmpeg_init, /* tp_init */
768 Image_allocNew, /* tp_new */
771 // object initialization
772 static int ImageFFmpeg_init (PyObject * pySelf, PyObject * args, PyObject * kwds)
774 PyImage * self = reinterpret_cast<PyImage*>(pySelf);
775 // parameters - video source
776 // file name or format type for capture (only for Linux: video4linux or dv1394)
780 if (!PyArg_ParseTuple(args, "s", &file))
785 // create video object
786 Video_init<VideoFFmpeg>(self);
788 getVideoFFmpeg(self)->initParams(0, 0, 1.0, true);
791 Video_open(getVideo(self), file, -1);
793 catch (Exception & exp)
798 // initialization succeded
802 PyObject * Image_reload (PyImage * self, PyObject *args)
804 char * newname = NULL;
806 if (self->m_image != NULL && PyArg_ParseTuple(args, "|s", &newname))
808 VideoFFmpeg* video = getFFmpeg(self);
809 // check type of object
811 newname = video->getImageName();
813 // if not set, retport error
814 PyErr_SetString(PyExc_RuntimeError, "No image file name given");
817 // make sure the previous file is cleared
820 video->openFile(newname);
826 static PyMethodDef imageMethods[] =
827 { // methods from VideoBase class
828 {"refresh", (PyCFunction)Video_refresh, METH_NOARGS, "Refresh image, i.e. load it"},
829 {"reload", (PyCFunction)Image_reload, METH_VARARGS, "Reload image, i.e. reopen it"},
832 // attributes structure
833 static PyGetSetDef imageGetSets[] =
834 { // methods from VideoBase class
835 {(char*)"status", (getter)Video_getStatus, NULL, (char*)"video status", NULL},
836 // attributes from ImageBase class
837 {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
838 {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
839 {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbour)", NULL},
840 {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
841 {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
845 // python type declaration
846 PyTypeObject ImageFFmpegType =
848 PyObject_HEAD_INIT(NULL)
850 "VideoTexture.ImageFFmpeg", /*tp_name*/
851 sizeof(PyImage), /*tp_basicsize*/
853 (destructor)Image_dealloc, /*tp_dealloc*/
860 0, /*tp_as_sequence*/
868 Py_TPFLAGS_DEFAULT, /*tp_flags*/
869 "FFmpeg image source", /* tp_doc */
872 0, /* tp_richcompare */
873 0, /* tp_weaklistoffset */
876 imageMethods, /* tp_methods */
878 imageGetSets, /* tp_getset */
881 0, /* tp_descr_get */
882 0, /* tp_descr_set */
883 0, /* tp_dictoffset */
884 (initproc)ImageFFmpeg_init, /* tp_init */
886 Image_allocNew, /* tp_new */