GStreamer Daemon - Python Video Player Example
Introduction to GStreamer Daemon Python Video Player Example
This is one concrete usage example of the GStreamer Daemon along with a Python Application. In this case, the application uses the Python GSTD API to communicate with GStreamer Daemon.
The application consists of a Simple Video Player with several capabilities:
- Regular video playback.
- Trick modes (fast-forward, speed change, seek to position).
- Read messages from the GSTD bus.
Usage
To execute, simply run:
python3 simple_pipeline.py $VIDEO_PATH
Within the application, you can use several commands. It supports the following commands:
play: To play and run pause: To pause the video stop: To stop and close the playing set_speed $SPEED [negative or positive floating-point number] jump $TIME [positive number in seconds] exit
Code Analysis
This example application can be divided into 3 sections:
- Init Sequence & Command Parser
- GstcPlayer Class
- Error Handler
Init Sequence & Command Parser
This section is pretty straightforward. You just need to pas the video file path as the only argument.
if __name__ == "__main__": print("Sample PyGstC Video Player") ''' . . . '''
Once the application is started. Automatically, it will open and reproduce the video. On the command prompt, you can specify any of the supported commands.
GstcPlayer Class
This class contains all the necessary calls to communicate with the GStreamer Daemon, through the Python GstdClient.
Firs of all the Python GstdClient needs to be imported with:
from pygstc.gstc import GstcError, GstdError, GstdClient from pygstc.logger import CustomLogger
There are two main objects to manipulate the GstdClient over Python. First the Logger class (CustomLogger) and the GstdClient itself. The logger can have different information levels, for example you can pass an 'ERROR' level.
class GstcPlayer: def __init__(self, videoPath): self.gstd_logger = CustomLogger('simple_playback', loglevel='WARNING') self.gstc = GstdClient(logger=self.gstd_logger)
Once those objects are created. The Python API offers a set of calls to communicate with the GStreamer Daemon. For example, when the play command is executed, the following actions are executed:
def playVideo(self): self.gstc.pipeline_create(self.pipeName, self.pipeline) self.gstc.pipeline_play(self.pipeName)
Let's say you just want to pause the video and continue from that point. Then you can modify the playing function similar to:
def playVideo(self): if (not self.pipe_exists(self.pipeName)): self.gstc.pipeline_create(self.pipeName, self.pipeline) self.gstc.pipeline_play(self.pipeName) def pipe_exists(self, pipe_name): #Check if pipe is already created existing_pipes = self.gstc.list_pipelines() if (existing_pipes == []): ret = False elif( existing_pipes[0]['name'] == pipe_name): ret = True else: ret = False return ret
Then a pause call can be created with:
def pauseVideo(self): self.gstc.pipeline_pause(self.pipeName)
And to use the same play command:
Similar, when the command stop is executed there are some instructions to achieve this:
def stopVideo(self) self.gstc.pipeline_stop(self.pipeName) self.gstc.pipeline_delete(self.pipeName)
The same process was used to implement changes in playback speed, by using:
self.gstc.event_seek(self.pipeName, rate=self.playingSpeed, format=3, flags=1, start_type=1, start=0, end_type=1, end=-1)
You can use the same event_seek call to select a time to jump on the video.
Error Handler
The Python API offers calls to read messages from the GStreamer Daemon Bus. The very basic command to read from the bus is:
resp = self.gstc.bus_read(self.pipeName)
This call does not need any use thread to be executed. It can be called at any moment by the user application.
For the Video Player, this function is contained within a separate thread just to avoid manually reading from the bus with a command. By doing so we achieved a more complete application, and also for the demonstration purposes. This thread function configures the reading from the bus with a timeout, so we can gracefully exit the thread whenever the user demands so. Also, it is possible to filter the messages by specific types, such as errors or warnings.
def errPlayerHandler(self): self.gstc.bus_timeout(self.pipeName, 1000) self.gstc.bus_filter(self.pipeName, "error+eos+warning") if(self.pipe_exists(self.pipeName)): resp = self.gstc.bus_read(self.pipeName) if (resp != None and resp["type"] == "error"): print("Info: Video stopped") self.gstc.pipeline_pause(self.pipeName) self.gstc.pipeline_stop(self.pipeName) elif (resp != None and resp["type"] == "eos"): print("Info: Video stream ended") self.gstc.pipeline_pause(self.pipeName)
With the implemented filters two main actions are detected:
- When the Video finalize reproducing, and then the pipeline is paused.
- If an error occurred the pipeline is stopped.
If you inspect the code, you will find that this function has locking objects to avoid race conditions. But for basic understanding, we presented the essentials instructions from the application.