CIS FFmpeg API implemented
[living-lab-site.git] / cis / api / base.py
1 #!/usr/bin/env python
2
3 """
4 Base classes for the external programs API.
5 """
6
7 import cis_exceptions
8 import re
9
10 class BaseTranscoder:
11     """
12     Abstraction of the API class for the transcoder program. 
13     """
14
15     prog_bin = None
16     input_file = None
17     output_file = None
18     dest_path = ''
19     name = None
20
21     # Recommended formats.
22     containers = {
23         "avi": None,
24         "flv": None,
25         "mp4": None,
26         "ogg": None,
27         "webm": None,
28         "mpegts": None
29     }
30     # File extensions by container. First value is for audio files and the
31     # second one is for (audio-)video files.
32     extensions = {
33         "avi": ["avi", "avi"],
34         "flv": ["flv", "flv"],
35         "mp4": ["mp4", "mp4"],
36         "ogg": ["oga", "ogv"],
37         "webm": ["webm", "webm"],
38         "mpegts": ["mts", "mts"]
39     }
40     a_codecs = {
41         "mp3": None,
42         "vorbis": None
43     }
44     v_codecs = {
45         "h264": None,
46         "theora": None,
47         "vp8": None
48     }
49
50     def __init__(self, input_file, name=None, prog_bin=None):
51         self.input_file = input_file
52         if prog_bin is not None:
53             self.prog_bin = prog_bin
54         
55         if name is None:
56             if input_file.find('/') is not -1:
57                 name = input_file[(input_file.rindex('/')+1):]
58             else:
59                 name = input_file
60             if name.find('.') is not -1:
61                 name = name[:name.rindex('.')]
62
63         self.name = name
64
65     def transcode(self, container, a_codec=None, v_codec=None,
66             a_bitrate=None, a_samplingrate=None, a_channels=None,
67             v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
68         """
69         Transcodes the input file to an audio-video file.
70
71         container: possible values are listed in containers member as keys
72         a_codec: possible values are listed in a_codecs member as keys
73         v_codec: possible values are listed in v_codecs member as keys
74         a_bitrate: (numeric) audio bit rate
75         a_samplingrate: (numeric) audio sampling rate in Hz
76         a_channels: (numeric) number of audio channels
77         v_bitrate: (numeric) video bit rate
78         v_framerate: (numeric) number of frames per second for a video
79         v_resolution: (string) video image size as <width>x<height>
80         v_dar: video display aspect ratio as <den>x<num> or float
81         """
82
83         # Check parameters.
84         if a_codec is None and v_codec is None:
85             raise ValueError('No audio or video codec specified.')
86
87         if a_codec is not None and type(a_codec) is not str:
88             raise TypeError('Audio codec must be string.')
89
90         if v_codec is not None and type(v_codec) is not str:
91             raise TypeError('Video codec must be string.')
92
93         if a_samplingrate is not None and type(a_samplingrate) is not int:
94             raise TypeError('Audio sampling rate must be an integer.')
95
96         if a_channels is not None and type(a_channels) is not int:
97             raise TypeError('Audio channels parameter must be an integer.')
98
99         if v_framerate is not None and type(v_framerate) is not int:
100             raise TypeError('Video frate rate must be an integer.')
101
102         if v_resolution is not None \
103                 and re.match('[\d]+x[\d]+', v_resolution) is None:
104             raise ValueError('Video resolution must be a string like <width>x<height>.')
105
106         if v_dar is not None and (type(v_dar) is not float \
107                 and re.match('[\d]+:[\d]+', v_dar) is None):
108             raise ValueError('Video display aspect ratio must be a float or a string like <den>:<num>.')
109
110         self.output_file = self.dest_path + self.name
111         if v_resolution is not None:
112             self.output_file += '_'
113             self.output_file += v_resolution[(v_resolution.rindex('x')+1):]
114             self.output_file += 'p'
115         ext = self.tr_extension(container, (v_codec is not None))
116         if ext is not None:
117             self.output_file += '.' + ext
118
119         self._transcode(self.tr_container(container),
120                 self.tr_a_codec(a_codec), self.tr_v_codec(v_codec),
121                 a_bitrate, a_samplingrate, a_channels,
122                 v_bitrate, v_framerate, v_resolution, v_dar)
123
124     def _transcode(self, container, a_codec=None, v_codec=None,
125             a_bitrate=None, a_samplingrate=None, a_channels=None,
126             v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
127         """
128         Called by transcode; must be overridden by a child class which
129         effectively transcodes the input file.
130         """
131         pass
132
133     def tr_container(self, name):
134         """ Translates container API name into external program identifier."""
135
136         if not self.containers.has_key(name) or self.containers[name] is None:
137             raise cis_exceptions.NotImplementedException("Container " + name)
138
139         return self.containers[name]
140
141     def tr_extension(self, name, video=True):
142         """ Translates container API name into file extension."""
143
144         if video is True:
145             i = 1
146         else:
147             i = 0
148
149         if not self.extensions.has_key(name) or self.extensions[name] is None:
150             return None
151
152         return self.extensions[name][i]
153
154     def tr_a_codec(self, name):
155         """ Translates audio codec API name into external program identifier."""
156
157         if not self.a_codecs.has_key(name) or self.a_codecs[name] is None:
158             raise cis_exceptions.NotImplementedException("Audio Codec " + name)
159
160         return self.a_codecs[name]
161
162     def tr_v_codec(self, name):
163         """ Translates video codec API name into external program identifier."""
164
165         if not self.v_codecs.has_key(name) or self.v_codecs[name] is None:
166             raise cis_exceptions.NotImplementedException("Video Codec " + name)
167
168         return self.v_codecs[name]