| [336] | 1 | # PySoy's textures.Video class |
|---|
| [214] | 2 | # |
|---|
| [1345] | 3 | # Copyright (C) 2006,2007,2008,2009 PySoy Group |
|---|
| [214] | 4 | # |
|---|
| 5 | # This program is free software; you can redistribute it and/or modify |
|---|
| [1144] | 6 | # it under the terms of the GNU Affero General Public License as published |
|---|
| [1145] | 7 | # by the Free Software Foundation, either version 3 of the License, or |
|---|
| [214] | 8 | # (at your option) any later version. |
|---|
| 9 | # |
|---|
| 10 | # This program is distributed in the hope that it will be useful, |
|---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| [1144] | 13 | # GNU Affero General Public License for more details. |
|---|
| [214] | 14 | # |
|---|
| [1144] | 15 | # You should have received a copy of the GNU Affero General Public License |
|---|
| [214] | 16 | # along with this program; if not, see http://www.gnu.org/licenses |
|---|
| 17 | # |
|---|
| [1359] | 18 | # $Id: Video.pym 1462 2009-01-11 05:26:23Z ArcRiley $ |
|---|
| [390] | 19 | |
|---|
| [214] | 20 | |
|---|
| [336] | 21 | cdef class Video (Texture) : |
|---|
| 22 | '''PySoy textures.Video Class |
|---|
| [214] | 23 | |
|---|
| [868] | 24 | Renders an Ogg Theora stream to a texture |
|---|
| [214] | 25 | ''' |
|---|
| [1129] | 26 | |
|---|
| [1166] | 27 | ############################################################################ |
|---|
| 28 | # |
|---|
| 29 | # C functions |
|---|
| 30 | # |
|---|
| 31 | |
|---|
| [1219] | 32 | cdef int _load(self, void* _data, int _size) nogil : |
|---|
| [1359] | 33 | cdef int _y |
|---|
| [1129] | 34 | cdef ogg.ogg_page* _page |
|---|
| [351] | 35 | cdef ogg.yuv_buffer _yuv |
|---|
| [340] | 36 | cdef ogg.ogg_packet _packet |
|---|
| [389] | 37 | cdef double _start # for benchmarking |
|---|
| [530] | 38 | if _data == NULL : |
|---|
| [392] | 39 | stdio.printf('done decoding.\n') |
|---|
| [351] | 40 | if self._stage > 0 : |
|---|
| 41 | ogg.ogg_stream_clear(&self._stream) |
|---|
| 42 | ogg.theora_info_clear(&self._info) |
|---|
| 43 | ogg.theora_comment_clear(&self._comment) |
|---|
| 44 | ogg.theora_clear(&self._decode) |
|---|
| 45 | return 0 |
|---|
| [1102] | 46 | _page = <ogg.ogg_page*> _data |
|---|
| [392] | 47 | # |
|---|
| 48 | # For Chaining/Looping |
|---|
| 49 | if ogg.ogg_page_bos(_page) : |
|---|
| 50 | if self._stage > 0 : |
|---|
| 51 | ogg.ogg_stream_clear(&self._stream) |
|---|
| 52 | ogg.theora_info_clear(&self._info) |
|---|
| 53 | ogg.theora_comment_clear(&self._comment) |
|---|
| 54 | ogg.theora_clear(&self._decode) |
|---|
| 55 | self._startTime = self._startTime + self._frameTime |
|---|
| 56 | self._frameTime = 0 |
|---|
| 57 | else : |
|---|
| [629] | 58 | self._startTime = _time() |
|---|
| [392] | 59 | self._stage = 0 |
|---|
| 60 | # |
|---|
| [382] | 61 | # Read Page 0 |
|---|
| [340] | 62 | if self._stage == 0 : |
|---|
| 63 | ogg.ogg_stream_init(&self._stream, ogg.ogg_page_serialno(_page)) |
|---|
| 64 | ogg.theora_info_init(&self._info) |
|---|
| 65 | ogg.theora_comment_init(&self._comment) |
|---|
| 66 | self._stage = 1 |
|---|
| [382] | 67 | # Read in the page if not -1 |
|---|
| [340] | 68 | if self._stage > 0 : |
|---|
| [382] | 69 | ogg.ogg_stream_pagein(&self._stream, _page) |
|---|
| 70 | # Read pre-data pages (if any) |
|---|
| 71 | if self._stage == 1 : |
|---|
| 72 | if ogg.ogg_page_granulepos(_page) == 0 : |
|---|
| 73 | while ogg.ogg_stream_packetout(&self._stream, &_packet) : |
|---|
| 74 | if ogg.theora_decode_header(&self._info, &self._comment,&_packet)<0 : |
|---|
| 75 | # Error in header, clear what we've setup and bail |
|---|
| 76 | ogg.ogg_stream_clear(&self._stream) |
|---|
| 77 | ogg.theora_info_clear(&self._info) |
|---|
| 78 | ogg.theora_comment_clear(&self._comment) |
|---|
| 79 | self._stage = -1 |
|---|
| 80 | return 0 |
|---|
| 81 | if ogg.ogg_page_pageno(_page) == 0 : |
|---|
| [1359] | 82 | # |
|---|
| 83 | # Resize for RGBX then set self._chans to RGB to ignore the X |
|---|
| 84 | self._resize(1, 4, self._squareup(self._info.frame_width), |
|---|
| [382] | 85 | self._squareup(self._info.frame_height), 1) |
|---|
| [1359] | 86 | self._chans = 3 |
|---|
| [383] | 87 | self._scaleX = <float> self._info.frame_width / <float> self._width |
|---|
| 88 | self._scaleY = <float> self._info.frame_height / <float> self._height |
|---|
| [382] | 89 | self._aspect = ( <float> self._info.frame_width / \ |
|---|
| 90 | <float> self._info.frame_height ) * \ |
|---|
| 91 | ( <float> self._info.aspect_numerator / \ |
|---|
| 92 | <float> self._info.aspect_denominator ) |
|---|
| 93 | else : |
|---|
| [340] | 94 | ogg.theora_decode_init(&self._decode, &self._info) |
|---|
| 95 | self._stage = 2 |
|---|
| 96 | if self._stage == 2 : |
|---|
| [351] | 97 | # Theora frames are 1:1 with Ogg packets, but packets != pages |
|---|
| 98 | while ogg.ogg_stream_packetout(&self._stream, &_packet) : |
|---|
| 99 | ogg.theora_decode_packetin(&self._decode, &_packet) |
|---|
| [386] | 100 | self._frameTime = ogg.theora_granule_time(&self._decode, |
|---|
| 101 | ogg.ogg_page_granulepos(_page)) |
|---|
| [351] | 102 | ogg.theora_decode_YUVout(&self._decode, &_yuv) |
|---|
| [384] | 103 | # |
|---|
| 104 | # This can and should be optimized further |
|---|
| [629] | 105 | #_start = _time() |
|---|
| [1359] | 106 | for _y from 0 <= _y < self._info.frame_height : |
|---|
| 107 | oil.oil_yuv2rgbx_sub2_u8( |
|---|
| 108 | self._texels + ((self._info.frame_height - _y) * self._width * 4), |
|---|
| 109 | _yuv.y + ( _y * _yuv.y_stride), |
|---|
| 110 | _yuv.u + ((_y / 2) * _yuv.uv_stride), |
|---|
| 111 | _yuv.v + ((_y / 2) * _yuv.uv_stride), |
|---|
| 112 | self._info.frame_width) |
|---|
| [382] | 113 | self._update = 1 |
|---|
| [340] | 114 | return 1 |
|---|
| [336] | 115 | |
|---|
| [386] | 116 | |
|---|
| [1219] | 117 | cdef int _save(self, void *_data, int _size) nogil : |
|---|
| [340] | 118 | cdef ogg.ogg_page *_page |
|---|
| [1102] | 119 | _page = <ogg.ogg_page*> _data |
|---|
| [340] | 120 | return 1 |
|---|
| [386] | 121 | |
|---|
| [387] | 122 | |
|---|
| [1219] | 123 | cdef int _ready(self) nogil : |
|---|
| [386] | 124 | cdef double _now |
|---|
| [629] | 125 | _now = _time() |
|---|
| [388] | 126 | if self._startTime + self._frameTime < _now : |
|---|
| [386] | 127 | return 1 |
|---|
| 128 | return 0 |
|---|
| [387] | 129 | |
|---|
| [1219] | 130 | |
|---|
| [1166] | 131 | ############################################################################ |
|---|
| 132 | # |
|---|
| 133 | # Properties |
|---|
| 134 | # |
|---|
| 135 | |
|---|
| [387] | 136 | property offset : |
|---|
| 137 | def __get__(self) : |
|---|
| 138 | return self._frameTime |
|---|