Merge lp:~jderose/novacut/renderer into lp:novacut

Proposed by Jason Gerard DeRose
Status: Merged
Merged at revision: 73
Proposed branch: lp:~jderose/novacut/renderer
Merge into: lp:novacut
Diff against target: 926 lines (+672/-32)
14 files modified
create-demo-project.py (+73/-0)
debian/control (+1/-0)
demo.json (+157/-0)
novacut-cli (+6/-0)
novacut-renderer (+50/-0)
novacut-service (+39/-9)
novacut/schema.py (+52/-2)
novacut2/builder.py (+2/-2)
novacut2/extractor.py (+186/-0)
novacut2/renderer.py (+8/-7)
novacut2/tests/test_renderer.py (+9/-8)
novacut2/worker.py (+82/-0)
setup.py (+6/-0)
setup2.py (+1/-4)
To merge this branch: bzr merge lp:~jderose/novacut/renderer
Reviewer Review Type Date Requested Status
Novacut Dev Pending
Review via email: mp+89452@code.launchpad.net

Description of the change

Yup, too tired to go into details!

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'create-demo-project.py'
--- create-demo-project.py 1970-01-01 00:00:00 +0000
+++ create-demo-project.py 2012-01-20 15:32:24 +0000
@@ -0,0 +1,73 @@
1#!/usr/bin/python3
2
3import json
4import time
5from microfiber import dmedia_env, Database, Conflict
6from novacut import schema
7from collections import OrderedDict
8
9docs = json.load(open('demo.json', 'r'))
10for doc in docs:
11 doc['ver'] = 0
12 doc['time'] = time.time()
13
14
15p = {
16 '_id': '5A236DNAC6NMS5XIBUMHTY2A',
17 'ver': 0,
18 'title': '2 Clip Demo',
19 'db_name': 'novacut-0-5a236dnac6nms5xibumhty2a',
20 'time': 1327022245.45872,
21 'atime': 1327022245.45872,
22 'type': 'novacut/project',
23}
24
25env = dmedia_env()
26db = Database('novacut-0', env)
27db.ensure()
28project = Database(p['db_name'], env)
29if project.ensure():
30 project.post(p)
31 project.post({'docs': docs}, '_bulk_docs')
32try:
33 db.save(p)
34except Conflict:
35 pass
36db.post({'docs': docs}, '_bulk_docs')
37
38root = schema.save_to_intrinsic('AUABDULVRZIBH727GQP2HXSA', project, db)
39print('root:', root)
40
41node = {
42 'muxer': {'name': 'oggmux'},
43 'video': {
44 'encoder': {
45 'name': 'theoraenc',
46 'props': {
47 'quality': 52,
48 },
49 },
50 'filter': {
51 'mime': 'video/x-raw-yuv',
52 'caps': {
53 'width': 960,
54 'height': 540,
55 },
56 },
57 },
58}
59doc = schema.create_settings(node)
60try:
61 db.save(doc)
62except Conflict:
63 pass
64print('settings:', doc['_id'])
65
66doc = schema.create_job(root, doc['_id'])
67try:
68 db.save(doc)
69except Conflict:
70 pass
71print('job:', doc['_id'])
72
73
074
=== modified file 'debian/control'
--- debian/control 2012-01-17 07:29:22 +0000
+++ debian/control 2012-01-20 15:32:24 +0000
@@ -18,6 +18,7 @@
18 python-gst0.10,18 python-gst0.10,
19 python-dbus,19 python-dbus,
20 dmedia (>= 12.01),20 dmedia (>= 12.01),
21 dc3 (>= 12.01),
21 python3-microfiber (>= 12.01),22 python3-microfiber (>= 12.01),
22 python3-userwebkit (>= 12.01),23 python3-userwebkit (>= 12.01),
23 python3-gi | python3-gobject,24 python3-gi | python3-gobject,
2425
=== added file 'demo.json'
--- demo.json 1970-01-01 00:00:00 +0000
+++ demo.json 2012-01-20 15:32:24 +0000
@@ -0,0 +1,157 @@
1[
2 {
3 "_id": "AUABDULVRZIBH727GQP2HXSA",
4 "node": {
5 "src": [
6 "XBIMKG7RJRSCNBWLI24BMIVS",
7 "THYLBLNPTA2BBFSBGPUDSE3J",
8 "TNSLSNXN7UCRC6XECSZ46LHC",
9 "7FR4UH4JQPO4ISOPOWFDJ3FO",
10 "SZ6A53C2YTYQMUMLY3SXWHKX",
11 "5B6QDHAFTHJUU3TWX6JWE5TV",
12 "QD3FLQVR5TQIQDQHXAZ5EPXM",
13 "WXW4VPQJDG4K5XVIHX5ZTVBJ"
14 ],
15 "type": "sequence"
16 },
17 "type": "novacut/node"
18 },
19 {
20 "_id": "WXW4VPQJDG4K5XVIHX5ZTVBJ",
21 "node": {
22 "src": "VQIXPULW3G77W4XLGROMEDGFAH2XJBN4SAVFUGOZRFSIVU7N",
23 "start": {
24 "frame": 421
25 },
26 "stop": {
27 "frame": 436
28 },
29 "stream": "video",
30 "type": "slice"
31 },
32 "type": "novacut/node"
33 },
34 {
35 "_id": "QD3FLQVR5TQIQDQHXAZ5EPXM",
36 "node": {
37 "src": "W62OZLFQUSKE4K6SLJWJ4EHFDUTRLD7JKQXUQMDJSSUG6TAQ",
38 "start": {
39 "frame": 294
40 },
41 "stop": {
42 "frame": 309
43 },
44 "stream": "video",
45 "type": "slice"
46 },
47 "type": "novacut/node"
48 },
49 {
50 "_id": "5B6QDHAFTHJUU3TWX6JWE5TV",
51 "node": {
52 "src": "VQIXPULW3G77W4XLGROMEDGFAH2XJBN4SAVFUGOZRFSIVU7N",
53 "start": {
54 "frame": 381
55 },
56 "stop": {
57 "frame": 406
58 },
59 "stream": "video",
60 "type": "slice"
61 },
62 "type": "novacut/node"
63 },
64 {
65 "_id": "SZ6A53C2YTYQMUMLY3SXWHKX",
66 "node": {
67 "src": "W62OZLFQUSKE4K6SLJWJ4EHFDUTRLD7JKQXUQMDJSSUG6TAQ",
68 "start": {
69 "frame": 244
70 },
71 "stop": {
72 "frame": 269
73 },
74 "stream": "video",
75 "type": "slice"
76 },
77 "type": "novacut/node"
78 },
79 {
80 "_id": "7FR4UH4JQPO4ISOPOWFDJ3FO",
81 "node": {
82 "src": "VQIXPULW3G77W4XLGROMEDGFAH2XJBN4SAVFUGOZRFSIVU7N",
83 "start": {
84 "frame": 318
85 },
86 "stop": {
87 "frame": 356
88 },
89 "stream": "video",
90 "type": "slice"
91 },
92 "type": "novacut/node"
93 },
94 {
95 "_id": "TNSLSNXN7UCRC6XECSZ46LHC",
96 "node": {
97 "src": "W62OZLFQUSKE4K6SLJWJ4EHFDUTRLD7JKQXUQMDJSSUG6TAQ",
98 "start": {
99 "frame": 168
100 },
101 "stop": {
102 "frame": 206
103 },
104 "stream": "video",
105 "type": "slice"
106 },
107 "type": "novacut/node"
108 },
109 {
110 "_id": "THYLBLNPTA2BBFSBGPUDSE3J",
111 "node": {
112 "src": "VQIXPULW3G77W4XLGROMEDGFAH2XJBN4SAVFUGOZRFSIVU7N",
113 "start": {
114 "frame": 230
115 },
116 "stop": {
117 "frame": 280
118 },
119 "stream": "video",
120 "type": "slice"
121 },
122 "type": "novacut/node"
123 },
124 {
125 "_id": "XBIMKG7RJRSCNBWLI24BMIVS",
126 "node": {
127 "src": "W62OZLFQUSKE4K6SLJWJ4EHFDUTRLD7JKQXUQMDJSSUG6TAQ",
128 "start": {
129 "frame": 68
130 },
131 "stop": {
132 "frame": 118
133 },
134 "stream": "video",
135 "type": "slice"
136 },
137 "type": "novacut/node"
138 },
139 {
140 "_id": "W62OZLFQUSKE4K6SLJWJ4EHFDUTRLD7JKQXUQMDJSSUG6TAQ",
141 "framerate": {
142 "denom": 1,
143 "num": 25
144 },
145 "samplerate": 48000,
146 "type": "dmedia/file"
147 },
148 {
149 "_id": "VQIXPULW3G77W4XLGROMEDGFAH2XJBN4SAVFUGOZRFSIVU7N",
150 "framerate": {
151 "denom": 1,
152 "num": 25
153 },
154 "samplerate": 48000,
155 "type": "dmedia/file"
156 }
157]
0158
=== modified file 'novacut-cli'
--- novacut-cli 2012-01-17 08:37:54 +0000
+++ novacut-cli 2012-01-20 15:32:24 +0000
@@ -105,6 +105,12 @@
105 return '{} was running for {}'.format(self.bus, minsec(seconds))105 return '{} was running for {}'.format(self.bus, minsec(seconds))
106106
107107
108class Render(_Method):
109 'Render an edit'
110
111 args = ['job_id']
112
113
108114
109parser = optparse.OptionParser(115parser = optparse.OptionParser(
110 usage='%prog METHOD [ARGS...]',116 usage='%prog METHOD [ARGS...]',
111117
=== added file 'novacut-renderer'
--- novacut-renderer 1970-01-01 00:00:00 +0000
+++ novacut-renderer 2012-01-20 15:32:24 +0000
@@ -0,0 +1,50 @@
1#!/usr/bin/python
2
3# novacut: the collaborative video editor
4# Copyright (C) 2011 Novacut Inc
5#
6# This file is part of `novacut`.
7#
8# `novacut` is free software: you can redistribute it and/or modify it under
9# the terms of the GNU Affero General Public License as published by the Free
10# Software Foundation, either version 3 of the License, or (at your option)
11# any later version.
12#
13# `novacut` is distributed in the hope that it will be useful, but WITHOUT ANY
14# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
16# more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with `novacut`. If not, see <http://www.gnu.org/licenses/>.
20#
21# Authors:
22# Jason Gerard DeRose <jderose@novacut.com>
23
24"""
25Script to fire-off a render.
26"""
27
28import optparse
29import novacut2
30
31import gobject
32import dbus
33from dbus.mainloop.glib import DBusGMainLoop
34
35
36gobject.threads_init()
37DBusGMainLoop(set_as_default=True)
38session = dbus.SessionBus()
39
40
41parser = optparse.OptionParser(
42 version=novacut2.__version__,
43)
44(options, args) = parser.parse_args()
45job_id = args[0]
46
47from novacut2.worker import Worker
48worker = Worker()
49_id = worker.run(job_id)
50print(_id)
051
=== modified file 'novacut-service'
--- novacut-service 2012-01-17 05:42:15 +0000
+++ novacut-service 2012-01-20 15:32:24 +0000
@@ -1,4 +1,4 @@
1#!/usr/bin/python1#!/usr/bin/python3
22
3# novacut: the collaborative video editor3# novacut: the collaborative video editor
4# Copyright (C) 2011 Novacut Inc4# Copyright (C) 2011 Novacut Inc
@@ -31,31 +31,42 @@
31import optparse31import optparse
32import json32import json
33from os import path33from os import path
34from threading import Thread
35import subprocess
3436
35import dbus37import dbus
36import dbus.service38import dbus.service
37from dbus.mainloop.glib import DBusGMainLoop39from dbus.mainloop.glib import DBusGMainLoop
38import gobject40from gi.repository import GObject
3941
40import novacut242import novacut
4143
4244
43BUS = 'com.novacut.Renderer'45BUS = 'com.novacut.Renderer'
44IFACE = BUS46IFACE = BUS
4547
46gobject.threads_init()48GObject.threads_init()
47DBusGMainLoop(set_as_default=True)49DBusGMainLoop(set_as_default=True)
48session = dbus.SessionBus()50session = dbus.SessionBus()
4951
5052
51def dumps(obj):53renderer = path.join(path.dirname(__file__), 'novacut-renderer')
52 return json.dumps(obj, sort_keys=True, separators=(',', ': '), indent=4)54assert path.isfile(renderer)
55
56
57def _start_thread(target, *args):
58 thread = Thread(target=target, args=args)
59 thread.daemon = True
60 thread.start()
61 return thread
5362
5463
55class Service(dbus.service.Object):64class Service(dbus.service.Object):
65 workers = {}
66
56 def __init__(self, bus):67 def __init__(self, bus):
57 self.bus = bus68 self.bus = bus
58 self.mainloop = gobject.MainLoop()69 self.mainloop = GObject.MainLoop()
59 super(Service, self).__init__(session, object_path='/')70 super(Service, self).__init__(session, object_path='/')
60 self.busname = dbus.service.BusName(bus, session)71 self.busname = dbus.service.BusName(bus, session)
6172
@@ -65,12 +76,17 @@
65 def kill(self):76 def kill(self):
66 self.mainloop.quit()77 self.mainloop.quit()
6778
79 def render(self, job_id):
80 cmd = [renderer, job_id]
81 file_id = subprocess.check_output(cmd).decode('utf-8')
82 self.RenderFinished(job_id, file_id)
83
68 @dbus.service.method(IFACE, in_signature='', out_signature='s')84 @dbus.service.method(IFACE, in_signature='', out_signature='s')
69 def Version(self):85 def Version(self):
70 """86 """
71 Return Novacut version.87 Return Novacut version.
72 """88 """
73 return novacut2.__version__89 return novacut.__version__
7490
75 @dbus.service.method(IFACE, in_signature='', out_signature='i')91 @dbus.service.method(IFACE, in_signature='', out_signature='i')
76 def Kill(self):92 def Kill(self):
@@ -80,9 +96,23 @@
80 self.kill()96 self.kill()
81 return int(time.time() - start_time)97 return int(time.time() - start_time)
8298
99 @dbus.service.method(IFACE, in_signature='s', out_signature='b')
100 def Render(self, job_id):
101 """
102 Render an edit.
103 """
104 if job_id in self.workers:
105 return False
106 self.workers[job_id] = _start_thread(self.render, job_id)
107 return True
108
109 @dbus.service.signal(IFACE, signature='ss')
110 def RenderFinished(self, job_id, file_id):
111 del self.workers[job_id]
112
83113
84parser = optparse.OptionParser(114parser = optparse.OptionParser(
85 version=novacut2.__version__,115 version=novacut.__version__,
86)116)
87parser.add_option('--bus',117parser.add_option('--bus',
88 help='DBus bus name; default is {!r}'.format(BUS),118 help='DBus bus name; default is {!r}'.format(BUS),
89119
=== modified file 'novacut/schema.py'
--- novacut/schema.py 2012-01-17 12:15:44 +0000
+++ novacut/schema.py 2012-01-20 15:32:24 +0000
@@ -132,7 +132,7 @@
132from collections import namedtuple132from collections import namedtuple
133133
134from skein import skein512134from skein import skein512
135from microfiber import random_id, RANDOM_B32LEN135from microfiber import random_id, RANDOM_B32LEN, Conflict
136from dmedia.schema import (136from dmedia.schema import (
137 _label,137 _label,
138 _value,138 _value,
@@ -267,6 +267,17 @@
267 return inode.id267 return inode.id
268268
269269
270def save_to_intrinsic(root, src, dst):
271 results = {}
272 iroot = intrinsic_graph(root, src.get, results)
273 for inode in results.values():
274 doc = create_inode(inode)
275 try:
276 dst.save(doc)
277 except Conflict:
278 pass
279 return iroot
280
270281
271def check_novacut(doc):282def check_novacut(doc):
272 """283 """
@@ -411,7 +422,46 @@
411 }422 }
412 },423 },
413 'ver': VER,424 'ver': VER,
414 'type': 'novacut/node',425 'type': 'novacut/inode',
426 'time': time.time(),
427 'node': inode.node,
428 'renders': {},
429 }
430
431
432def create_settings(node):
433 inode = intrinsic_node(node)
434 return {
435 '_id': inode.id,
436 '_attachments': {
437 'node': {
438 'data': b64encode(inode.data).decode('utf-8'),
439 'content_type': 'application/json',
440 }
441 },
442 'ver': VER,
443 'type': 'novacut/settings',
444 'time': time.time(),
445 'node': inode.node,
446 }
447
448
449def create_job(root, settings):
450 node = {
451 'root': root,
452 'settings': settings,
453 }
454 inode = intrinsic_node(node)
455 return {
456 '_id': inode.id,
457 '_attachments': {
458 'node': {
459 'data': b64encode(inode.data).decode('utf-8'),
460 'content_type': 'application/json',
461 }
462 },
463 'ver': VER,
464 'type': 'novacut/job',
415 'time': time.time(),465 'time': time.time(),
416 'node': inode.node,466 'node': inode.node,
417 'renders': {},467 'renders': {},
418468
=== modified file 'novacut2/builder.py'
--- novacut2/builder.py 2012-01-18 01:02:46 +0000
+++ novacut2/builder.py 2012-01-20 15:32:24 +0000
@@ -61,11 +61,11 @@
6161
6262
63class LiveBuilder(Builder):63class LiveBuilder(Builder):
64 def __init__(self, project_id):64 def __init__(self):
65 self.Dmedia = session.get_object('org.freedesktop.Dmedia', '/')65 self.Dmedia = session.get_object('org.freedesktop.Dmedia', '/')
66 env = json.loads(self.Dmedia.GetEnv())66 env = json.loads(self.Dmedia.GetEnv())
67 env['url'] = env['url'].encode('utf-8')67 env['url'] = env['url'].encode('utf-8')
68 self.db = Database(project_db_name(project_id), env)68 self.db = Database('novacut-0', env)
69 self._cache = {}69 self._cache = {}
7070
71 def resolve_file(self, _id):71 def resolve_file(self, _id):
7272
=== added file 'novacut2/extractor.py'
--- novacut2/extractor.py 1970-01-01 00:00:00 +0000
+++ novacut2/extractor.py 2012-01-20 15:32:24 +0000
@@ -0,0 +1,186 @@
1# novacut: the distributed video editor
2# Copyright (C) 2011 Novacut Inc
3#
4# This file is part of `novacut`.
5#
6# `novacut` is free software: you can redistribute it and/or modify it under
7# the terms of the GNU Affero General Public License as published by the Free
8# Software Foundation, either version 3 of the License, or (at your option)
9# any later version.
10#
11# `novacut` is distributed in the hope that it will be useful, but WITHOUT ANY
12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
14# more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with `novacut`. If not, see <http://www.gnu.org/licenses/>.
18#
19# Authors:
20# Jason Gerard DeRose <jderose@novacut.com>
21
22import sys
23import gst
24import gobject
25
26
27gobject.threads_init()
28
29
30class FakeBin(gst.Bin):
31 def __init__(self, doc, rate):
32 super(FakeBin, self).__init__()
33 self._doc = doc
34 self._q = gst.element_factory_make('queue')
35 self._rate = gst.element_factory_make(rate)
36 self._sink = gst.element_factory_make('fakesink')
37 self.add(self._q, self._rate, self._sink)
38 self._q.link(self._rate)
39 self._rate.link(self._sink)
40
41 # Ghost Pads
42 pad = self._q.get_pad('sink')
43 self.add_pad(
44 gst.GhostPad('sink', pad)
45 )
46 pad.connect('notify::caps', self._on_notify_caps)
47
48 def _get_doc(self):
49 self._finalize()
50 return self._doc
51
52 def _query_duration(self, pad):
53 q = gst.query_new_duration(gst.FORMAT_TIME)
54 if pad.get_peer().query(q):
55 (format, duration) = q.parse_duration()
56 if format == gst.FORMAT_TIME:
57 return duration
58
59
60class VideoBin(FakeBin):
61 def __init__(self, doc):
62 super(VideoBin, self).__init__(doc, 'videorate')
63
64 def _on_notify_caps(self, pad, args):
65 caps = pad.get_negotiated_caps()
66 if not caps:
67 return
68 d = caps[0]
69 num = d['framerate'].num
70 denom = d['framerate'].denom
71 self._doc['framerate'] = {
72 'num': num,
73 'denom': denom,
74 }
75 self._doc['width'] = d['width']
76 self._doc['height'] = d['height']
77 ns = self._query_duration(pad)
78 if ns:
79 self._doc['video_ns'] = ns
80 self._doc['frames'] = ns * num / denom / gst.SECOND
81
82 def _finalize(self):
83 self._doc['frames2'] = self._rate.get_property('in')
84
85
86class AudioBin(FakeBin):
87 def __init__(self, doc):
88 super(AudioBin, self).__init__(doc, 'audiorate')
89
90 def _on_notify_caps(self, pad, args):
91 caps = pad.get_negotiated_caps()
92 if not caps:
93 return
94 d = caps[0]
95 self._doc['samplerate'] = d['rate']
96 self._doc['channels'] = d['channels']
97 ns = self._query_duration(pad)
98 if ns:
99 self._doc['audio_ns'] = ns
100 self._doc['samples'] = d['rate'] * ns / gst.SECOND
101
102 def _finalize(self):
103 # FIXME: why is this so worthless?
104 if 'samples' not in self._doc:
105 self._doc['samples'] = self._rate.get_property('in')
106
107
108class Extractor(object):
109 def __init__(self, filename):
110 self.doc = {'video_ns': 0, 'audio_ns': 0}
111 self.mainloop = gobject.MainLoop()
112 self.pipeline = gst.Pipeline()
113
114 # Create bus and connect several handlers
115 self.bus = self.pipeline.get_bus()
116 self.bus.add_signal_watch()
117 self.bus.connect('message::eos', self.on_eos)
118 self.bus.connect('message::error', self.on_error)
119
120 # Create elements
121 self.src = gst.element_factory_make('filesrc')
122 self.dec = gst.element_factory_make('decodebin2')
123
124 # Set properties
125 self.src.set_property('location', filename)
126
127 # Connect handler for 'new-decoded-pad' signal
128 self.dec.connect('pad-added', self.on_pad_added)
129 self.dec.connect('no-more-pads', self.on_no_more_pads)
130 self.typefind = self.dec.get_by_name('typefind')
131 self.typefind.connect('have-type', self.on_have_type)
132
133 # Add elements to pipeline
134 self.pipeline.add(self.src, self.dec)
135
136 # Link elements
137 self.src.link(self.dec)
138
139 self.audio = None
140 self.video = None
141
142 def run(self):
143 self.pipeline.set_state(gst.STATE_PLAYING)
144 self.mainloop.run()
145
146 def kill(self):
147 ns = max(self.doc['video_ns'], self.doc['audio_ns'])
148 self.doc['seconds'] = float(ns) / gst.SECOND
149 self.pipeline.set_state(gst.STATE_NULL)
150 self.pipeline.get_state()
151 self.mainloop.quit()
152
153 def link_pad(self, pad, name):
154 cls = {'audio': AudioBin, 'video': VideoBin}[name]
155 fakebin = cls(self.doc)
156 self.pipeline.add(fakebin)
157 pad.link(fakebin.get_pad('sink'))
158 fakebin.set_state(gst.STATE_PLAYING)
159 return fakebin
160
161 def on_pad_added(self, element, pad):
162 name = pad.get_caps()[0].get_name()
163 if name.startswith('audio/'):
164 assert self.audio is None
165 self.audio = self.link_pad(pad, 'audio')
166 elif name.startswith('video/'):
167 assert self.video is None
168 self.video = self.link_pad(pad, 'video')
169
170 def on_no_more_pads(self, element):
171 print('no more decoded pads')
172
173 def on_have_type(self, element, prop, caps):
174 self.doc['content_type'] = caps.to_string()
175
176 def on_eos(self, bus, msg):
177 if self.video:
178 self.video._finalize()
179 self.kill()
180
181 def on_error(self, bus, msg):
182 error = msg.parse_error()[1]
183 self.kill()
184 print(error)
185 sys.exit(2)
186
0187
=== modified file 'novacut2/renderer.py'
--- novacut2/renderer.py 2012-01-17 05:15:04 +0000
+++ novacut2/renderer.py 2012-01-20 15:32:24 +0000
@@ -251,14 +251,15 @@
251251
252252
253class Renderer(object):253class Renderer(object):
254 def __init__(self, job, builder, dst):254 def __init__(self, root, settings, builder, dst):
255 """255 """
256 Initialize.256 Initialize.
257257
258 :param job: a ``dict`` describing the transcode to perform.258 :param job: a ``dict`` describing the transcode to perform.
259 :param fs: a `FileStore` instance in which to store transcoded file259 :param fs: a `FileStore` instance in which to store transcoded file
260 """260 """
261 self.job = job261 self.root = root
262 self.settings = settings
262 self.builder = builder263 self.builder = builder
263 self.mainloop = gobject.MainLoop()264 self.mainloop = gobject.MainLoop()
264 self.pipeline = gst.Pipeline()265 self.pipeline = gst.Pipeline()
@@ -270,8 +271,8 @@
270 self.bus.connect('message::error', self.on_error)271 self.bus.connect('message::error', self.on_error)
271272
272 # Create elements273 # Create elements
273 self.src = builder.build(job['src'])274 self.src = builder.build(root)
274 self.mux = make_element(job['muxer'])275 self.mux = make_element(settings['muxer'])
275 self.sink = gst.element_factory_make('filesink')276 self.sink = gst.element_factory_make('filesink')
276277
277 # Add elements to pipeline278 # Add elements to pipeline
@@ -301,15 +302,15 @@
301302
302 def link_pad(self, pad, name, key):303 def link_pad(self, pad, name, key):
303 log.info('link_pad: %r, %r, %r', pad, name, key)304 log.info('link_pad: %r, %r, %r', pad, name, key)
304 if key in self.job:305 if key in self.settings:
305 klass = {'audio': AudioEncoder, 'video': VideoEncoder}[key]306 klass = {'audio': AudioEncoder, 'video': VideoEncoder}[key]
306 el = klass(self.job[key])307 el = klass(self.settings[key])
307 else:308 else:
308 el = gst.element_factory_make('fakesink')309 el = gst.element_factory_make('fakesink')
309 self.pipeline.add(el)310 self.pipeline.add(el)
310 log.info('Linking pad %r with %r', name, el)311 log.info('Linking pad %r with %r', name, el)
311 pad.link(el.get_compatible_pad(pad, pad.get_caps()))312 pad.link(el.get_compatible_pad(pad, pad.get_caps()))
312 if key in self.job:313 if key in self.settings:
313 el.link(self.mux)314 el.link(self.mux)
314 el.set_state(gst.STATE_PLAYING)315 el.set_state(gst.STATE_PLAYING)
315 return el316 return el
316317
=== modified file 'novacut2/tests/test_renderer.py'
--- novacut2/tests/test_renderer.py 2012-01-17 05:15:04 +0000
+++ novacut2/tests/test_renderer.py 2012-01-20 15:32:24 +0000
@@ -535,26 +535,27 @@
535 tmp = TempDir()535 tmp = TempDir()
536 builder = DummyBuilder(docs)536 builder = DummyBuilder(docs)
537537
538 job = {538 root = sequence1
539 'src': sequence1,539 settings = {
540 'muxer': {'name': 'oggmux'},540 'muxer': {'name': 'oggmux'},
541 }541 }
542 dst = tmp.join('out1.ogv')542 dst = tmp.join('out1.ogv')
543 inst = renderer.Renderer(job, builder, dst)543 inst = renderer.Renderer(root, settings, builder, dst)
544544
545 self.assertTrue(inst.job is job)545 self.assertIs(inst.root, root)
546 self.assertTrue(inst.builder is builder)546 self.assertIs(inst.settings, settings)
547 self.assertIs(inst.builder, builder)
547548
548 self.assertIsInstance(inst.src, gst.Element)549 self.assertIsInstance(inst.src, gst.Element)
549 self.assertTrue(inst.src.get_parent() is inst.pipeline)550 self.assertIs(inst.src.get_parent(), inst.pipeline)
550 self.assertEqual(inst.src.get_factory().get_name(), 'gnlcomposition')551 self.assertEqual(inst.src.get_factory().get_name(), 'gnlcomposition')
551552
552 self.assertIsInstance(inst.mux, gst.Element)553 self.assertIsInstance(inst.mux, gst.Element)
553 self.assertTrue(inst.mux.get_parent() is inst.pipeline)554 self.assertIs(inst.mux.get_parent(), inst.pipeline)
554 self.assertEqual(inst.mux.get_factory().get_name(), 'oggmux')555 self.assertEqual(inst.mux.get_factory().get_name(), 'oggmux')
555556
556 self.assertIsInstance(inst.sink, gst.Element)557 self.assertIsInstance(inst.sink, gst.Element)
557 self.assertTrue(inst.sink.get_parent() is inst.pipeline)558 self.assertIs(inst.sink.get_parent(), inst.pipeline)
558 self.assertEqual(inst.sink.get_factory().get_name(), 'filesink')559 self.assertEqual(inst.sink.get_factory().get_name(), 'filesink')
559 self.assertEqual(inst.sink.get_property('location'), dst)560 self.assertEqual(inst.sink.get_property('location'), dst)
560561
561562
=== added file 'novacut2/worker.py'
--- novacut2/worker.py 1970-01-01 00:00:00 +0000
+++ novacut2/worker.py 2012-01-20 15:32:24 +0000
@@ -0,0 +1,82 @@
1# novacut: the distributed video editor
2# Copyright (C) 2011 Novacut Inc
3#
4# This file is part of `novacut`.
5#
6# `novacut` is free software: you can redistribute it and/or modify it under
7# the terms of the GNU Affero General Public License as published by the Free
8# Software Foundation, either version 3 of the License, or (at your option)
9# any later version.
10#
11# `novacut` is distributed in the hope that it will be useful, but WITHOUT ANY
12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
14# more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with `novacut`. If not, see <http://www.gnu.org/licenses/>.
18#
19# Authors:
20# Jason Gerard DeRose <jderose@novacut.com>
21
22import json
23
24import dbus
25from dbus.mainloop.glib import DBusGMainLoop
26import gobject
27from dc3lib.microfiber import Database
28
29from renderer import Builder, Renderer
30
31
32gobject.threads_init()
33DBusGMainLoop(set_as_default=True)
34session = dbus.SessionBus()
35
36
37class LiveBuilder(Builder):
38 def __init__(self, Dmedia, db):
39 self.Dmedia = Dmedia
40 self.db = db
41 self._cache = {}
42
43 def resolve_file(self, _id):
44 return self.Dmedia.Resolve(_id)
45
46 def get_doc(self, _id):
47 try:
48 return self._cache[_id]
49 except KeyError:
50 doc = self.db.get(_id)
51 self._cache[_id] = doc
52 return doc
53
54
55class Worker(object):
56 def __init__(self):
57 self.Dmedia = session.get_object('org.freedesktop.Dmedia', '/')
58 env = json.loads(self.Dmedia.GetEnv())
59 env['url'] = env['url'].encode('utf-8')
60 self.novacut = Database('novacut-0', env)
61 self.dmedia = Database('dmedia-0', env)
62
63 def run(self, job_id):
64 job = self.novacut.get(job_id)
65 root = job['node']['root']
66 settings = self.novacut.get(job['node']['settings'])
67 builder = LiveBuilder(self.Dmedia, self.novacut)
68 dst = self.Dmedia.AllocateTmp()
69 renderer = Renderer(root, settings['node'], builder, dst)
70 renderer.run()
71 _id = self.Dmedia.HashAndMove(dst, 'render')
72 doc = self.dmedia.get(_id)
73 doc['render_of'] = job_id
74 self.dmedia.save(doc)
75 job['renders'][_id] = {
76 'bytes': doc['bytes'],
77 'time': doc['time'],
78 }
79 self.novacut.save(job)
80 return _id
81
82
083
=== modified file 'setup.py'
--- setup.py 2012-01-17 08:37:54 +0000
+++ setup.py 2012-01-20 15:32:24 +0000
@@ -147,6 +147,12 @@
147 ('share/icons/hicolor/48x48/apps',147 ('share/icons/hicolor/48x48/apps',
148 ['data/novacut.svg']148 ['data/novacut.svg']
149 ),149 ),
150 ('lib/novacut',
151 ['novacut-service'],
152 ),
153 ('share/dbus-1/services/',
154 ['data/com.novacut.Renderer.service']
155 ),
150 ],156 ],
151 cmdclass={'test': Test},157 cmdclass={'test': Test},
152)158)
153159
=== modified file 'setup2.py'
--- setup2.py 2012-01-17 08:26:59 +0000
+++ setup2.py 2012-01-20 15:32:24 +0000
@@ -134,10 +134,7 @@
134 packages=['novacut2'],134 packages=['novacut2'],
135 data_files=[135 data_files=[
136 ('lib/novacut',136 ('lib/novacut',
137 ['novacut-service'],137 ['novacut-renderer'],
138 ),
139 ('share/dbus-1/services/',
140 ['data/com.novacut.Renderer.service']
141 ),138 ),
142 ],139 ],
143)140)

Subscribers

People subscribed via source and target branches

to status/vote changes: