Gstreamer을 gst-launch로 pipeline으로 연결하여 실행도 가능하지만, Gstreamer을 C나 python을
이용하여 간단히 Programming이 가능하다. 이때 UBUNTU에서 GTK이 이용을 한다.
Gstreamer 관련설명 및 관련 C기반 예제
http://blog.daum.net/basetechnology/6998023
일반 Gstreamer 의 플러그들
https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/
지금 현재 GSTREAM로 PHYTHON을 지원하는 것은 UBUNTU에서 지원을 해준다.
https://wiki.ubuntu.com/Novacut/GStreamer1.0#Using_GStreamer_1.0_from_Python
1.1 Gstreamer Python 준비
아래의 Python을 하기 위해서는 반드시 Python version 3이며, 아래와 같이 설치가 필요하다.
처음 프로그래밍하기 전에 주의해야 할 사항은 다음과 같다.
1. python version 확인
2. 띄어쓰기 문제
3. #!/usr/bin/env python 문제
만약 Ubuntu를 사용한다면 Ubuntu version에따라 Gstreamer version이 지원이 다를수 있으니, 아래 사이트를 참고하자.
gstremer0.10 용 video player 와 gstreamer 1.0 용 video player는 다르다.
(video player 각 예제가 ubuntu에서 동작되는 것을 기본적으로 확인했음)
https://wiki.ubuntu.com/Novacut/GStreamer1.0#Adding_PPA_for_Ubuntu_Precise
1.2 Python 관련기본자료
https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/
지금 현재 GSTREAM로 PHYTHON을 지원하는 것은 UBUNTU에서 지원을 해준다.
https://wiki.ubuntu.com/Novacut/GStreamer1.0#Using_GStreamer_1.0_from_Python
1.1 Gstreamer Python 준비
아래의 Python을 하기 위해서는 반드시 Python version 3이며, 아래와 같이 설치가 필요하다.
$ sudo apt-get install python-gi python3-gi \ gstreamer1.0-tools \ gir1.2-gstreamer-1.0 \ gir1.2-gst-plugins-base-1.0 \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-ugly \ gstreamer1.0-plugins-bad \ gstreamer1.0-libav
처음 프로그래밍하기 전에 주의해야 할 사항은 다음과 같다.
1. python version 확인
2. 띄어쓰기 문제
3. #!/usr/bin/env python 문제
만약 Ubuntu를 사용한다면 Ubuntu version에따라 Gstreamer version이 지원이 다를수 있으니, 아래 사이트를 참고하자.
gstremer0.10 용 video player 와 gstreamer 1.0 용 video player는 다르다.
(video player 각 예제가 ubuntu에서 동작되는 것을 기본적으로 확인했음)
https://wiki.ubuntu.com/Novacut/GStreamer1.0#Adding_PPA_for_Ubuntu_Precise
1.2 Python 관련기본자료
- Python 관련 기본설명
- GSTREAMER-PYTHON TUTORIAL
- Gst-1.0 (Function 과 Hierarchy 로 구조파악)
- Python GOBJECT
- Python GTK
솔직히 Python Gstreamer은 gst-lanuch을 다룬다면, 프로그래밍을 하기가 쉬운거 같다.
아래와 같이 시작 매뉴얼을 익히고, 기본 GTK 이나, QT에 연결하여 하면 되는 것 같다.
다만 문제가 최종동작이 되는지는 보장이 되는지는 모르겠다.
gst-lanuch와 동일하게 Gst.ElementFactory.make() 에 PlugIns의 Element의 정보를 넣으면 된다.
Gstreamer 관련 사항은 Tutorial 에 나와 있지만, 관련사항을 다음과 같이 정리한다.
- Pipeline을 우선 만들고,
- add로 추가한다
- link로 연결한다.
- connect 는 일종의 call back function이다.
manual의 4.3 connection에서 event와 bus 부분을 보면 쉽게 개념을 잘 이해 할 수 있다.
일종의 Timing 맞추기 위해서 사용한다고 보면된다.
아래의 상태는 Gstreamer의 상태를 말하며, pipeline의 상태를 말한다.
Gst.State.PAUSE
Gst.State.PLAYING
GTK관련 Example Manual 보면 Gst.parse_launch 있는데, 사용법이 독특하다.
사용하기도 편한데 아직 다뤄보지 못했다.
1.3 Python Gstremer ex-1
아래의 예제는 위의 pygst-tutorial-org/pygst-tutorial.pdf에서 가져온것이며,실제로 실행본 것이 아니다.
하지만 대충 보면 쉽게 이해가기 때문에 일단 정리한다.
#!/usr/bin/env python import os import gi gi.require_version('Gst', '1.0') from gi.repository import Gst, GObject, Gtk ### GTK Main Class로 구성 및 이기반으로 실행 class GTK_Main(object): def __init__(self): window = Gtk.Window(Gtk.WindowType.TOPLEVEL) window.set_title("Mpeg2-Player") window.set_default_size(500, 400) window.connect("destroy", Gtk.main_quit, "WM destroy") vbox = Gtk.VBox() window.add(vbox) hbox = Gtk.HBox() vbox.pack_start(hbox, False, False, 0) self.entry = Gtk.Entry() hbox.add(self.entry) self.button = Gtk.Button("Start") hbox.pack_start(self.button, False, False, 0) self.button.connect("clicked", self.start_stop) self.movie_window = Gtk.DrawingArea() vbox.add(self.movie_window) window.show_all() self.player = Gst.Pipeline.new("player") source = Gst.ElementFactory.make("filesrc", "file-source") demuxer = Gst.ElementFactory.make("mpegpsdemux", "demuxer") demuxer.connect("pad-added", self.demuxer_callback) # callback function self.video_decoder = Gst.ElementFactory.make("mpeg2dec", "video-decoder") self.audio_decoder = Gst.ElementFactory.make("mad", "audio-decoder") audioconv = Gst.ElementFactory.make("audioconvert", "converter") audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-output") videosink = Gst.ElementFactory.make("autovideosink", "video-output") self.queuea = Gst.ElementFactory.make("queue", "queuea") self.queuev = Gst.ElementFactory.make("queue", "queuev") colorspace = Gst.ElementFactory.make("videoconvert", "colorspace") ####pipleline 추가 self.player.add(source) self.player.add(demuxer) self.player.add(self.video_decoder) self.player.add(self.audio_decoder) self.player.add(audioconv) self.player.add(audiosink) self.player.add(videosink) self.player.add(self.queuea) self.player.add(self.queuev) self.player.add(colorspace) source.link(demuxer) self.queuev.link(self.video_decoder) self.video_decoder.link(colorspace) colorspace.link(videosink) self.queuea.link(self.audio_decoder) self.audio_decoder.link(audioconv) audioconv.link(audiosink) bus = self.player.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() bus.connect("message", self.on_message) bus.connect("sync-message::element", self.on_sync_message) def start_stop(self, w): if self.button.get_label() == "Start": filepath = self.entry.get_text().strip() if os.path.isfile(filepath): filepath = os.path.realpath(filepath) self.button.set_label("Stop") self.player.get_by_name("file-source").set_property("location", filepath) self.player.set_state(Gst.State.PLAYING) else: self.player.set_state(Gst.State.NULL) self.button.set_label("Start") def on_message(self, bus, message): t = message.type if t == Gst.MessageType.EOS: self.player.set_state(Gst.State.NULL) self.button.set_label("Start") elif t == Gst.MessageType.ERROR: err, debug = message.parse_error() print "Error: %s" % err, debug self.player.set_state(Gst.State.NULL) self.button.set_label("Start") def on_sync_message(self, bus, message): if message.get_structure().get_name() == 'prepare-window-handle': imagesink = message.src imagesink.set_property("force-aspect-ratio", True) xid = self.movie_window.get_property('window').get_xid() imagesink.set_window_handle(xid) def demuxer_callback(self, demuxer, pad): if pad.get_property("template").name_template == "video_%02d": qv_pad = self.queuev.get_pad("sink") pad.link(qv_pad) elif pad.get_property("template").name_template == "audio_%02d": qa_pad = self.queuea.get_pad("sink") pad.link(qa_pad) #### #### Main Call Gst.init(None) GTK_Main() GObject.threads_init() Gtk.main()
1.4 Python simple example 수정방법
아래의 example은 video-player-1.0을 기반으로 돌아가는 video-player를 rtsp용으로 본인이 변경한 것이다.
이전에 다른회사에서 요청이 와서 관련부분들을 고쳤는데, 간단한 것만 정리한다.
Jeston TK1/TX1 기반으로 작성한 것이므로, 일반 Ubuntu에서는 동작이 되지 않는다.
아래와 같이 Jeston TK1/TX1에서 gstreamer로 확인한 후, phython으로 아래와 같이 변경하였다.
$ gst-launch-1.0 -v rtspsrc location=rtsp://192.168.1.168:8557/PSIA/Streaming/channels/2?videoCodecType=H.264 caps="video/x-h264,mapping=/video " \ ! rtph264depay ! h264parse ! omxh264dec ! nveglglessink -e
main은 p = Player() p.run() 이며 아래소스와 상위 command 를 보면 대충 쉽게 이해가며 각 필요한 부분들은 자신에 맞게 수정하도록하자
나의 경우는 각 Jetson에 맞게 수정하였으며, 각 Jetson의 Manual을 참조해야함
#!/usr/bin/python3 # http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-1.0 from os import path import gi gi.require_version('Gst', '1.0') from gi.repository import GObject, Gst, Gtk # Needed for window.get_xid(), xvimagesink.set_window_handle(), respectively: from gi.repository import GdkX11, GstVideo GObject.threads_init() Gst.init(None) WORKAROUND_SLEEP_SEC = 0.5 #WORKAROUND_SLEEP_SEC = 0.0 class Player(object): def __init__(self): self.window = Gtk.Window() self.window.connect('destroy', self.quit) self.window.set_default_size(800, 450) self.drawingarea = Gtk.DrawingArea() self.window.add(self.drawingarea) # Create GStreamer pipeline self.pipeline = Gst.Pipeline() # 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) # 상위와 같이 RTSP기반으로 Gst make 및 property 설정 videosource = Gst.ElementFactory.make('rtspsrc', 'videosource') videosource.set_property('location', 'rtsp://192.168.1.168:8557/PSIA/Streaming/channels/2?videoCodecType=H.264') videosource.set_property('latency', 500) #videoqueue = Gst.ElementFactory.make('queue', 'videoqueue') videodepay = Gst.ElementFactory.make('rtph264depay', 'videodepay') videoparser = Gst.ElementFactory.make('h264parse', 'videoparser') videodecoder = Gst.ElementFactory.make('omxh264dec', 'videodecoder') #filesink = Gst.ElementFactory.make('filesink', 'sink') #filesink.set_property('location', r'/home/ubuntu/Downloads/N/test11.mp4') #filesink.set_property('sync', 'false') videosink = Gst.ElementFactory.make('nveglglessink', 'videosink') videosink.set_property('create-window', False) #self.player.add(videosource, videoqueue, videodepay, videoparser,videodecoder,videosink) #Gst.element_link_many(videosource, videoqueue, videodepay, videoparser,videodecoder,videosink) ### Callback Functions 등록 변경 및 테스트할 경우 videosource.connect("pad-added", self.videosource_pad_added) # videodepay.connect("pad-added", self.test2_pad_added) # videoparser.connect("pad-added", self.test3_pad_added) # videodecoder.connect("pad-added", self.videodecoder_pad_added)
### Pipeline self.pipeline.add(videosource) #self.pipeline.add(videoqueue) self.pipeline.add(videodepay) self.pipeline.add(videoparser) self.pipeline.add(videodecoder) self.pipeline.add(videosink) #self.pipelie.add(filesink) ### TEST Callback 과 필요 Callback Functions 추가하며 필요시 디버깅 및 상위에서 등록 def test1_pad_added(self, dbin, pad): print('jhlee-debugmsg connected test1_pad_added!!!!!!!!!!!!!!!!!!!!!!!!!!!1!!!') def test2_pad_added(self, dbin, pad): print('jhlee-debugmsg connected test2_pad_added!!!!!!!!!!!!!!!!!!!!!!!!!!!1!!!') def test3_pad_added(self, dbin, pad): print('jhlee-debugmsg connected test3_pad_added!!!!!!!!!!!!!!!!!!!!!!!!!!!1!!!') def test4_pad_added(self, dbin, pad): print('jhlee-debugmsg connected test4_pad_added!!!!!!!!!!!!!!!!!!!!!!!!!!!1!!!') def videosource_pad_added(self, dbin, pad): print('jhlee-debugmsg connected videosource_pad_added start') #####videosource.link(videodepay) 각 부분 디버깅 pipeline = dbin.get_parent() videodepay = pipeline.get_by_name('videodepay') dbin.link(videodepay) #####videodepay.link(videoparser) videoparser = pipeline.get_by_name('videoparser') videodepay.link(videoparser) ######videoparser.link(videodecoder) videodecoder = pipeline.get_by_name('videodecoder') videoparser.link(videodecoder) videosink = pipeline.get_by_name('videosink') videodecoder.link(videosink) #### PIPELINE STATE 변경 pipeline.set_state(Gst.State.PLAYING) if (WORKAROUND_SLEEP_SEC > 0): sleep(WORKAROUND_SLEEP_SEC) print('jhlee-debugmsg connected videosource_pad_added end') def videodepay_pad_added(self, dbin, pad): print('jhlee-debugmsg connected videodepay_pad_added start') def videodecoder_pad_added(self, dbin, pad): print('jhlee-debugmsg connected videodecoder_pad_added start') pipeline = dbin.get_parent() videosink = pipeline.get_by_name('videosink') # Link decodebin to video sink dbin.link(videosink) pipeline.set_state(Gst.State.PLAYING) // PIPELINE STATE 변경 if (WORKAROUND_SLEEP_SEC > 0): sleep(WORKAROUND_SLEEP_SEC) print('jhlee-debugmsg connected videodecoder_pad_added end') 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('prepare-window-handle') 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()) p = Player() p.run()
1.5 Python 의 API 관련 설명
솔직히 상위 Python은 Python3를 이해하고, Gstreamer를 이해하며 눈짐작으로 대충은 이해하지만 정확한 이해는 하지 못한다.
아래의 각각의 GTK or Gstreamer의 API를 정확히 어떻게 동작하는 지를 그 함수들의 API 설명해주는 곳이
아래 SITE이며 , 이곳에서 각각의 함수의 동작을 알아보자.
https://lazka.github.io/pgi-docs/
1.6 Python 관련 Open Source
python의 opensource를 보다보니, repo source도 좋은 예제인 것 같다.
- QT Example
http://www.jonobacon.org/2006/08/28/getting-started-with-gstreamer-with-python/
- Gstreamer 일반다른예제들 (아래 예제포함)
- Video Player (상위예제)
- Webcam 예제
https://storage.googleapis.com/git-repo-downloads/repo
- NVIDIA의 JETSON 의 RTSP 작업 오픈소스에 반영
이전에 다른회사에서 개발의뢰가 들어와서 이기반으로 소스를 수정하여 다른 회사의 RTSP 문제를 해결 해주었다. (적은돈이 아니라 기쁘다)
mulitmedia.py 기반으로 multimedia-rtsp.py 새로 만들어 아래에 내가 반영했으며, Open Source라서 회사에는 크게 문제는 상관이 없다.