Skip to content

Adding Video Option for MMS #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 63 additions & 6 deletions messaging/mms/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,29 @@ def smil(self):
audio_node.setAttribute('begin', str(begin))
audio_node.setAttribute('end', str(end))

par_node.appendChild(text_node)
par_node.appendChild(audio_node)

if page.video is not None:
part, begin, end = page.video
if 'Content-Location' in part.headers:
src = part.headers['Content-Location']
elif 'Content-ID' in part.headers:
src = part.headers['Content-ID']
else:
src = part.data

video_node = smil_doc.createElement('video')
video_node.setAttribute('src', src)
video_node.setAttribute('region', 'Image')
if begin > 0 or end > 0:
if end > page.duration:
end = page.duration

video_node.setAttribute('begin', str(begin))
video_node.setAttribute('end', str(end))

par_node.appendChild(video_node)

body_node.appendChild(par_node)

smil_doc.documentElement.appendChild(body_node)
Expand Down Expand Up @@ -282,11 +302,12 @@ def __init__(self):
self.image = None
self.audio = None
self.text = None
self.video = None

@property
def data_parts(self):
"""Returns a list of the data parst in this slide"""
return [part for part in (self.image, self.audio, self.text)
return [part for part in (self.image, self.audio, self.text, self.video)
if part is not None]

def number_of_parts(self):
Expand All @@ -296,7 +317,7 @@ def number_of_parts(self):
@rtype: int
"""
num_parts = 0
for item in (self.image, self.audio, self.text):
for item in (self.image, self.audio, self.text, self.video):
if item is not None:
num_parts += 1

Expand Down Expand Up @@ -376,6 +397,42 @@ def add_audio(self, filename, time_begin=0, time_end=0):

self.audio = (DataPart(filename), time_begin, time_end)

def add_video(self, filename, time_begin=0, time_end=0):
"""
Adds an video clip to this slide.

:param filename: The name of the video file to add. Currently the only
supported format is AMR.
:type filename: str
:param time_begin: The time (in milliseconds) during the duration of
this slide to begin playback of the video clip. If
this is 0 or less, the video clip will be played the
moment the slide is opened.
:type time_begin: int
:param time_end: The time (in milliseconds) during the duration of this
slide at which to stop playing (i.e. mute) the video
clip. If this is 0 or less, or if it is greater than
the actual duration of this slide, the entire video
clip will be played, or until the next slide is
accessed.
:type time_end: int
:raise TypeError: An inappropriate variable type was passed in of the
parameters
"""
if not isinstance(filename, str):
raise TypeError("filename must be a string")

if not isinstance(time_begin, int) or not isinstance(time_end, int):
raise TypeError("time_begin and time_end must be ints")

if not os.path.isfile(filename):
raise OSError("filename must be a file")

if time_end > 0 and time_end < time_begin:
raise ValueError('time_end cannot be lower than time_begin')

self.video = (DataPart(filename), time_begin, time_end)

def add_text(self, text, time_begin=0, time_end=0):
"""
Adds a block of text to this slide.
Expand Down Expand Up @@ -430,7 +487,7 @@ def set_duration(self, duration):
self.duration = duration


class DataPart(object):
class DataPart:
"""
I am a data entry in the MMS body

Expand Down Expand Up @@ -544,11 +601,11 @@ def data(self):
"""A buffer containing the binary data of this part"""
if self._data is not None:
if type(self._data) == array.array:
self._data = self._data.tostring()
self._data = self._data.tobytes()
return self._data

elif self._filename is not None:
with open(self._filename, 'r') as f:
with open(self._filename, 'rb') as f:
self._data = f.read()
return self._data

Expand Down
79 changes: 42 additions & 37 deletions messaging/mms/mms_pdu.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
import array
import os
import random
import logging

from messaging.utils import debug
from messaging.mms import message, wsp_pdu
from messaging.mms.iterator import PreviewIterator

Expand All @@ -25,7 +25,7 @@ def flatten_list(x):
"""Flattens ``x`` into a single list"""
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
if hasattr(el, "__iter__") and not isinstance(el, str):
result.extend(flatten_list(el))
else:
result.append(el)
Expand Down Expand Up @@ -167,23 +167,23 @@ def decode_message_body(self, data_iter):
except StopIteration:
return

#print 'Number of data entries (parts) in MMS body:', num_entries
logging.debug('Number of data entries (parts) in MMS body: %i' % num_entries)

########## MMS body: entries ##########
# For every data "part", we have to read the following sequence:
# <length of content-type + other possible headers>,
# <length of data>,
# <content-type + other possible headers>,
# <data>
for part_num in xrange(num_entries):
#print '\nPart %d:\n------' % part_num
for part_num in range(num_entries):
logging.debug('\nPart %d:\n------' % part_num)
headers_len = self.decode_uint_var(data_iter)
data_len = self.decode_uint_var(data_iter)

# Prepare to read content-type + other possible headers
ct_field_bytes = []
for i in xrange(headers_len):
ct_field_bytes.append(data_iter.next())
for i in range(headers_len):
ct_field_bytes.append(next(data_iter))

ct_iter = PreviewIterator(ct_field_bytes)
# Get content type
Expand All @@ -201,8 +201,8 @@ def decode_message_body(self, data_iter):

# Data (note: this is not null-terminated)
data = array.array('B')
for i in xrange(data_len):
data.append(data_iter.next())
for i in range(data_len):
data.append(next(data_iter))

part = message.DataPart()
part.set_data(data, ctype)
Expand Down Expand Up @@ -269,7 +269,7 @@ def decode_mms_header(byte_iter):
byte = wsp_pdu.Decoder.decode_short_integer_from_byte(preview)

if byte in mms_field_names:
byte_iter.next()
next(byte_iter)
mms_field_name = mms_field_names[byte][0]
else:
byte_iter.reset_preview()
Expand All @@ -281,11 +281,11 @@ def decode_mms_header(byte_iter):
try:
name = mms_field_names[byte][1]
mms_value = getattr(MMSDecoder, 'decode_%s' % name)(byte_iter)
except wsp_pdu.DecodeError, msg:
except wsp_pdu.DecodeError as e:
raise wsp_pdu.DecodeError('Invalid MMS Header: Could '
'not decode MMS-value: %s' % msg)
'not decode MMS-value: %s' % e)
except:
raise RuntimeError('A fatal error occurred, probably due to an '
print('A fatal error occurred, probably due to an '
'unimplemented decoding operation. Tried to '
'decode header: %s' % mms_field_name)

Expand Down Expand Up @@ -316,11 +316,11 @@ def decode_encoded_string_value(byte_iter):
# TODO: add proper support for charsets...
try:
charset = wsp_pdu.Decoder.decode_well_known_charset(byte_iter)
except wsp_pdu.DecodeError, msg:
except wsp_pdu.DecodeError as e:
raise Exception('encoded_string_value decoding error - '
'Could not decode Charset value: %s' % msg)
'Could not decode Charset value: %s' % e)

return wsp_pdu.Decoder.decode_text_string(byte_iter)
return wsp_pdu.Decoder.decode_text_string(byte_iter, charset)
except wsp_pdu.DecodeError:
# Fall back on just "Text-string"
return wsp_pdu.Decoder.decode_text_string(byte_iter)
Expand Down Expand Up @@ -350,13 +350,13 @@ def decode_boolean_value(byte_iter):
byte_iter.reset_preview()
raise wsp_pdu.DecodeError('Error parsing boolean value '
'for byte: %s' % hex(byte))
byte = byte_iter.next()
byte = next(byte_iter)
return byte == 128

@staticmethod
def decode_delivery_time_value(byte_iter):
value_length = wsp_pdu.Decoder.decode_value_length(byte_iter)
token = byte_iter.next()
token = next(byte_iter)
value = wsp_pdu.Decoder.decode_long_integer(byte_iter)
if token == 128:
token_type = 'absolute'
Expand All @@ -383,7 +383,7 @@ def decode_from_value(byte_iter):
"""
value_length = wsp_pdu.Decoder.decode_value_length(byte_iter)
# See what token we have
byte = byte_iter.next()
byte = next(byte_iter)
if byte == 129: # Insert-address-token
return '<not inserted>'

Expand Down Expand Up @@ -416,7 +416,7 @@ def decode_message_class_value(byte_iter):
}
byte = byte_iter.preview()
if byte in class_identifiers:
byte_iter.next()
next(byte_iter)
return class_identifiers[byte]

byte_iter.reset_preview()
Expand Down Expand Up @@ -444,7 +444,7 @@ def decode_message_type_value(byte_iter):

byte = byte_iter.preview()
if byte in message_types:
byte_iter.next()
next(byte_iter)
return message_types[byte]

byte_iter.reset_preview()
Expand All @@ -467,7 +467,7 @@ def decode_priority_value(byte_iter):

byte = byte_iter.preview()
if byte in priorities:
byte = byte_iter.next()
byte = next(byte_iter)
return priorities[byte]

byte_iter.reset_preview()
Expand Down Expand Up @@ -498,7 +498,7 @@ def decode_sender_visibility_value(byte_iter):
raise wsp_pdu.DecodeError('Error parsing sender visibility '
'value for byte: %s' % hex(byte))

byte = byte_iter.next()
byte = next(byte_iter)
value = 'Hide' if byte == 128 else 'Show'
return value

Expand Down Expand Up @@ -529,7 +529,7 @@ def decode_response_status_value(byte_iter):
0x88: 'Error-unsupported-message',
}
byte = byte_iter.preview()
byte_iter.next()
next(byte_iter)
# Return error unspecified if it couldn't be decoded
return response_status_values.get(byte, 0x81)

Expand All @@ -555,7 +555,7 @@ def decode_status_value(byte_iter):
0x84: 'Unrecognised',
}

byte = byte_iter.next()
byte = next(byte_iter)
# Return an unrecognised state if it couldn't be decoded
return status_values.get(byte, 0x84)

Expand All @@ -576,7 +576,7 @@ def decode_expiry_value(byte_iter):
:rtype: str or int
"""
value_length = MMSDecoder.decode_value_length(byte_iter)
token = byte_iter.next()
token = next(byte_iter)

if token == 0x80: # Absolute-token
return MMSDecoder.decode_date_value(byte_iter)
Expand Down Expand Up @@ -695,10 +695,11 @@ def encode_message_header(self):
MMSEncoder.encode_header(hdr, headers_to_encode[hdr]))

# Ok, now only "Content-type" should be left
content_type, ct_parameters = headers_to_encode['Content-Type']
message_header.extend(MMSEncoder.encode_mms_field_name('Content-Type'))
ret = MMSEncoder.encode_content_type_value(content_type, ct_parameters)
message_header.extend(flatten_list(ret))
if 'Content-Type' in headers_to_encode:
content_type, ct_parameters = headers_to_encode['Content-Type']
message_header.extend(MMSEncoder.encode_mms_field_name('Content-Type'))
ret = MMSEncoder.encode_content_type_value(content_type, ct_parameters)
message_header.extend(flatten_list(ret))

return message_header

Expand Down Expand Up @@ -749,12 +750,13 @@ def encode_message_body(self):
#TODO: enable encoding of MMSs without SMIL file
########## MMS body: header ##########
# Parts: SMIL file + <number of data elements in each slide>
if len(self._mms_message._pages) == 0:
return message_body
num_entries = 1
for page in self._mms_message._pages:
num_entries += page.number_of_parts()

for data_part in self._mms_message._data_parts:
num_entries += 1
num_entries += len(self._mms_message._data_parts)

message_body.extend(self.encode_uint_var(num_entries))

Expand All @@ -773,7 +775,7 @@ def encode_message_body(self):
smil_part.headers['Content-ID'] = '<0000>'
parts = [smil_part]
for slide in self._mms_message._pages:
for part_tuple in (slide.image, slide.audio, slide.text):
for part_tuple in (slide.image, slide.audio, slide.text, slide.video):
if part_tuple is not None:
parts.append(part_tuple[0])

Expand All @@ -800,7 +802,10 @@ def encode_message_body(self):
message_body.extend(encoded_part_headers)
# Data (note: we do not null-terminate this)
for char in part.data:
message_body.append(ord(char))
if isinstance(char, int):
message_body.append(char)
else:
message_body.append(ord(char))

return message_body

Expand Down Expand Up @@ -844,11 +849,11 @@ def encode_header(header_field_name, header_value):
ret = getattr(MMSEncoder,
'encode_%s' % expected_type)(header_value)
encoded_header.extend(ret)
except wsp_pdu.EncodeError, msg:
except wsp_pdu.EncodeError as e:
raise wsp_pdu.EncodeError('Error encoding parameter '
'value: %s' % msg)
'value: %s' % e)
except:
debug('A fatal error occurred, probably due to an '
logging.error('A fatal error occurred, probably due to an '
'unimplemented encoding operation')
raise

Expand Down
Loading