Skip to content
This repository has been archived by the owner on May 28, 2022. It is now read-only.

Commit

Permalink
Closes GH-241 Merge branch 'youtube-streaming' into experimental
Browse files Browse the repository at this point in the history
- Merges in Jonathan Shen & Mike Chong's work on rtmp-streaming
- There is still work that needs to be done per GH-206 before youtube
streamin functionality is complete.

#206
  • Loading branch information
zxiiro committed Dec 2, 2012
2 parents 86201d0 + eb0223f commit 1dae43a
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 2 deletions.
Binary file not shown.
7 changes: 7 additions & 0 deletions pkg/windows/Freeseer-for-Windows/setup.bat
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ cd ..
xcopy /y /s "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\sdk\bindings\python\v2.7\lib" "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\"
xcopy /y /s "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\site-packages" "C:\Python27\Lib\site-packages"

cd gstreamer

:: Add libgstrtmp to Gstreamer
xcopy /y /i "libgstrtmp.dll" "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\gstreamer-0.10\libgstrtmp.dll"

cd ..

:: Delete the dependencies installers.
del /q deps

Expand Down
7 changes: 7 additions & 0 deletions pkg/windows/installDependencies.bat
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,12 @@ python C:\Python27\Lib\site-packages\easy_install.py yapsy==1.9.2
xcopy /y /s "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\sdk\bindings\python\v2.7\lib" "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\"
xcopy /y /s "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\site-packages" "C:\Python27\Lib\site-packages"

cd gstreamer

:: Add libgstrtmp to Gstreamer
xcopy /y /i "libgstrtmp.dll" "%PROGRAMFILES%\OSSBuild\GStreamer\v0.10.7\lib\gstreamer-0.10\libgstrtmp.dll"

cd ..

:: Delete the dependencies installers.
del /q deps
2 changes: 1 addition & 1 deletion src/freeseer/framework/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(self, configdir):
self.record_to_file = True
self.record_to_file_plugin = 'Ogg Output'
self.record_to_stream = False
self.record_to_stream_plugin = None
self.record_to_stream_plugin = 'RTMP Streaming'
self.audio_feedback = False
self.video_preview = True
self.default_language = "tr_en_US.qm" # Set default language to English if user did not define
Expand Down
1 change: 1 addition & 0 deletions src/freeseer/framework/gstreamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def load_output_plugins(self, plugins, record_audio, record_video, metadata):
def unload_output_plugins(self):
for plugin in self.output_plugins:
gst.element_unlink_many(self.video_tee, plugin)
gst.element_unlink_many(self.audio_tee, plugin)
self.player.remove(plugin)

def load_audiomixer(self, mixer, inputs):
Expand Down
10 changes: 10 additions & 0 deletions src/freeseer/frontend/configtool/configtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,16 @@ def load_av_widget(self):
self.avWidget.streamComboBox.setEnabled(False)
self.avWidget.streamSetupPushButton.setEnabled(False)

n = 0 # Counter for finding Stream Format to set as current
self.avWidget.streamComboBox.clear()
plugins = self.plugman.plugmanc.getPluginsOfCategory("Output")
for plugin in plugins:
if plugin.plugin_object.get_recordto() == IOutput.STREAM:
self.avWidget.streamComboBox.addItem(plugin.plugin_object.get_name())
if plugin.plugin_object.get_name() == self.config.record_to_stream_plugin:
self.avWidget.streamComboBox.setCurrentIndex(n)
n += 1

def toggle_audiomixer_state(self, state):
self.config.enable_audio_recording = state
self.config.writeConfig()
Expand Down
289 changes: 289 additions & 0 deletions src/freeseer/plugins/output/rtmp-streaming/rtmp-streaming.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
'''
freeseer - vga/presentation capture software
Copyright (C) 2011-2012 Free and Open Source Software Learning Centre
http://fosslc.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
For support, questions, suggestions or any other inquiries, visit:
http://wiki.github.com/Freeseer/freeseer/
@author: Jonathan Shen
'''

import ConfigParser

import pygst
pygst.require("0.10")
import gst

from PyQt4 import QtGui, QtCore

from freeseer.framework.plugin import IOutput

class RTMPOutput(IOutput):
name = "RTMP Streaming"
type = IOutput.BOTH
recordto = IOutput.STREAM
tags = None

# RTMP Streaming variables
url = ""
audio_quality = 0.3
video_bitrate = 2400
video_tune='none'

TUNE_VALUES = ['none', 'film', 'animation', 'grain', 'stillimage', 'psnr', 'ssim', 'fastdecode', 'zerolatency']

#@brief - RTMP Streaming plugin.
# Structure for function was based primarily off the ogg function
# Creates a bin to stream flv content to [self.url]
# Bin has audio and video ghost sink pads
# Converts audio and video to flv with [flvmux] element
# Streams flv content to [self.url]
# TODO - Error handling - verify pad setup
def get_output_bin(self, audio=True, video=True, metadata=None):
bin = gst.Bin(self.name)

if metadata is not None:
self.set_metadata(metadata)

# Muxer
muxer = gst.element_factory_make("flvmux", "muxer")

# Setup metadata
# set tag merge mode to GST_TAG_MERGE_REPLACE
merge_mode = gst.TagMergeMode.__enum_values__[2]

muxer.merge_tags(self.tags, merge_mode)
muxer.set_tag_merge_mode(merge_mode)

bin.add(muxer)

# RTMP sink
rtmpsink = gst.element_factory_make('rtmpsink', 'rtmpsink')
rtmpsink.set_property('location', self.url)
bin.add(rtmpsink)

#
# Setup Audio Pipeline if Audio Recording is Enabled
#
if audio:
audioqueue = gst.element_factory_make("queue", "audioqueue")
bin.add(audioqueue)

audioconvert = gst.element_factory_make("audioconvert", "audioconvert")
bin.add(audioconvert)

audiolevel = gst.element_factory_make('level', 'audiolevel')
audiolevel.set_property('interval', 20000000)
bin.add(audiolevel)

audiocodec = gst.element_factory_make("faac", "audiocodec")
audiocodec.set_property("quality", float(self.audio_quality))
bin.add(audiocodec)

# Setup ghost pads
audiopad = audioqueue.get_pad("sink")
audio_ghostpad = gst.GhostPad("audiosink", audiopad)
bin.add_pad(audio_ghostpad)

gst.element_link_many(audioqueue, audioconvert, audiolevel, audiocodec, muxer)


#
# Setup Video Pipeline
#
if video:
videoqueue = gst.element_factory_make("queue", "videoqueue")
bin.add(videoqueue)

videocodec = gst.element_factory_make("x264enc", "videocodec")
videocodec.set_property("bitrate", int(self.video_bitrate))
if self.video_tune != 'none':
videocodec.set_property('tune', self.video_tune)
bin.add(videocodec)

# Setup ghost pads
videopad = videoqueue.get_pad("sink")
video_ghostpad = gst.GhostPad("videosink", videopad)
bin.add_pad(video_ghostpad)

gst.element_link_many(videoqueue, videocodec, muxer)

#
# Link muxer to rtmpsink
#
gst.element_link_many(muxer, rtmpsink)

return bin

def set_metadata(self, data):
'''
Populate global tag list variable with file metadata for
vorbistag audio element
'''
self.tags = gst.TagList()

for tag in data.keys():
if(gst.tag_exists(tag)):
self.tags[tag] = data[tag]
else:
#self.core.logger.log.debug("WARNING: Tag \"" + str(tag) + "\" is not registered with gstreamer.")
pass

def load_config(self, plugman):
self.plugman = plugman

try:
self.url = self.plugman.plugmanc.readOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Stream URL")
self.audio_quality = self.plugman.plugmanc.readOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Audio Quality")
self.video_bitrate = self.plugman.plugmanc.readOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Bitrate")
self.video_tune = self.plugman.plugmanc.readOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Tune")
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Stream URL", self.url)
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Audio Quality", self.audio_quality)
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Bitrate", self.video_bitrate)
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Tune", self.video_tune)

def get_widget(self):
if self.widget is None:
self.widget = QtGui.QWidget()
self.widget.setWindowTitle("RTMP Streaming Options")

layout = QtGui.QFormLayout()
self.widget.setLayout(layout)

#
# Stream URL
#

# TODO: URL validation?

self.label_stream_url = QtGui.QLabel("Stream URL")
self.lineedit_stream_url = QtGui.QLineEdit()
layout.addRow(self.label_stream_url, self.lineedit_stream_url)

self.lineedit_stream_url.textEdited.connect(self.set_stream_url)

#
# Audio Quality
#

self.label_audio_quality = QtGui.QLabel("Audio Quality")
self.spinbox_audio_quality = QtGui.QDoubleSpinBox()
self.spinbox_audio_quality.setMinimum(0.0)
self.spinbox_audio_quality.setMaximum(1.0)
self.spinbox_audio_quality.setSingleStep(0.1)
self.spinbox_audio_quality.setDecimals(1)
self.spinbox_audio_quality.setValue(0.3) # Default value 0.3
layout.addRow(self.label_audio_quality, self.spinbox_audio_quality)

self.widget.connect(self.spinbox_audio_quality, QtCore.SIGNAL('valueChanged(double)'), self.set_audio_quality)

#
# Video Quality
#

self.label_video_quality = QtGui.QLabel("Video Quality (kb/s)")
self.spinbox_video_quality = QtGui.QSpinBox()
self.spinbox_video_quality.setMinimum(0)
self.spinbox_video_quality.setMaximum(16777215)
self.spinbox_video_quality.setValue(2400) # Default value 2400
layout.addRow(self.label_video_quality, self.spinbox_video_quality)

self.widget.connect(self.spinbox_video_quality, QtCore.SIGNAL('valueChanged(int)'), self.set_video_bitrate)

#
# Video Tune
#

self.label_video_tune = QtGui.QLabel("Video Tune")
self.combobox_video_tune = QtGui.QComboBox()
self.combobox_video_tune.addItems(self.TUNE_VALUES)
layout.addRow(self.label_video_tune, self.combobox_video_tune)

self.widget.connect(self.combobox_video_tune,
QtCore.SIGNAL('currentIndexChanged(const QString&)'),
self.set_video_tune)

#
# Note
#

self.label_note = QtGui.QLabel("*For RTMP streaming, all other outputs must be set to leaky")
layout.addRow(self.label_note)


return self.widget

def widget_load_config(self, plugman):
self.load_config(plugman)

self.lineedit_stream_url.setText(self.url)

self.spinbox_audio_quality.setValue(float(self.audio_quality))
self.spinbox_video_quality.setValue(int(self.video_bitrate))

tuneIndex = self.combobox_video_tune.findText(self.video_tune)
self.combobox_video_tune.setCurrentIndex(tuneIndex)

def set_stream_url(self, text):
self.url = text
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Stream URL", self.url)
self.plugman.save()

def set_audio_quality(self):
self.audio_quality = self.spinbox_audio_quality.value()
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Audio Quality", str(self.audio_quality))
self.plugman.save()

def set_video_bitrate(self):
self.video_bitrate = self.spinbox_video_quality.value()
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Bitrate", str(self.video_bitrate))
self.plugman.save()

def set_video_tune(self, tune):
self.video_tune = tune
self.plugman.plugmanc.registerOptionFromPlugin(self.CATEGORY, self.get_config_name(), "Video Tune", str(self.video_tune))
self.plugman.save()

def get_properties(self):
return ['StreamURL', 'AudioQuality', 'VideoBitrate', 'VideoTune']

def get_property_value(self, property):
if property == "StreamURL":
return self.url
elif property == "AudioQuality":
return self.audio_quality
elif property == "VideoBitrate":
return self.video_bitrate
elif property == "VideoTune":
return self.video_tune
else:
return "There's no property with such name"

def set_property_value(self, property, value):
if property == "StreamURL":
return self.set_stream_url(value)
elif property == "AudioQuality":
return self.set_audio_quality(value)
elif property == "VideoBitrate":
return self.set_video_bitrate(value)
elif property == "VideoTune":
return self.set_video_tune(value)
else:
return "Error: There's no property with such name"

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Core]
Name = RTMP Streaming
Module = rtmp-streaming

[Documentation]
Author = Jonathan Shen & Mike Chong
Version = 0.1
Website = http://fosslc.org
Description = RTMP Streaming plugin streams video to a RTMP server.
Loading

0 comments on commit 1dae43a

Please sign in to comment.