mirror of
https://github.com/albfan/miraclecast.git
synced 2025-02-13 10:11:54 +00:00
since gstplayer has argument --audio to enable audio support but no way to disable it, audio=False might a better choise. If we left audio=True and incoming stream has video only, the whole pipeline will be blocked and wait for audio stream.
226 lines
8.6 KiB
Python
Executable file
226 lines
8.6 KiB
Python
Executable file
#!/usr/bin/python3 -u
|
|
|
|
import gi
|
|
import argparse
|
|
|
|
gi.require_version('Gst', '1.0')
|
|
gi.require_version('Gtk', '3.0')
|
|
gi.require_version('GstVideo', '1.0')
|
|
gi.require_version('GdkX11', '3.0')
|
|
|
|
from gi.repository import GObject, Gst, Gtk, Gdk, GLib
|
|
|
|
# Needed for window.get_xid(), xvimagesink.set_window_handle(), respectively:
|
|
from gi.repository import GdkX11, GstVideo
|
|
|
|
GObject.threads_init()
|
|
Gst.init(None)
|
|
|
|
class Player(object):
|
|
def __init__(self, **kwargs):
|
|
|
|
resolution = kwargs.get("resolution")
|
|
if resolution:
|
|
split = resolution.split("x")
|
|
self.width = int(split[0])
|
|
self.height = int(split[1])
|
|
|
|
scale = kwargs.get("scale")
|
|
if scale:
|
|
split = scale.split("x")
|
|
self.width = int(split[0])
|
|
self.height = int(split[1])
|
|
|
|
debug = kwargs.get("debug")
|
|
if debug:
|
|
Gst.debug_set_active(True)
|
|
Gst.debug_set_threshold_from_string(debug, True)
|
|
|
|
port = kwargs.get("port")
|
|
|
|
uri = kwargs.get("uri")
|
|
|
|
self.window = Gtk.Window()
|
|
self.window.set_name('eco')
|
|
self.window.connect('destroy', self.quit)
|
|
|
|
title = kwargs.get("title")
|
|
if title:
|
|
self.window.set_title(title)
|
|
|
|
self.window.set_default_size(self.width, self.height)
|
|
|
|
self.drawingarea = Gtk.DrawingArea()
|
|
self.window.add(self.drawingarea)
|
|
|
|
self.drawingarea.set_size_request(self.width,self.height)
|
|
self.drawingarea.add_events(Gdk.EventMask.BUTTON_PRESS_MASK|Gdk.EventMask.BUTTON_RELEASE_MASK)
|
|
self.drawingarea.connect('button-press-event', self.on_mouse_pressed)
|
|
self.drawingarea.connect('button-release-event', self.on_mouse_pressed)
|
|
self.drawingarea.add_events(Gdk.EventMask.KEY_PRESS_MASK)
|
|
self.window.connect('key-press-event', self.on_key_pressed)
|
|
|
|
audio = kwargs.get("audio")
|
|
|
|
self.playbin = None
|
|
|
|
#Create GStreamer pipeline
|
|
if uri is not None:
|
|
self.pipeline = Gst.Pipeline()
|
|
|
|
# Create GStreamer elements
|
|
self.playbin = Gst.ElementFactory.make('playbin', "source")
|
|
|
|
if not uri.startswith("http://") or not uri.startswith("http://") or not uri.startswith("file://"):
|
|
if not uri.startswith("/"):
|
|
import os
|
|
uri = os.path.abspath(uri)
|
|
uri = "file://"+uri
|
|
|
|
# Set properties
|
|
self.playbin.set_property('uri', uri)
|
|
|
|
|
|
# Add playbin to the pipeline
|
|
self.pipeline.add(self.playbin)
|
|
else:
|
|
gstcommand = "udpsrc port="+str(port)+" caps=\"application/x-rtp, media=video\" ! rtpjitterbuffer latency=100 ! rtpmp2tdepay ! tsdemux "
|
|
|
|
if audio:
|
|
gstcommand += "name=demuxer demuxer. "
|
|
|
|
gstcommand += "! queue max-size-buffers=0 max-size-time=0 ! h264parse ! avdec_h264 ! videoconvert ! "
|
|
|
|
if scale:
|
|
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
|
|
|
|
gstcommand += "autovideosink "
|
|
|
|
if audio:
|
|
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink "
|
|
|
|
self.pipeline = Gst.parse_launch(gstcommand)
|
|
|
|
|
|
# Create bus to get events from GStreamer pipeline
|
|
self.bus = self.pipeline.get_bus()
|
|
self.bus.add_signal_watch()
|
|
self.bus.connect('message::eos', self.on_eos)
|
|
self.bus.connect('message::error', self.on_error)
|
|
|
|
# This is needed to make the video output in our DrawingArea:
|
|
self.bus.enable_sync_message_emission()
|
|
self.bus.connect('sync-message::element', self.on_sync_message)
|
|
self.bus.connect('message', self.on_message)
|
|
|
|
self.success = False
|
|
|
|
def on_message(self, bus, message):
|
|
if self.playbin:
|
|
videoPad = self.playbin.emit("get-video-pad", 0)
|
|
if videoPad and not self.success:
|
|
videoPadCapabilities = videoPad.get_current_caps()
|
|
if videoPadCapabilities:
|
|
(self.success, self.videoWidth) = \
|
|
videoPadCapabilities.get_structure(0).get_int("width")
|
|
(self.success, self.videoHeight) = \
|
|
videoPadCapabilities.get_structure(0).get_int("height")
|
|
if self.success:
|
|
print("{0} {1}".format(self.videoWidth, self.videoHeight))
|
|
self.drawingarea.set_size_request(self.videoWidth, self.videoHeight)
|
|
|
|
def on_mouse_pressed(self, widget, event):
|
|
#<type>,<count>,<id>,<x>,<y>
|
|
if event.type == Gdk.EventType.BUTTON_PRESS:
|
|
type = 0
|
|
else:
|
|
type = 1
|
|
|
|
width = self.drawingarea.get_allocation().width
|
|
height = self.drawingarea.get_allocation().height
|
|
half_area_width = width / 2
|
|
half_area_height = height / 2
|
|
|
|
half_def_width = self.width / 2
|
|
half_def_height = self.height / 2
|
|
|
|
min_hor_pos = half_area_width - half_def_width
|
|
max_hor_pos = half_area_width + half_def_width
|
|
min_ver_pos = half_area_height - half_def_height
|
|
max_ver_pos = half_area_height + half_def_height
|
|
|
|
pos_event_x = event.x
|
|
pos_event_y = event.y
|
|
|
|
|
|
#print('{0} {1} {2} {3} {4} {5}'.format(min_hor_pos, pos_event_x, max_hor_pos, min_ver_pos, pos_event_y,max_ver_pos))
|
|
if min_hor_pos <= pos_event_x <= max_hor_pos and min_ver_pos <= pos_event_y <= max_ver_pos:
|
|
uibc_x = int(pos_event_x - (half_area_width - half_def_width))
|
|
#print ('{0} {1} {2} {3}'.format(uibc_x, pos_event_x, half_area_width,half_def_width))
|
|
uibc_y = int(pos_event_y - (half_area_height - half_def_height))
|
|
#print ('{0} {1} {2} {3}'.format(uibc_y, pos_event_y, half_area_height,half_def_height))
|
|
print('{0},1,0,{1},{2}'.format(type, uibc_x , uibc_y))
|
|
|
|
def on_key_pressed(self, widget, event):
|
|
#print(Gdk.keyval_name(event.keyval))
|
|
print("3,0x%04X,0x0000" % event.keyval)
|
|
# print(event.state)
|
|
# if event.state & Gdk.ModifierType.LOCK_MASK == Gdk.ModifierType.LOCK_MASK:
|
|
# print("caps lock")
|
|
# print(Gtk.accelerator_get_label(event.keyval, event.state))
|
|
|
|
def run(self):
|
|
self.window.show_all()
|
|
# You need to get the XID after window.show_all(). You shouldn't get it
|
|
# in the on_sync_message() handler because threading issues will cause
|
|
# segfaults there.
|
|
self.xid = self.drawingarea.get_property('window').get_xid()
|
|
self.pipeline.set_state(Gst.State.PLAYING)
|
|
Gtk.main()
|
|
|
|
|
|
def quit(self, window):
|
|
self.pipeline.set_state(Gst.State.NULL)
|
|
Gtk.main_quit()
|
|
|
|
def on_sync_message(self, bus, msg):
|
|
if msg.get_structure().get_name() == 'prepare-window-handle':
|
|
print(self.drawingarea.get_allocation())
|
|
#msg.src.set_property("force-aspect-radio", True)
|
|
msg.src.set_window_handle(self.xid)
|
|
|
|
def on_eos(self, bus, msg):
|
|
print('on_eos(): seeking to start of video')
|
|
self.pipeline.seek_simple(
|
|
Gst.Format.TIME,
|
|
Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
|
|
0
|
|
)
|
|
|
|
def on_error(self, bus, msg):
|
|
print('on_error():', msg.parse_error())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("uri", nargs="?", help="Uri to play")
|
|
parser.add_argument("-v", "--version", help="Show package version")
|
|
parser.add_argument("--log-level", metavar="lvl", help="Maximum level for log messages")
|
|
parser.add_argument("-p", "--port", type=int, default=1991, help="Port for rtsp")
|
|
parser.add_argument("-a", "--audio", dest="audio", action="store_true", help="Enable audio support")
|
|
parser.add_argument("-s", "--scale", metavar="WxH", help="Scale to resolution")
|
|
parser.add_argument("-d", "--debug", help="Debug")
|
|
parser.add_argument("--uibc", help="Enable UIBC")
|
|
parser.add_argument("--title", help="set player title")
|
|
parser.add_argument("--res", metavar="n,n,n", help="Supported resolutions masks (CEA, VESA, HH)")
|
|
# res
|
|
# " default CEA %08X\n"
|
|
# " default VESA %08X\n"
|
|
# " default HH %08X\n"
|
|
parser.add_argument("-r", "--resolution", help="Resolution")
|
|
parser.set_defaults(audio=False)
|
|
args = parser.parse_args()
|
|
|
|
p = Player(**vars(args))
|
|
p.run()
|