e59214b0b9f8e78a9d67678d80b12f56d8a37487
[mediagoblin.git] / mediagoblin / media_types / audio / transcoders.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import pdb
18 import logging
19
20 from mediagoblin.processing import BadMediaFail
21
22
23 _log = logging.getLogger(__name__)
24
25 CPU_COUNT = 2 # Just assuming for now
26
27 # IMPORT MULTIPROCESSING
28 try:
29 import multiprocessing
30 try:
31 CPU_COUNT = multiprocessing.cpu_count()
32 except NotImplementedError:
33 _log.warning('multiprocessing.cpu_count not implemented!\n'
34 'Assuming 2 CPU cores')
35 except ImportError:
36 _log.warning('Could not import multiprocessing, assuming 2 CPU cores')
37
38 # IMPORT GOBJECT
39 try:
40 import gobject
41 gobject.threads_init()
42 except ImportError:
43 raise Exception('gobject could not be found')
44
45 # IMPORT PYGST
46 try:
47 import pygst
48
49 # We won't settle for less. For now, this is an arbitrary limit
50 # as we have not tested with > 0.10
51 pygst.require('0.10')
52
53 import gst
54
55 import gst.extend.discoverer
56 except ImportError:
57 raise Exception('gst/pygst >= 0.10 could not be imported')
58
59 class AudioTranscoder(object):
60 def __init__(self):
61 _log.info('Initializing {0}'.format(self.__class__.__name__))
62
63 # Instantiate MainLoop
64 self._loop = gobject.MainLoop()
65
66 def discover(self, src):
67 _log.info('Discovering {0}'.format(src))
68 self._discovery_path = src
69
70 self._discoverer = gst.extend.discoverer.Discoverer(
71 self._discovery_path)
72 self._discoverer.connect('discovered', self.__on_discovered)
73 self._discoverer.discover()
74
75 self._loop.run() # Run MainLoop
76
77 # Once MainLoop has returned, return discovery data
78 return self._discovery_data
79
80 def __on_discovered(self, data, is_media):
81 if not is_media:
82 self.halt()
83 _log.error('Could not discover {0}'.format(self._src_path))
84 raise BadMediaFail()
85
86 _log.debug('Discovered: {0}'.format(data.__dict__))
87
88 self._discovery_data = data
89
90 # Gracefully shut down MainLoop
91 self.halt()
92
93 def transcode(self, src, dst, **kw):
94 self._discovery_data = kw.get('data', self.discover(src))
95
96 self.__on_progress = kw.get('progress_callback')
97
98 quality = kw.get('quality', 0.3)
99
100 # Set up pipeline
101 self.pipeline = gst.parse_launch(
102 'filesrc location="{src}" ! '
103 'decodebin2 ! queue ! audiorate tolerance={tolerance} ! '
104 'audioconvert ! audio/x-raw-float,channels=2 ! '
105 'vorbisenc quality={quality} ! webmmux ! '
106 'progressreport silent=true ! '
107 'filesink location="{dst}"'.format(
108 src=src,
109 tolerance=80000000,
110 quality=quality,
111 dst=dst))
112
113 self.bus = self.pipeline.get_bus()
114 self.bus.add_signal_watch()
115 self.bus.connect('message', self.__on_bus_message)
116
117 self.pipeline.set_state(gst.STATE_PLAYING)
118
119 self._loop.run()
120
121 def __on_bus_message(self, bus, message):
122 _log.debug(message)
123
124 if (message.type == gst.MESSAGE_ELEMENT
125 and message.structure.get_name() == 'progress'):
126 data = dict(message.structure)
127
128 if self.__on_progress:
129 self.__on_progress(data)
130
131 _log.info('{0}% done...'.format(
132 data.get('percent')))
133 elif message.type == gst.MESSAGE_EOS:
134 _log.info('Done')
135 self.halt()
136
137 def halt(self):
138 _log.info('Quitting MainLoop gracefully...')
139 gobject.idle_add(self._loop.quit)
140
141 if __name__ == '__main__':
142 import sys
143 logging.basicConfig()
144 _log.setLevel(logging.INFO)
145
146 transcoder = AudioTranscoder()
147 data = transcoder.discover(sys.argv[1])
148 res = transcoder.transcode(*sys.argv[1:3])
149
150 pdb.set_trace()