52
117
GladeWindow.__init__(self)
120
self.project = project
121
self.settings = self.project.getSettings()
57
self.progressbar = self.widgets["progressbar"]
58
self.filebutton = self.widgets["filebutton"]
59
self.settingsbutton = self.widgets["settingsbutton"]
60
self.cancelbutton = self.widgets["cancelbutton"]
61
self.recordbutton = self.widgets["recordbutton"]
62
self.recordbutton.set_sensitive(False)
63
self.vinfo = self.widgets["videoinfolabel"]
64
self.ainfo = self.widgets["audioinfolabel"]
65
124
self.window.set_icon_from_file(configure.get_pixmap_dir() + "/pitivi-render-16.png")
67
# grab the Pipeline and settings
68
self.project = project
70
self.pipeline = pipeline
72
self.pipeline = self.project.pipeline
73
self.detectStreamTypes()
76
self.rendering = False
77
self.renderaction = None
78
self.settings = project.getSettings()
126
# FIXME: re-enable this widget when bug #637078 is implemented
127
self.selected_only_button.destroy()
129
Renderer.__init__(self, project, pipeline)
132
self.preferred_vencoder = self.settings.vencoder
133
self.preferred_aencoder = self.settings.aencoder
79
135
self.timestarted = 0
80
136
self._displaySettings()
82
138
self.window.connect("delete-event", self._deleteEventCb)
139
self.settings.connect("settings-changed", self._settingsChanged)
140
self.settings.connect("encoders-changed", self._settingsChanged)
85
self.debug("shutting down")
87
self.removeRecordAction()
142
def _settingsChanged(self, settings):
143
self.updateResolution()
90
145
def _displaySettings(self):
92
self.vinfo.set_markup(self.settings.getVideoDescription())
94
self.vinfo.set_markup("no video")
97
self.ainfo.set_markup(self.settings.getAudioDescription())
99
self.ainfo.set_markup("no audio")
101
def _fileButtonClickedCb(self, button):
102
dialog = gtk.FileChooserDialog(title=_("Choose file to render to"),
104
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
105
gtk.STOCK_OK, gtk.RESPONSE_ACCEPT),
106
action=gtk.FILE_CHOOSER_ACTION_SAVE)
107
dialog.set_icon_name("pitivi")
109
fullfilename = urlparse(self.outfile).path
110
dialog.set_filename(urllib.url2pathname(fullfilename))
111
dialog.set_current_name(urllib.url2pathname(os.path.basename(fullfilename)))
113
dialog.set_current_folder(self.app.settings.lastExportFolder)
117
if res == gtk.RESPONSE_ACCEPT:
118
self.outfile = dialog.get_uri()
119
shortfilename = os.path.basename(urlparse(self.outfile).path)
120
button.set_label(urllib.url2pathname(shortfilename))
121
self.recordbutton.set_sensitive(True)
122
self.progressbar.set_text("")
123
self.app.settings.lastExportFolder = dialog.get_current_folder()
126
def _positionCb(self, unused_pipeline, position):
127
self.debug("%r %r", unused_pipeline, position)
128
timediff = time.time() - self.timestarted
129
length = self.project.timeline.duration
130
self.progressbar.set_fraction(float(min(position, length)) / float(length))
131
if timediff > 5.0 and position:
132
# only display ETA after 5s in order to have enough averaging and
133
# if the position is non-null
134
totaltime = (timediff * float(length) / float(position)) - timediff
135
length = beautify_length(int(totaltime * gst.SECOND))
137
self.progressbar.set_text(_("About %s left") % length)
139
def _changeSourceSettings(self, settings):
140
videocaps = settings.getVideoCaps()
141
for source in self.project.sources.getSources():
142
source.setFilterCaps(videocaps)
144
def _recordButtonClickedCb(self, unused_button):
145
self.debug("Rendering")
146
if self.outfile and not self.rendering:
147
self.addRecordAction()
149
self.timestarted = time.time()
150
self.rendering = True
151
self.cancelbutton.set_label("gtk-cancel")
152
self.progressbar.set_text(_("Rendering"))
153
self.recordbutton.set_sensitive(False)
154
self.filebutton.set_sensitive(False)
155
self.settingsbutton.set_sensitive(False)
157
def _settingsButtonClickedCb(self, unused_button):
158
dialog = ExportSettingsDialog(self.app, self.settings)
161
if res == gtk.RESPONSE_ACCEPT:
162
self.settings = dialog.getSettings()
163
self._displaySettings()
166
def _eosCb(self, unused_pipeline):
168
self.rendering = False
169
self.progressbar.set_text(_("Rendering Complete"))
170
self.progressbar.set_fraction(1.0)
171
self.recordbutton.set_sensitive(False)
172
self.filebutton.set_sensitive(True)
173
self.settingsbutton.set_sensitive(True)
174
self.cancelbutton.set_label("gtk-close")
175
self.removeRecordAction()
147
self.frame_rate_combo.set_model(frame_rates)
148
set_combo_value(self.frame_rate_combo, self.settings.videorate)
149
# note: this will trigger an update of the video resolution label
150
self.scale_spinbutton.set_value(self.settings.render_scale)
153
self.channels_combo.set_model(audio_channels)
154
set_combo_value(self.channels_combo, self.settings.audiochannels)
156
self.sample_rate_combo.set_model(audio_rates)
157
set_combo_value(self.sample_rate_combo, self.settings.audiorate)
159
self.sample_depth_combo.set_model(audio_depths)
160
set_combo_value(self.sample_depth_combo, self.settings.audiodepth)
163
self.muxercombobox.set_model(factorylist(self.settings.muxers))
164
# note: this will trigger an update of the codec comboboxes
165
set_combo_value(self.muxercombobox,
166
gst.element_factory_find(self.settings.muxer))
168
ellipsize(self.muxercombobox)
169
ellipsize(self.audio_encoder_combo)
170
ellipsize(self.video_encoder_combo)
173
self.filebutton.set_current_folder(self.app.settings.lastExportFolder)
174
self.updateFilename(self.project.name)
176
def updateFilename(self, basename):
177
"""Updates the filename UI element to show the specified file name."""
178
extension = extension_for_muxer(self.settings.muxer)
180
name = "%s%s%s" % (basename, os.path.extsep, extension)
183
self.fileentry.set_text(name)
185
def _muxerComboChangedCb(self, muxer_combo):
186
muxer = get_combo_value(muxer_combo).get_name()
187
self.settings.setEncoders(muxer=muxer)
189
# Update the extension of the filename.
190
basename = os.path.splitext(self.fileentry.get_text())[0]
191
self.updateFilename(basename)
193
# Update muxer-dependent widgets.
194
self.muxer_combo_changing = True
196
self.updateAvailableEncoders()
198
self.muxer_combo_changing = False
200
def updateAvailableEncoders(self):
201
"""Update the encoder comboboxes to show the available encoders."""
202
video_encoders = self.settings.getVideoEncoders()
203
video_encoder_model = factorylist(video_encoders)
204
self.video_encoder_combo.set_model(video_encoder_model)
206
audio_encoders = self.settings.getAudioEncoders()
207
audio_encoder_model = factorylist(audio_encoders)
208
self.audio_encoder_combo.set_model(audio_encoder_model)
210
self._updateEncoderCombo(
211
self.video_encoder_combo, self.preferred_vencoder)
212
self._updateEncoderCombo(
213
self.audio_encoder_combo, self.preferred_aencoder)
215
def _updateEncoderCombo(self, encoder_combo, preferred_encoder):
216
"""Select the specified encoder for the specified encoder combo."""
217
if preferred_encoder:
218
# A preferrence exists, pick it if it can be found in
219
# the current model of the combobox.
220
vencoder = gst.element_factory_find(preferred_encoder)
221
set_combo_value(encoder_combo, vencoder, default_index=0)
223
# No preferrence exists, pick the first encoder from
224
# the current model of the combobox.
225
encoder_combo.set_active(0)
227
def _scaleSpinbuttonChangedCb(self, button):
228
render_scale = self.scale_spinbutton.get_value()
229
self.settings.setVideoProperties(render_scale=render_scale)
230
self.updateResolution()
232
def updateResolution(self):
233
width, height = self.settings.getVideoWidthAndHeight(render=True)
234
self.resolution_label.set_text("%d x %d" % (width, height))
236
def _projectSettingsButtonClickedCb(self, button):
237
from pitivi.ui.projectsettings import ProjectSettingsDialog
238
d = ProjectSettingsDialog(self.window, self.project)
239
d.window.connect("destroy", self._projectSettingsDestroyCb)
242
def _projectSettingsDestroyCb(self, dialog):
243
self._displaySettings()
245
def _frameRateComboChangedCb(self, combo):
246
framerate = get_combo_value(combo)
247
self.settings.setVideoProperties(framerate=framerate)
249
def _videoEncoderComboChangedCb(self, combo):
250
vencoder = get_combo_value(combo).get_name()
251
self.settings.setEncoders(vencoder=vencoder)
252
if not self.muxer_combo_changing:
253
# The user directly changed the video encoder combo.
254
self.preferred_vencoder = vencoder
256
def _videoSettingsButtonClickedCb(self, button):
257
self._elementSettingsDialog(self.video_encoder_combo, 'vcodecsettings')
259
def _channelsComboChangedCb(self, combo):
260
self.settings.setAudioProperties(nbchanns=get_combo_value(combo))
262
def _sampleDepthComboChangedCb(self, combo):
263
self.settings.setAudioProperties(depth=get_combo_value(combo))
265
def _sampleRateComboChangedCb(self, combo):
266
self.settings.setAudioProperties(rate=get_combo_value(combo))
268
def _audioEncoderChangedComboCb(self, combo):
269
aencoder = get_combo_value(combo).get_name()
270
self.settings.setEncoders(aencoder=aencoder)
271
if not self.muxer_combo_changing:
272
# The user directly changed the audio encoder combo.
273
self.preferred_aencoder = aencoder
275
def _audioSettingsButtonClickedCb(self, button):
276
self._elementSettingsDialog(self.audio_encoder_combo, 'acodecsettings')
278
def _elementSettingsDialog(self, combo, settings_attr):
279
factory = get_combo_value(combo)
280
settings = getattr(self.settings, settings_attr)
281
dialog = GstElementSettingsDialog(factory, settings)
283
response = dialog.run()
284
if response == gtk.RESPONSE_OK:
285
setattr(self.settings, settings_attr, dialog.getSettings())
288
def _renderButtonClickedCb(self, unused_button):
289
self.outfile = self.filebutton.get_uri() + "/" + self.fileentry.get_text()
290
self.progress = EncodingProgressDialog(self.app, self)
291
self.window.hide() # Hide the rendering settings dialog while rendering
294
self.progress.connect("cancel", self._cancelRender)
295
self.progress.connect("pause", self._pauseRender)
296
self.pipeline.connect("state-changed", self._stateChanged)
298
def _cancelRender(self, progress):
299
self.debug("aborting render")
302
def _pauseRender(self, progress):
303
self.pipeline.togglePlayback()
305
def _stateChanged(self, pipeline, state):
306
self.progress.setState(state)
308
def updatePosition(self, fraction, text):
310
self.progress.updatePosition(fraction, text)
312
def updateUIOnEOS(self):
314
When a render completes or is cancelled, update the UI
316
self.progress.destroy()
318
self.window.show() # Show the encoding dialog again
319
self.pipeline.disconnect_by_function(self._stateChanged)
177
321
def _cancelButtonClickedCb(self, unused_button):
178
322
self.debug("Cancelling !")
181
325
def _deleteEventCb(self, window, event):
182
326
self.debug("delete event")
185
def detectStreamTypes(self):
186
self.have_video = False
187
self.have_audio = False
189
# we can only render TimelineSourceFactory
190
sources = [factory for factory in self.pipeline.factories.keys()
191
if isinstance(factory, SourceFactory)]
192
timeline_source = sources[0]
193
assert isinstance(timeline_source, TimelineSourceFactory)
195
for track in timeline_source.timeline.tracks:
196
if isinstance(track.stream, AudioStream) and track.duration > 0:
197
self.have_audio = True
198
elif isinstance(track.stream, VideoStream) and \
200
self.have_video = True
202
def addRecordAction(self):
203
self.debug("renderaction %r", self.renderaction)
204
if self.renderaction == None:
205
self.pipeline.connect('position', self._positionCb)
206
self.pipeline.connect('eos', self._eosCb)
207
self.debug("Setting pipeline to STOP")
209
settings = export_settings_to_render_settings(self.settings,
210
self.have_video, self.have_audio)
211
self.debug("Creating RenderAction")
212
sources = [factory for factory in self.pipeline.factories
213
if isinstance(factory, SourceFactory)]
214
self.renderaction = render_action_for_uri(self.outfile,
216
self.debug("setting action on pipeline")
217
self.pipeline.addAction(self.renderaction)
218
self.debug("Activating render action")
219
self.renderaction.activate()
220
self.debug("Setting all active ViewAction to sync=False")
221
for ac in self.pipeline.actions:
222
if isinstance(ac, ViewAction) and ac.isActive():
224
self.debug("Updating all sources to render settings")
225
self._changeSourceSettings(self.settings)
226
self.debug("setting pipeline to PAUSE")
227
self.pipeline.pause()
230
def removeRecordAction(self):
231
self.debug("renderaction %r", self.renderaction)
232
if self.renderaction:
234
self.renderaction.deactivate()
235
self.pipeline.removeAction(self.renderaction)
236
self.debug("putting all active ViewActions back to sync=True")
237
for ac in self.pipeline.actions:
238
if isinstance(ac, ViewAction) and ac.isActive():
240
self._changeSourceSettings(self.project.getSettings())
241
self.pipeline.pause()
242
self.pipeline.disconnect_by_function(self._positionCb)
243
self.pipeline.disconnect_by_function(self._eosCb)
244
self.renderaction = None
330
# TODO: Do this only when the settings actually changed.
331
self.project.setSettings(self.settings)
332
GladeWindow.destroy(self)