Skip to content

Commit

Permalink
Merged 'develop'.
Browse files Browse the repository at this point in the history
  • Loading branch information
dominic-miglar committed Dec 12, 2018
2 parents 4afffb3 + a4f38fe commit 86c2804
Show file tree
Hide file tree
Showing 24 changed files with 1,239 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -12,7 +12,7 @@ Installation
To install the bitmovin client with pip, run the following command:

```bash
pip install git+https://github.com/bitmovin/bitmovin-python.git@v1.45.0
pip install git+https://github.com/bitmovin/bitmovin-python.git@v1.48.0
```

Depending on the platform which you are using your default python version may be python2.7.
Expand Down
2 changes: 1 addition & 1 deletion bitmovin/package_information.py
@@ -1,2 +1,2 @@
NAME = 'bitmovin-python'
VERSION = '1.46.0.dev0'
VERSION = '1.49.0.dev0'
2 changes: 1 addition & 1 deletion bitmovin/resources/models/encodings/__init__.py
Expand Up @@ -14,7 +14,7 @@
from .stream import Stream
from .stream_input import StreamInput
from .encoding_live_details import EncodingLiveDetails
from .live import LiveHlsManifest, LiveDashManifest, LiveStreamConfiguration
from .live import LiveHlsManifest, LiveDashManifest, LiveStreamConfiguration, AutoRestartConfiguration
from .thumbnail import Thumbnail
from .sprite import Sprite
from .stream_filter import StreamFilter
Expand Down
1 change: 1 addition & 0 deletions bitmovin/resources/models/encodings/live/__init__.py
@@ -1,3 +1,4 @@
from .live_dash_manifest import LiveDashManifest
from .live_hls_manifest import LiveHlsManifest
from .live_stream_configuration import LiveStreamConfiguration
from .auto_restart_configuration import AutoRestartConfiguration
@@ -0,0 +1,14 @@
from bitmovin.utils import Serializable


class AutoRestartConfiguration(Serializable):
def __init__(self, segments_written_timeout: float = None, bytes_written_timeout: float = None,
frames_written_timeout: float = None, hls_manifests_update_timeout: float = None,
dash_manifests_update_timeout: float = None, schedule_expression: str = None):
super().__init__()
self.segmentsWrittenTimeout = segments_written_timeout
self.bytesWrittenTimeout = bytes_written_timeout
self.framesWrittenTimeout = frames_written_timeout
self.hlsManifestsUpdateTimeout = hls_manifests_update_timeout
self.dashManifestsUpdateTimeout = dash_manifests_update_timeout
self.scheduleExpression = schedule_expression
Expand Up @@ -4,20 +4,23 @@
from bitmovin.errors import InvalidTypeError
from .live_dash_manifest import LiveDashManifest
from .live_hls_manifest import LiveHlsManifest
from .auto_restart_configuration import AutoRestartConfiguration


class LiveStreamConfiguration(Serializable):
def __init__(self,
stream_key: str,
live_dash_manifests: List[LiveDashManifest] = None,
live_hls_manifests: List[LiveHlsManifest] = None,
live_encoding_mode=None):
live_encoding_mode=None,
auto_restart_configuration: AutoRestartConfiguration=None):
super().__init__()
self.streamKey = stream_key
self.dashManifests = live_dash_manifests
self.hlsManifests = live_hls_manifests
self._live_encoding_mode = None
self.liveEncodingMode = live_encoding_mode
self.autoRestartConfiguration = auto_restart_configuration

@property
def liveEncodingMode(self):
Expand Down
Expand Up @@ -3,7 +3,11 @@

class H265PerTitleConfiguration(PerTitleConfiguration):
def __init__(self, min_bitrate=None, max_bitrate=None, min_bitrate_step_size=None,
max_bitrate_step_size=None, target_quality_crf=None, auto_representations=None):
max_bitrate_step_size=None, target_quality_crf=None, auto_representations=None,
codec_min_bitrate_factor=None, codec_max_bitrate_factor=None, codec_bufsize_factor=None):
super().__init__(min_bitrate=min_bitrate, max_bitrate=max_bitrate, min_bitrate_step_size=min_bitrate_step_size,
max_bitrate_step_size=max_bitrate_step_size, auto_representations=auto_representations)
self.targetQualityCrf = target_quality_crf
self.codecMinBitrateFactor = codec_min_bitrate_factor
self.codecMaxBitrateFactor = codec_max_bitrate_factor
self.codecBufsizeFactor = codec_bufsize_factor
1 change: 1 addition & 0 deletions bitmovin/resources/models/manifests/dash/__init__.py
Expand Up @@ -13,3 +13,4 @@
from .dash_mp4_representation import DashMP4Representation
from .dash_name_space import DASHNamespace
from .custom_xml_element import CustomXMLElement
from .vtt_representation import VttRepresentation
Expand Up @@ -7,7 +7,7 @@
class AbstractFMP4Representation(AbstractModel, Serializable):

def __init__(self, type, encoding_id, muxing_id, segment_path, start_segment_number=None,
end_segment_number=None, id_=None, custom_data=None):
end_segment_number=None, id_=None, custom_data=None, start_keyframe_id=None, end_keyframe_id=None):
super().__init__(id_=id_, custom_data=custom_data)
self._type = None
self.type = type
Expand All @@ -16,6 +16,8 @@ def __init__(self, type, encoding_id, muxing_id, segment_path, start_segment_num
self.segmentPath = segment_path
self.startSegmentNumber = start_segment_number
self.endSegmentNumber = end_segment_number
self.startKeyframeId = start_keyframe_id
self.endKeyframeId = end_keyframe_id

@property
def type(self):
Expand Down Expand Up @@ -46,10 +48,12 @@ def parse_from_json_object(cls, json_object):
segment_path = json_object['segmentPath']
start_segment_number = json_object.get('startSegmentNumber')
end_segment_number = json_object.get('endSegmentNumber')
start_keyframe_id = json_object.get('startKeyframeId')
end_keyframe_id = json_object.get('endKeyframeId')
abstract_fmp4_representation = AbstractFMP4Representation(
id_=id_, custom_data=custom_data, type=type, encoding_id=encoding_id, muxing_id=muxing_id,
segment_path=segment_path, start_segment_number=start_segment_number,
end_segment_number=end_segment_number)
end_segment_number=end_segment_number, start_keyframe_id=start_keyframe_id, end_keyframe_id=end_keyframe_id)
return abstract_fmp4_representation

def serialize(self):
Expand Down
11 changes: 8 additions & 3 deletions bitmovin/resources/models/manifests/dash/fmp4_representation.py
Expand Up @@ -4,10 +4,11 @@
class FMP4Representation(AbstractFMP4Representation):

def __init__(self, type, encoding_id, muxing_id, segment_path, start_segment_number=None,
end_segment_number=None, id_=None, custom_data=None):
end_segment_number=None, id_=None, custom_data=None, start_keyframe_id=None, end_keyframe_id=None):
super().__init__(id_=id_, custom_data=custom_data, type=type, encoding_id=encoding_id, muxing_id=muxing_id,
segment_path=segment_path, start_segment_number=start_segment_number,
end_segment_number=end_segment_number)
end_segment_number=end_segment_number, start_keyframe_id=start_keyframe_id,
end_keyframe_id=end_keyframe_id)

@classmethod
def parse_from_json_object(cls, json_object):
Expand All @@ -20,9 +21,13 @@ def parse_from_json_object(cls, json_object):
segment_path = representation.segmentPath
start_segment_number = representation.startSegmentNumber
end_segment_number = representation.endSegmentNumber
start_keyframe_id = representation.startKeyframeId
end_keyframe_id = representation.endKeyframeId

fmp4_representation = FMP4Representation(id_=id_, custom_data=custom_data, type=type, encoding_id=encoding_id,
muxing_id=muxing_id, segment_path=segment_path,
start_segment_number=start_segment_number,
end_segment_number=end_segment_number)
end_segment_number=end_segment_number,
start_keyframe_id=start_keyframe_id,
end_keyframe_id=end_keyframe_id)
return fmp4_representation
19 changes: 19 additions & 0 deletions bitmovin/resources/models/manifests/dash/vtt_representation.py
@@ -0,0 +1,19 @@
from bitmovin.resources.models import AbstractModel
from bitmovin.utils import Serializable


class VttRepresentation(AbstractModel, Serializable):

def __init__(self, vtt_url, id_=None, custom_data=None):
super().__init__(id_=id_, custom_data=custom_data)
self.vttUrl = vtt_url

@classmethod
def parse_from_json_object(cls, json_object):
id_ = json_object['id']
custom_data = json_object.get('customData')
vtt_url = json_object['vttUrl']

vtt_representation = VttRepresentation(
id_=id_, custom_data=custom_data, vtt_url=vtt_url)
return vtt_representation
6 changes: 4 additions & 2 deletions bitmovin/resources/models/manifests/hls/vtt_media.py
Expand Up @@ -4,10 +4,11 @@
class VttMedia(AbstractMedia):

def __init__(self, name, group_id, vtt_url, language=None, assoc_language=None, is_default=None, autoselect=None,
characteristics=None, id_=None):
characteristics=None, id_=None, uri=None):
super().__init__(id_=id_, name=name, group_id=group_id, language=language, assoc_language=assoc_language,
is_default=is_default, autoselect=autoselect, characteristics=characteristics)
self.vttUrl = vtt_url
self.uri = uri

@classmethod
def parse_from_json_object(cls, json_object):
Expand All @@ -21,9 +22,10 @@ def parse_from_json_object(cls, json_object):
autoselect = media.autoselect
characteristics = media.characteristics
vtt_url = json_object.get('vttUrl')
uri = json_object.get('uri')

vtt_media = VttMedia(id_=id_, name=name, group_id=group_id, language=language, assoc_language=assoc_language,
is_default=is_default, autoselect=autoselect, characteristics=characteristics,
vtt_url=vtt_url)
vtt_url=vtt_url, uri=uri)

return vtt_media
28 changes: 25 additions & 3 deletions bitmovin/resources/models/outputs/s3_output.py
@@ -1,20 +1,22 @@
from bitmovin.errors import InvalidTypeError
from bitmovin.resources.enums import AWSCloudRegion
from bitmovin.resources.enums import AWSCloudRegion, S3SignatureVersion
from bitmovin.utils import Serializable
from . import AbstractOutput


class S3Output(AbstractOutput, Serializable):

def __init__(self, access_key, secret_key, bucket_name, cloud_region=None, id_=None, custom_data=None,
name=None, description=None, md5_meta_tag=None):
name=None, description=None, md5_meta_tag=None, signature_version=None):
super().__init__(id_=id_, custom_data=custom_data, name=name, description=description)
self.accessKey = access_key
self.secretKey = secret_key
self.bucketName = bucket_name
self._cloudRegion = None
self.cloudRegion = cloud_region
self.md5MetaTag = md5_meta_tag
self._signatureVersion = None
self.signatureVersion = signature_version

@property
def cloudRegion(self):
Expand All @@ -32,6 +34,24 @@ def cloudRegion(self, new_region):
raise InvalidTypeError(
'Invalid type {} for cloudRegion: must be either str or AWSCloudRegion!'.format(type(new_region)))

@property
def signatureVersion(self):
return self._signatureVersion

@signatureVersion.setter
def signatureVersion(self, new_signature_version):
if new_signature_version is None:
self._signatureVersion = None
elif isinstance(new_signature_version, str):
self._signatureVersion = new_signature_version
elif isinstance(new_signature_version, S3SignatureVersion):
self._signatureVersion = new_signature_version.value
else:
raise InvalidTypeError(
'Invalid type {} for signatureVersion: must be either str or S3SignatureVersion!'.format(
type(new_signature_version))
)

@classmethod
def parse_from_json_object(cls, json_object):
id_ = json_object['id']
Expand All @@ -42,13 +62,15 @@ def parse_from_json_object(cls, json_object):
name = json_object.get('name')
description = json_object.get('description')
md5_meta_tag = json_object.get('md5MetaTag')
signature_version = json_object.get('signatureVersion')

s3_output = S3Output(
access_key=access_key, secret_key=secret_key, bucket_name=bucket_name, cloud_region=cloud_region, id_=id_,
name=name, description=description, md5_meta_tag=md5_meta_tag)
name=name, description=description, md5_meta_tag=md5_meta_tag, signature_version=signature_version)
return s3_output

def serialize(self):
serialized = super().serialize()
serialized['cloudRegion'] = self.cloudRegion
serialized['signatureVersion'] = self.signatureVersion
return serialized
25 changes: 24 additions & 1 deletion bitmovin/services/manifests/dash_manifest_service.py
Expand Up @@ -2,7 +2,7 @@
from bitmovin.errors import FunctionalityNotAvailableError, InvalidTypeError, BitmovinApiError, InvalidStatusError
from bitmovin.resources import DashManifest, Period, ResourceResponse, Status, Response, AudioAdaptationSet, \
VideoAdaptationSet, SubtitleAdaptationSet, FMP4Representation, DRMFMP4Representation, WebMRepresentation, \
ContentProtection, DashMP4Representation, CustomXMLElement
ContentProtection, DashMP4Representation, CustomXMLElement, VttRepresentation
from bitmovin.services.manifests.generic_manifest_service import GenericManifestService
from .manifest_control_service import ManifestControlService

Expand Down Expand Up @@ -100,6 +100,29 @@ def add_subtitle_adaptation_set(self, object_, manifest_id, period_id):

raise InvalidStatusError('Unknown status {} received'.format(response.status))

def add_vtt_representation(self, object_, manifest_id, period_id, adaptationset_id):
if not isinstance(object_, VttRepresentation):
raise InvalidTypeError('object_ has to be an instance of {}'.format(VttRepresentation.__name__))

url = self.relative_url
if not url.endswith('/'):
url += '/'

url = urljoin(url, '{}/periods/{}/adaptationsets/{}/representations/vtt'.format(
manifest_id, period_id, adaptationset_id))

response = self.http_client.post(url, object_) # type: Response

if response.status == Status.ERROR.value:
raise BitmovinApiError('Response was not successful: {}'.format(response.raw_response), response)

if response.status == Status.SUCCESS.value:
created_resource = self.parsing_utils.parse_bitmovin_resource_from_response(
response=response, class_=VttRepresentation)
return ResourceResponse(response=response, resource=created_resource)

raise InvalidStatusError('Unknown status {} received'.format(response.status))

def add_fmp4_representation(self, object_, manifest_id, period_id, adaptationset_id):
if not isinstance(object_, FMP4Representation):
raise InvalidTypeError('object_ has to be an instance of {}'.format(FMP4Representation.__name__))
Expand Down
34 changes: 18 additions & 16 deletions examples/encoding/create_av1_encoding.py
Expand Up @@ -16,42 +16,41 @@
S3_OUTPUT_BUCKETNAME = '<INSERT_YOUR_BUCKET_NAME>'

date_component = datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S')
OUTPUT_BASE_PATH = 'output/internal/av1/{}/'.format(date_component)
OUTPUT_BASE_PATH = '/output/internal/av1/{}/'.format(date_component)

bitmovin = Bitmovin(api_key=API_KEY)

encoding_profiles = [
dict(height=240, bitrate=400000, fps=None)
]
encoding_profiles = [dict(height=240, bitrate=400000, fps=None)]

audio_bitrate = 128000


def main():
input = HTTPSInput(name='Demo HTTPS input', host=HTTPS_INPUT_HOST)
input = bitmovin.inputs.HTTPS.create(input).resource
input_ = HTTPSInput(name='Demo HTTPS input', host=HTTPS_INPUT_HOST)
input_ = bitmovin.inputs.HTTPS.create(input_).resource

output = S3Output(access_key=S3_OUTPUT_ACCESSKEY,
secret_key=S3_OUTPUT_SECRETKEY,
bucket_name=S3_OUTPUT_BUCKETNAME,
name='Demo S3 Output')
secret_key=S3_OUTPUT_SECRETKEY,
bucket_name=S3_OUTPUT_BUCKETNAME,
name='Demo S3 Output')
output = bitmovin.outputs.S3.create(output).resource

encoding = Encoding(name='Python Example - Create AV1 Encoding',
cloud_region=CloudRegion.GOOGLE_EUROPE_WEST_1)

encoding = bitmovin.encodings.Encoding.create(encoding).resource

video_input_stream = StreamInput(input_id=input.id,
video_input_stream = StreamInput(input_id=input_.id,
input_path=HTTPS_INPUT_PATH,
selection_mode=SelectionMode.AUTO)
audio_input_stream = StreamInput(input_id=input.id,
audio_input_stream = StreamInput(input_id=input_.id,
input_path=HTTPS_INPUT_PATH,
selection_mode=SelectionMode.AUTO)

audio_codec_configuration = OpusCodecConfiguration(name='Demo Opus Codec Configuration',
bitrate=audio_bitrate,
rate=48000)
audio_codec_configuration = bitmovin.codecConfigurations.OPUS.create(audio_codec_configuration).resource
bitrate=audio_bitrate,
rate=48000)
audio_codec_configuration = bitmovin.codecConfigurations.Opus.create(audio_codec_configuration).resource

audio_stream = Stream(codec_configuration_id=audio_codec_configuration.id,
input_streams=[audio_input_stream], name='Demo Audio Stream')
Expand All @@ -64,7 +63,10 @@ def main():
bitrate=profile['bitrate'],
rate=profile['fps'],
width=None,
height=profile['height']
height=profile['height'],
key_placement_mode='AUTO',
rate_control_mode='0',
adaptive_quant_mode='OFF'
)

video_codec_configuration = bitmovin.codecConfigurations.AV1.create(video_codec_configuration).resource
Expand All @@ -90,7 +92,7 @@ def create_muxing(encoding, output, video_stream, audio_stream):
acl_entry = ACLEntry(permission=ACLPermission.PUBLIC_READ)

video_muxing_output = EncodingOutput(output_id=output.id,
output_path=OUTPUT_BASE_PATH,
output_path=S3_OUTPUT_BUCKETNAME+OUTPUT_BASE_PATH,
acl=[acl_entry])

video_muxing_stream = MuxingStream(video_stream.id)
Expand Down

0 comments on commit 86c2804

Please sign in to comment.