2008-07-30 22:07:47 +00:00
|
|
|
"""tstools.pyx -- Pyrex bindings for the TS tools
|
2008-08-25 18:22:01 +00:00
|
|
|
|
|
|
|
This is being developed on a Mac, running OS X. I expect that it will also
|
|
|
|
build on a Linux machine. I do not expect it to build (as it stands) on
|
|
|
|
Windows, as it is making assumptions that may not follow thereon.
|
|
|
|
|
|
|
|
It is my intent to worry about Windows after it works on the platforms that
|
|
|
|
I can test most easily!
|
2008-07-28 22:20:43 +00:00
|
|
|
"""
|
|
|
|
|
2008-07-31 10:09:59 +00:00
|
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
|
|
# Version: MPL 1.1
|
|
|
|
#
|
|
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
# the License. You may obtain a copy of the License at
|
|
|
|
# http://www.mozilla.org/MPL/
|
|
|
|
#
|
|
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
# for the specific language governing rights and limitations under the
|
|
|
|
# License.
|
|
|
|
#
|
|
|
|
# The Original Code is the MPEG TS, PS and ES tools.
|
|
|
|
#
|
|
|
|
# The Initial Developer of the Original Code is Amino Communications Ltd.
|
|
|
|
# Portions created by the Initial Developer are Copyright (C) 2008
|
|
|
|
# the Initial Developer. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s):
|
|
|
|
# Tibs (tibs@berlios.de)
|
|
|
|
#
|
|
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
# If we're going to use definitions like this in more than one pyx file, we'll
|
|
|
|
# need to define the shared types in a .pxd file and use cimport to import
|
|
|
|
# them.
|
2008-08-26 20:37:15 +00:00
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
cdef extern from "stdio.h":
|
|
|
|
ctypedef struct FILE:
|
|
|
|
int _fileno
|
2008-08-18 21:43:53 +00:00
|
|
|
cdef enum:
|
|
|
|
EOF = -1
|
|
|
|
cdef FILE *stdout
|
2008-08-22 22:58:15 +00:00
|
|
|
# Associate a stream (returned) with an existing file descriptor.
|
|
|
|
# The specified mode must be compatible with the existing mode of
|
|
|
|
# the file descriptor. Closing the stream will close the descriptor
|
|
|
|
# as well.
|
|
|
|
cdef FILE *fdopen(int fildes, char *mode)
|
|
|
|
|
2008-08-24 18:48:40 +00:00
|
|
|
cdef FILE *fopen(char *path, char *mode)
|
|
|
|
cdef int fclose(FILE *stream)
|
|
|
|
cdef int fileno(FILE *stream)
|
|
|
|
|
|
|
|
cdef extern from "errno.h":
|
|
|
|
cdef int errno
|
|
|
|
|
|
|
|
cdef extern from "string.h":
|
|
|
|
cdef char *strerror(int errnum)
|
|
|
|
|
2008-08-25 18:22:01 +00:00
|
|
|
# Copied from the Pyrex documentation...
|
|
|
|
cdef extern from "Python.h":
|
2008-08-26 20:37:15 +00:00
|
|
|
# Return a new string object with a copy of the string v as value and
|
|
|
|
# length len on success, and NULL on failure. If v is NULL, the contents of
|
|
|
|
# the string are uninitialized.
|
2008-08-25 18:22:01 +00:00
|
|
|
object PyString_FromStringAndSize(char *v, int len)
|
2008-08-26 20:37:15 +00:00
|
|
|
|
|
|
|
# Return a NUL-terminated representation of the contents of the object obj
|
|
|
|
# through the output variables buffer and length.
|
|
|
|
#
|
|
|
|
# The function accepts both string and Unicode objects as input. For
|
|
|
|
# Unicode objects it returns the default encoded version of the object. If
|
|
|
|
# length is NULL, the resulting buffer may not contain NUL characters; if
|
|
|
|
# it does, the function returns -1 and a TypeError is raised.
|
|
|
|
#
|
|
|
|
# The buffer refers to an internal string buffer of obj, not a copy. The
|
|
|
|
# data must not be modified in any way, unless the string was just created
|
|
|
|
# using PyString_FromStringAndSize(NULL, size). It must not be deallocated.
|
|
|
|
# If string is a Unicode object, this function computes the default
|
|
|
|
# encoding of string and operates on that. If string is not a string object
|
|
|
|
# at all, PyString_AsStringAndSize() returns -1 and raises TypeError.
|
2008-08-25 18:22:01 +00:00
|
|
|
int PyString_AsStringAndSize(object obj, char **buffer, Py_ssize_t* length) except -1
|
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
cdef FILE *convert_python_file(object file):
|
|
|
|
"""Given a Python file object, return an equivalent stream.
|
|
|
|
There are *so many things* dodgy about doing this...
|
|
|
|
"""
|
|
|
|
cdef int fileno
|
|
|
|
cdef char *mode
|
|
|
|
cdef FILE *stream
|
|
|
|
fileno = file.fileno()
|
|
|
|
mode = file.mode
|
|
|
|
stream = fdopen(fileno, mode)
|
|
|
|
if stream == NULL:
|
|
|
|
raise TSToolsException, 'Error converting Python file to C FILE *'
|
|
|
|
else:
|
|
|
|
return stream
|
2008-07-28 22:20:43 +00:00
|
|
|
|
2008-08-25 18:22:01 +00:00
|
|
|
cdef extern from "stdint.h":
|
|
|
|
ctypedef int uint8_t # !!! *some* sort of int..
|
|
|
|
|
2008-08-20 22:11:27 +00:00
|
|
|
cdef extern from "compat.h":
|
|
|
|
# We don't need to define 'offset_t' exactly, just to let Pyrex
|
|
|
|
# know it's vaguely int-like
|
|
|
|
ctypedef int offset_t
|
2008-08-25 18:22:01 +00:00
|
|
|
ctypedef uint8_t byte
|
2008-08-18 22:52:39 +00:00
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
cdef extern from 'es_defns.h':
|
|
|
|
# The reader for an ES file
|
|
|
|
struct elementary_stream:
|
|
|
|
pass
|
2008-08-18 22:52:39 +00:00
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
ctypedef elementary_stream ES
|
|
|
|
ctypedef elementary_stream *ES_p
|
|
|
|
|
2008-08-20 22:11:27 +00:00
|
|
|
# A location within said stream
|
|
|
|
struct _ES_offset:
|
|
|
|
offset_t infile # as used by lseek
|
|
|
|
int inpacket # in PES file, offset within PES packet
|
|
|
|
|
|
|
|
ctypedef _ES_offset ES_offset
|
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
# An actual ES unit
|
|
|
|
struct ES_unit:
|
2008-08-20 22:11:27 +00:00
|
|
|
ES_offset start_posn
|
|
|
|
byte *data
|
|
|
|
unsigned data_len
|
|
|
|
unsigned data_size
|
|
|
|
byte start_code
|
|
|
|
byte PES_had_PTS
|
2008-08-18 22:52:39 +00:00
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
ctypedef ES_unit *ES_unit_p
|
|
|
|
|
|
|
|
cdef extern from 'es_fns.h':
|
|
|
|
int open_elementary_stream(char *filename, ES_p *es)
|
2008-07-31 09:28:23 +00:00
|
|
|
void close_elementary_stream(ES_p *es)
|
2008-08-22 22:58:15 +00:00
|
|
|
|
|
|
|
int build_elementary_stream_file(int input, ES_p *es)
|
|
|
|
void free_elementary_stream(ES_p *es)
|
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
int find_and_build_next_ES_unit(ES_p es, ES_unit_p *unit)
|
|
|
|
void free_ES_unit(ES_unit_p *unit)
|
|
|
|
void report_ES_unit(FILE *stream, ES_unit_p unit)
|
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
# We perhaps need a Python object to represent an ES_offset?
|
|
|
|
# Otherwise, it's going to be hard to use them within Python itself
|
2008-08-20 22:11:27 +00:00
|
|
|
int seek_ES(ES_p es, ES_offset where)
|
|
|
|
int compare_ES_offsets(ES_offset offset1, ES_offset offset2)
|
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
# I'd like to be able to *write* ES files, so...
|
|
|
|
# Python file objects can return a file descriptor (i.e., integer)
|
|
|
|
# via their fileno() method, so the simplest thing to do may be to
|
|
|
|
# add a new C function that uses write() instead of fwrite(). Or I
|
|
|
|
# could use fdopen to turn the fileno() into a FILE *...
|
2008-08-26 20:37:15 +00:00
|
|
|
int build_ES_unit_from_data(ES_unit_p *unit, byte *data, unsigned data_len)
|
2008-08-22 22:58:15 +00:00
|
|
|
int write_ES_unit(FILE *output, ES_unit_p unit)
|
|
|
|
|
2008-08-20 22:11:27 +00:00
|
|
|
|
2008-07-28 22:20:43 +00:00
|
|
|
# Is this the best thing to do?
|
|
|
|
class TSToolsException(Exception):
|
|
|
|
pass
|
2008-08-18 21:43:53 +00:00
|
|
|
|
2008-08-23 21:15:29 +00:00
|
|
|
cdef same_ES_unit(ES_unit_p this, ES_unit_p that):
|
|
|
|
"""Two ES units do not need to be at the same place to be the same.
|
|
|
|
"""
|
|
|
|
if this.data_len != that.data_len:
|
|
|
|
return False
|
|
|
|
for 0 <= ii < this.data_len:
|
|
|
|
if this.data[ii] != that.data[ii]:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2008-08-26 21:44:05 +00:00
|
|
|
cdef class ESOffset:
|
|
|
|
"""An offset within an ES file.
|
|
|
|
|
|
|
|
If the ES unit was read directly from a raw ES file, then a simple file
|
|
|
|
offset is sufficient.
|
|
|
|
|
|
|
|
However, if we're reading from a PS or TS file (via the PES reading layer),
|
|
|
|
then we have the offset of the PES packet, and then the offset of the ES
|
|
|
|
unit therein.
|
|
|
|
|
|
|
|
We *could* just use a tuple for this, but it's nice to have a bit more
|
|
|
|
documentation self-evident.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Keep the original names, even though they're not very Pythonic
|
|
|
|
cdef readonly long long infile # Hoping this is 64 bit...
|
|
|
|
cdef readonly int inpacket
|
|
|
|
|
|
|
|
def __cinit__(self, infile, inpacket):
|
|
|
|
self.infile = infile
|
|
|
|
self.inpacket = inpacket
|
|
|
|
|
|
|
|
def __init__(self, infile, inpacket):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '%08d/%08d'%(self.inpacket,self.infile)
|
|
|
|
|
|
|
|
def report(self):
|
|
|
|
print 'Offset %d in packet at offset %d in file'%(self.inpacket,self.infile)
|
|
|
|
|
|
|
|
def __cmp__(self,other):
|
|
|
|
if self.infile > other.infile:
|
|
|
|
return 1
|
|
|
|
elif self.infile < other.infile:
|
|
|
|
return -1
|
|
|
|
elif self.inpacket > other.inpacket:
|
|
|
|
return 1
|
|
|
|
elif self.inpacket < other.inpacket:
|
|
|
|
return -1
|
|
|
|
else:
|
|
|
|
return 0
|
|
|
|
|
2008-08-23 21:15:29 +00:00
|
|
|
cdef class ESUnit # Forward declaration
|
|
|
|
cdef object compare_ESUnits(ESUnit this, ESUnit that, int op):
|
|
|
|
"""op is 2 for ==, 3 for !=, other values not allowed.
|
|
|
|
"""
|
|
|
|
if op == 2: # ==
|
|
|
|
return same_ES_unit(this.unit, that.unit)
|
|
|
|
elif op == 3: # !=
|
|
|
|
return not same_ES_unit(this.unit, that.unit)
|
|
|
|
else:
|
|
|
|
#return NotImplemented
|
|
|
|
raise TypeError, 'ESUnit only supports == and != comparisons'
|
2008-08-18 21:43:53 +00:00
|
|
|
|
|
|
|
cdef class ESUnit:
|
|
|
|
"""A Python class representing an ES unit.
|
|
|
|
"""
|
|
|
|
|
|
|
|
cdef ES_unit_p unit
|
|
|
|
|
|
|
|
# It appears to be recommended to make __cinit__ expand to take more
|
|
|
|
# arguments (if __init__ ever gains them), since both get the same
|
|
|
|
# things passed to them. Hmm, normally I'd trust myself, but let's
|
|
|
|
# try the recommended route
|
2008-08-26 21:44:05 +00:00
|
|
|
def __cinit__(self, data=None, *args,**kwargs):
|
2008-08-26 20:37:15 +00:00
|
|
|
cdef char *buffer
|
|
|
|
cdef Py_ssize_t length
|
|
|
|
if data:
|
|
|
|
PyString_AsStringAndSize(data, &buffer, &length)
|
|
|
|
retval = build_ES_unit_from_data(&self.unit, <byte *>buffer, length);
|
|
|
|
if retval < 0:
|
|
|
|
raise TSToolsException,'Error building ES unit from Python string'
|
|
|
|
|
|
|
|
def __init__(self,data=None):
|
2008-08-18 21:43:53 +00:00
|
|
|
pass
|
|
|
|
|
2008-08-20 22:11:27 +00:00
|
|
|
def report(self):
|
|
|
|
"""Report (briefly) on an ES unit. This write to C stdout, which means
|
|
|
|
that Python has no control over the output. A proper Python version of
|
|
|
|
this will be provided eventually.
|
|
|
|
"""
|
2008-08-18 21:43:53 +00:00
|
|
|
report_ES_unit(stdout, self.unit)
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
|
|
|
free_ES_unit(&self.unit)
|
|
|
|
|
|
|
|
def __repr__(self):
|
2008-08-26 20:37:15 +00:00
|
|
|
text = 'ES unit: start code %02x, len %4d:'%(self.unit.start_code,
|
|
|
|
self.unit.data_len)
|
|
|
|
for 0 <= ii < min(self.unit.data_len,8):
|
|
|
|
text += ' %02x'%self.unit.data[ii]
|
|
|
|
return text
|
2008-08-18 21:43:53 +00:00
|
|
|
|
|
|
|
cdef __set_es_unit(self, ES_unit_p unit):
|
|
|
|
if self.unit == NULL:
|
|
|
|
raise ValueError,'ES unit already defined'
|
|
|
|
else:
|
|
|
|
self.unit = unit
|
|
|
|
|
2008-08-23 21:15:29 +00:00
|
|
|
def __richcmp__(self,other,op):
|
|
|
|
return compare_ESUnits(self,other,op)
|
|
|
|
|
2008-08-25 18:22:01 +00:00
|
|
|
def __getattr__(self,name):
|
|
|
|
if name == 'start_posn':
|
2008-08-26 21:44:05 +00:00
|
|
|
return ESOffset(self.unit.start_posn.infile,
|
|
|
|
self.unit.start_posn.inpacket)
|
2008-08-25 18:22:01 +00:00
|
|
|
elif name == 'data':
|
|
|
|
# Cast the first parameter so that the C compiler is happy
|
|
|
|
# when compiling the (derived) tstools.c
|
|
|
|
return PyString_FromStringAndSize(<char *>self.unit.data, self.unit.data_len)
|
|
|
|
elif name == 'start_code':
|
|
|
|
return self.unit.start_code
|
|
|
|
elif name == 'PES_had_PTS':
|
|
|
|
return self.unit.PES_had_PTS
|
|
|
|
else:
|
|
|
|
raise AttributeError
|
|
|
|
|
2008-08-18 21:43:53 +00:00
|
|
|
# Is this the simplest way? Since it appears that a class method
|
|
|
|
# doesn't want to take a non-Python item as an argument...
|
|
|
|
cdef _next_ESUnit(ES_p stream, filename):
|
|
|
|
cdef ES_unit_p unit
|
2008-08-22 22:58:15 +00:00
|
|
|
# The C function assumes it has a valid ES stream passed to it
|
|
|
|
# = I don't think we're always called with such
|
|
|
|
if stream == NULL:
|
|
|
|
raise TSToolsException,'No ES stream to read'
|
|
|
|
|
2008-08-18 21:43:53 +00:00
|
|
|
retval = find_and_build_next_ES_unit(stream, &unit)
|
|
|
|
if retval == EOF:
|
|
|
|
raise StopIteration
|
|
|
|
elif retval != 0:
|
|
|
|
raise TSToolsException,'Error getting next ES unit from file %s'%filename
|
|
|
|
|
2008-08-26 21:44:05 +00:00
|
|
|
# I'd like to be able to do:
|
|
|
|
# return ESUnit(unit=unit)
|
|
|
|
# but it's not possible to pass anything other than a Python object
|
|
|
|
# to methods, and an ES_unit_p is not (which is the point of what we're
|
|
|
|
# doing!). I could take the innards of the ES unit, and pass them as
|
|
|
|
# individual arguments, appropriately mangled, but that seems a bit like
|
|
|
|
# overkill when the (rather inelegant but at least hidden in this factory
|
|
|
|
# method) approach below actually works.
|
|
|
|
# Maybe I'll figure out something better as I learn more about Pyrex.
|
|
|
|
|
2008-08-20 22:11:27 +00:00
|
|
|
# From http://www.philhassey.com/blog/2007/12/05/pyrex-from-confusion-to-enlightenment/
|
|
|
|
# Pyrex doesn't do type inference, so it doesn't detect that 'u' is allowed
|
|
|
|
# to hold an ES_unit_p. It's up to us to *tell* it, specifically, what type
|
|
|
|
# 'u' is going to be.
|
2008-08-18 21:43:53 +00:00
|
|
|
cdef ESUnit u
|
|
|
|
u = ESUnit()
|
|
|
|
u.unit = unit
|
|
|
|
return u
|
|
|
|
|
2008-08-24 18:48:40 +00:00
|
|
|
cdef class ESFile:
|
2008-08-22 22:58:15 +00:00
|
|
|
"""A Python class representing an ES stream.
|
|
|
|
|
|
|
|
Initially, always a file (so maybe this should be ESFile?)
|
|
|
|
|
|
|
|
We support opening for read, or opening (creating) a new file
|
2008-08-24 18:48:40 +00:00
|
|
|
for write. For the moment, we don't support appending, and
|
|
|
|
support for trying to read and write the same file is undefined.
|
2008-08-22 22:58:15 +00:00
|
|
|
|
2008-08-24 18:48:40 +00:00
|
|
|
So, create a new ESFile as either:
|
2008-08-22 22:58:15 +00:00
|
|
|
|
2008-08-24 18:48:40 +00:00
|
|
|
* ESFile(filename,'r') or
|
|
|
|
* ESFile(filename,'w')
|
2008-08-22 22:58:15 +00:00
|
|
|
|
|
|
|
Note that there is always an implicit 'b' attached to the mode (i.e., the
|
|
|
|
file is accessed in binary mode).
|
2008-07-28 22:20:43 +00:00
|
|
|
"""
|
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
cdef FILE *file_stream # The corresponding C file stream
|
2008-08-24 18:48:40 +00:00
|
|
|
cdef int fileno # and file number
|
2008-08-22 22:58:15 +00:00
|
|
|
cdef ES_p stream # For reading an existing ES stream
|
2008-08-24 18:48:40 +00:00
|
|
|
cdef readonly object name
|
2008-08-22 22:58:15 +00:00
|
|
|
cdef readonly object mode
|
2008-07-28 22:20:43 +00:00
|
|
|
|
|
|
|
# It appears to be recommended to make __cinit__ expand to take more
|
|
|
|
# arguments (if __init__ ever gains them), since both get the same
|
|
|
|
# things passed to them. Hmm, normally I'd trust myself, but let's
|
|
|
|
# try the recommended route
|
2008-08-22 22:58:15 +00:00
|
|
|
def __cinit__(self,filename,mode='r',*args,**kwargs):
|
2008-08-24 18:48:40 +00:00
|
|
|
actual_mode = mode+'b'
|
|
|
|
self.file_stream = fopen(filename,mode)
|
|
|
|
if self.file_stream == NULL:
|
|
|
|
raise TSToolsException,"Error opening file '%s'"\
|
|
|
|
" with (actual) mode '%s': %s"%(filename,mode,strerror(errno))
|
|
|
|
self.fileno = fileno(self.file_stream)
|
2008-08-22 22:58:15 +00:00
|
|
|
if mode == 'r':
|
2008-08-24 18:48:40 +00:00
|
|
|
retval = build_elementary_stream_file(self.fileno,&self.stream)
|
2008-08-22 22:58:15 +00:00
|
|
|
if retval != 0:
|
2008-08-24 18:48:40 +00:00
|
|
|
raise TSToolsException,'Error attaching elementary stream to file %s'%filename
|
2008-08-22 22:58:15 +00:00
|
|
|
|
|
|
|
def __init__(self,filename,mode='r'):
|
|
|
|
# What should go in __init__ and what in __cinit__ ???
|
2008-08-24 18:48:40 +00:00
|
|
|
self.name = filename
|
2008-08-22 22:58:15 +00:00
|
|
|
self.mode = mode
|
2008-07-28 22:20:43 +00:00
|
|
|
|
|
|
|
def __dealloc__(self):
|
2008-08-24 18:48:40 +00:00
|
|
|
if self.file_stream != NULL:
|
|
|
|
retval = fclose(self.file_stream)
|
|
|
|
if retval != 0:
|
|
|
|
raise TSToolsException,"Error closing file '%s':"\
|
|
|
|
" %s"%(filename,strerror(errno))
|
2008-08-22 22:58:15 +00:00
|
|
|
if self.stream != NULL:
|
|
|
|
free_elementary_stream(&self.stream)
|
2008-08-18 21:43:53 +00:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return self
|
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
def is_readable(self):
|
|
|
|
"""This is a convenience method, whilst reading and writing are exclusive.
|
|
|
|
"""
|
|
|
|
return self.mode == 'r' and self.stream != NULL
|
|
|
|
|
|
|
|
def is_writable(self):
|
|
|
|
"""This is a convenience method, whilst reading and writing are exclusive.
|
|
|
|
"""
|
|
|
|
return self.mode == 'w' and self.file_stream != NULL
|
|
|
|
|
2008-08-18 21:43:53 +00:00
|
|
|
# For Pyrex classes, we define a __next__ instead of a next method
|
|
|
|
# in order to form our iterator
|
|
|
|
def __next__(self):
|
|
|
|
"""Our iterator interface retrieves the ES units from the stream.
|
|
|
|
"""
|
2008-08-24 18:48:40 +00:00
|
|
|
return _next_ESUnit(self.stream,self.name)
|
2008-08-22 22:58:15 +00:00
|
|
|
|
2008-08-24 18:48:40 +00:00
|
|
|
def read(self):
|
|
|
|
"""Read the next ES unit from this stream.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return _next_ESUnit(self.stream,self.name)
|
|
|
|
except StopIteration:
|
|
|
|
raise EOFError
|
|
|
|
|
|
|
|
def write(self, ESUnit unit):
|
|
|
|
"""Write an ES unit to this stream.
|
|
|
|
"""
|
|
|
|
if self.file_stream == NULL:
|
|
|
|
raise TSToolsException,'ESFile does not seem to have been opened for write'
|
|
|
|
|
|
|
|
retval = write_ES_unit(self.file_stream,unit.unit)
|
|
|
|
if retval != 0:
|
|
|
|
raise TSToolsException,'Error writing ES unit to file %s'%self.name
|
2008-08-23 21:15:29 +00:00
|
|
|
|
2008-08-22 22:58:15 +00:00
|
|
|
def close(self):
|
|
|
|
# Apparently we can't call the __dealloc__ method itself,
|
|
|
|
# but I think this is sensible to do here...
|
2008-08-24 18:48:40 +00:00
|
|
|
if self.file_stream != NULL:
|
|
|
|
retval = fclose(self.file_stream)
|
|
|
|
if retval != 0:
|
|
|
|
raise TSToolsException,"Error closing file '%s':"\
|
|
|
|
" %s"%(filename,strerror(errno))
|
2008-08-22 22:58:15 +00:00
|
|
|
if self.stream != NULL:
|
|
|
|
free_elementary_stream(&self.stream)
|
2008-08-24 18:48:40 +00:00
|
|
|
# And obviously we're not available any more
|
|
|
|
self.file_stream = NULL
|
|
|
|
self.fileno = -1
|
|
|
|
self.name = None
|
|
|
|
self.mode = None
|