442 lines
15 KiB
C
442 lines
15 KiB
C
// Copyright (C) 2022 Amlogic, Inc. All rights reserved.
|
|||
|
|
//
|
||
|
|
// All information contained herein is Amlogic confidential.
|
||
|
|
//
|
||
|
|
// This software is provided to you pursuant to Software License
|
||
|
|
// Agreement (SLA) with Amlogic Inc ("Amlogic"). This software may be
|
||
|
|
// used only in accordance with the terms of this agreement.
|
||
|
|
//
|
||
|
|
// Redistribution and use in source and binary forms, with or without
|
||
|
|
// modification is strictly prohibited without prior written permission
|
||
|
|
// from Amlogic.
|
||
|
|
//
|
||
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
|
||
|
|
#include "glib.h"
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <inttypes.h>
|
||
|
|
#include <getopt.h>
|
||
|
|
#include <gst/gst.h>
|
||
|
|
#ifdef SECMEM
|
||
|
|
#include <gst/allocators/gstsecmemallocator.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
GstElement *playbin;
|
||
|
|
GstElement *uridecodebin;
|
||
|
|
GstElement *decbin;
|
||
|
|
GstElement *parsebin;
|
||
|
|
GstElement *videobin;
|
||
|
|
GstElement *audiobin;
|
||
|
|
|
||
|
|
GMainLoop *main_loop;
|
||
|
|
char *uri;
|
||
|
|
char *license_url;
|
||
|
|
char *proxy;
|
||
|
|
int key_type;
|
||
|
|
gboolean need_parser;
|
||
|
|
gint64 pos;
|
||
|
|
GMutex lock;
|
||
|
|
GstAllocator* allocator;
|
||
|
|
} Context;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
char BestEffort_Enabled; // Use best efforts on each setting, but not mandatory
|
||
|
|
int ccACP_Level; // 0=off, 1, 2, 3
|
||
|
|
char DwightCavendish_Enabled;
|
||
|
|
char HDCP_Enabled; // ON = use highest ver available
|
||
|
|
int ccCGMS_A_Level; // 0=Freely, No More, Once, Never
|
||
|
|
char CIT_Analog_Enabled; // HD must be downres on Analog output
|
||
|
|
char CIT_Digital_Enabled; // HD must be downres if no HDCP
|
||
|
|
char DOT_Enabled; // Doesn't allow playback on Analog
|
||
|
|
char Anti_Mirroring_Enabled; // Disable mirroring (e.g. Airplay on iOS)
|
||
|
|
int HDCP_Version; // 0=not required, 1=HDCP 1.4, 2=HDCP 2.2
|
||
|
|
} OutputControlSettings_t;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
char bGlobal; // true if the data is Global Operator Data
|
||
|
|
char data[257]; //256 max plus a char for \0
|
||
|
|
unsigned int dataSize;
|
||
|
|
} OperatorDataSettings_t;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
int status;
|
||
|
|
int urlLen;
|
||
|
|
char *url;
|
||
|
|
} KeyRetrievalStatus_t;
|
||
|
|
|
||
|
|
enum CUSTOM_EVENT_TYPE
|
||
|
|
{
|
||
|
|
CUSTOM_EVENT_UNUSED = 0,
|
||
|
|
CUSTOM_EVENT_KEY_RETRIEVAL_STATUS,
|
||
|
|
CUSTOM_EVENT_OUTPUT_CONTROL,
|
||
|
|
CUSTOM_EVENT_OPERATOR_DATA,
|
||
|
|
};
|
||
|
|
|
||
|
|
static GstAllocator* handle_need_allocator(GstElement *wlcdmi, gboolean is_4k, uint8_t decoder_format, Context *main_ctx)
|
||
|
|
{
|
||
|
|
g_mutex_lock(&main_ctx->lock);
|
||
|
|
if (!main_ctx->allocator) {
|
||
|
|
#ifdef SECMEM
|
||
|
|
main_ctx->allocator = gst_secmem_allocator_new(is_4k, decoder_format);
|
||
|
|
#else
|
||
|
|
main_ctx->allocator = NULL;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
g_mutex_unlock(&main_ctx->lock);
|
||
|
|
return main_ctx->allocator;
|
||
|
|
}
|
||
|
|
|
||
|
|
static guint handle_custom_event(GstElement *wlcdmi, uint32_t type, char *pbEvent, uint32_t cbEvent, Context *main_ctx)
|
||
|
|
{
|
||
|
|
g_mutex_lock(&main_ctx->lock);
|
||
|
|
GST_INFO("%s type %d pbEvent %p cbEvent %d\n", __func__, type, pbEvent, cbEvent);
|
||
|
|
KeyRetrievalStatus_t KR;
|
||
|
|
OutputControlSettings_t *OPC;
|
||
|
|
OperatorDataSettings_t *OD;
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case CUSTOM_EVENT_KEY_RETRIEVAL_STATUS:
|
||
|
|
KR.status = *(int *)(pbEvent);
|
||
|
|
KR.urlLen = *(int *)(pbEvent + 4);
|
||
|
|
KR.url = (char *)(pbEvent + 8);
|
||
|
|
GST_INFO("status %d \n", KR.status);
|
||
|
|
GST_INFO("urlLen %d \n", KR.urlLen);
|
||
|
|
GST_INFO("url %s \n", KR.url);
|
||
|
|
break;
|
||
|
|
case CUSTOM_EVENT_OUTPUT_CONTROL:
|
||
|
|
OPC = (OutputControlSettings_t *)pbEvent;
|
||
|
|
GST_INFO("BestEffort_Enabled %d \n", OPC->BestEffort_Enabled);
|
||
|
|
GST_INFO("ccACP_Level %d \n", OPC->ccACP_Level);
|
||
|
|
GST_INFO("DwightCavendish_Enabled %d \n", OPC->DwightCavendish_Enabled);
|
||
|
|
GST_INFO("HDCP_Enabled %d \n", OPC->HDCP_Enabled);
|
||
|
|
GST_INFO("ccCGMS_A_Level %d \n", OPC->ccCGMS_A_Level);
|
||
|
|
GST_INFO("CIT_Analog_Enabled %d \n", OPC->CIT_Analog_Enabled);
|
||
|
|
GST_INFO("CIT_Digital_Enabled %d \n", OPC->CIT_Digital_Enabled);
|
||
|
|
GST_INFO("DOT_Enabled %d \n", OPC->DOT_Enabled);
|
||
|
|
GST_INFO("Anti_Mirroring_Enabled %d \n", OPC->Anti_Mirroring_Enabled);
|
||
|
|
GST_INFO("HDCP_Version %d \n", OPC->HDCP_Version);
|
||
|
|
break;
|
||
|
|
case CUSTOM_EVENT_OPERATOR_DATA:
|
||
|
|
OD = (OperatorDataSettings_t *)pbEvent;
|
||
|
|
GST_INFO("bGlobal %d \n", OD->bGlobal);
|
||
|
|
GST_INFO("data %s \n", OD->data);
|
||
|
|
GST_INFO("dataSize %d \n", OD->dataSize);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
g_mutex_unlock(&main_ctx->lock);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void on_element_added(GstBin *bin, GstElement *element,
|
||
|
|
gpointer user_data) {
|
||
|
|
Context *ctx;
|
||
|
|
const gchar *name = GST_ELEMENT_NAME(element);
|
||
|
|
const gchar *type = G_OBJECT_TYPE_NAME(element);
|
||
|
|
|
||
|
|
if (NULL == user_data) {
|
||
|
|
GST_ERROR("user_data is null.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
ctx = (Context *)user_data;
|
||
|
|
GST_DEBUG("New element added to %s : %s", GST_ELEMENT_NAME(bin), name);
|
||
|
|
|
||
|
|
if (g_strrstr(name, "uridecodebin")) {
|
||
|
|
if (ctx->uridecodebin != element) {
|
||
|
|
g_signal_connect(element, "element-added", G_CALLBACK(on_element_added), ctx);
|
||
|
|
}
|
||
|
|
ctx->uridecodebin = element;
|
||
|
|
} else if (g_strrstr(name, "decodebin")) {
|
||
|
|
if (ctx->decbin != element) {
|
||
|
|
g_signal_connect(element, "element-added", G_CALLBACK(on_element_added), ctx);
|
||
|
|
}
|
||
|
|
ctx->decbin = element;
|
||
|
|
// serial property case hls dv case frame dropped when doing resolution
|
||
|
|
// changed g_object_set (play->db, "serial-switch", TRUE, NULL);
|
||
|
|
} else if (g_strrstr(name, "parsebin")) {
|
||
|
|
if (ctx->parsebin != element) {
|
||
|
|
g_signal_connect(element, "element-added", G_CALLBACK(on_element_added), ctx);
|
||
|
|
}
|
||
|
|
ctx->parsebin = element;
|
||
|
|
} else if (g_strrstr(name, "vbin")) {
|
||
|
|
if (ctx->videobin != element) {
|
||
|
|
g_signal_connect(element, "element-added", G_CALLBACK(on_element_added), ctx);
|
||
|
|
}
|
||
|
|
ctx->videobin = element;
|
||
|
|
} else if (g_strrstr(name, "abin")) {
|
||
|
|
if (ctx->audiobin != element) {
|
||
|
|
g_signal_connect(element, "element-added", G_CALLBACK(on_element_added), ctx);
|
||
|
|
}
|
||
|
|
ctx->audiobin = element;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void on_element_setup (GstElement *playbin, GstElement *element, Context *main_ctx)
|
||
|
|
{
|
||
|
|
GstElementFactory *f = gst_element_get_factory(element);
|
||
|
|
if (!f)
|
||
|
|
return;
|
||
|
|
if (!strcmp(GST_OBJECT_NAME (f), "wlcdmi")) {
|
||
|
|
g_signal_connect (G_OBJECT(element), "need-allocator", (GCallback) handle_need_allocator, main_ctx);
|
||
|
|
{
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
g_value_init(&value, G_TYPE_BOOLEAN);
|
||
|
|
g_value_set_boolean(&value, TRUE);
|
||
|
|
g_object_set_property(G_OBJECT(element), "external-allocator", &value);
|
||
|
|
g_value_unset(&value);
|
||
|
|
}
|
||
|
|
g_signal_connect (G_OBJECT(element), "custom-event", (GCallback) handle_custom_event, main_ctx);
|
||
|
|
if (main_ctx->license_url) {
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
GST_INFO("set license url %s\n", main_ctx->license_url);
|
||
|
|
g_value_init(&value, G_TYPE_STRING);
|
||
|
|
g_value_set_string(&value, main_ctx->license_url);
|
||
|
|
g_object_set_property(G_OBJECT(element), "license-url", &value);
|
||
|
|
g_value_unset(&value);
|
||
|
|
}
|
||
|
|
if (main_ctx->proxy) {
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
GST_INFO("set proxy %s\n", main_ctx->proxy);
|
||
|
|
g_value_init(&value, G_TYPE_STRING);
|
||
|
|
g_value_set_string(&value, main_ctx->proxy);
|
||
|
|
g_object_set_property(G_OBJECT(element), "proxy", &value);
|
||
|
|
g_value_unset (&value);
|
||
|
|
}
|
||
|
|
if (main_ctx->key_type) {
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
GST_INFO("set key_type %d\n", main_ctx->key_type);
|
||
|
|
g_value_init(&value, G_TYPE_UINT);
|
||
|
|
g_value_set_uint(&value, main_ctx->key_type);
|
||
|
|
g_object_set_property(G_OBJECT(element), "key-type", &value);
|
||
|
|
g_value_unset(&value);
|
||
|
|
}
|
||
|
|
if (main_ctx->need_parser) {
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
GST_INFO("set need_parser TRUE\n");
|
||
|
|
g_value_init(&value, G_TYPE_BOOLEAN);
|
||
|
|
g_value_set_boolean(&value, TRUE);
|
||
|
|
g_object_set_property(G_OBJECT(element), "need-parser", &value);
|
||
|
|
g_value_unset(&value);
|
||
|
|
}
|
||
|
|
} else if (!strcmp(GST_OBJECT_NAME (f), "hlsdemux")) {
|
||
|
|
GValue value = G_VALUE_INIT;
|
||
|
|
g_value_init(&value, G_TYPE_BOOLEAN);
|
||
|
|
g_value_set_boolean(&value, TRUE);
|
||
|
|
g_object_set_property(G_OBJECT(element), "use-hw-decrypt", &value);
|
||
|
|
g_value_unset(&value);
|
||
|
|
} else if (g_strrstr(GST_ELEMENT_NAME(element), "dec") && g_strrstr(GST_ELEMENT_NAME(element), "v4l2")) {
|
||
|
|
g_object_set (element, "enable-nr", TRUE, NULL);
|
||
|
|
g_object_set (element, "local-buffer-pool", TRUE, NULL);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void handle_message (GstBus *bus, GstMessage *msg, Context *main_ctx)
|
||
|
|
{
|
||
|
|
GError *err = NULL;
|
||
|
|
gchar *debug_info = NULL;
|
||
|
|
switch (GST_MESSAGE_TYPE (msg)) {
|
||
|
|
case GST_MESSAGE_ASYNC_DONE:
|
||
|
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (main_ctx->playbin),
|
||
|
|
GST_DEBUG_GRAPH_SHOW_ALL, "gst-play.async-done");
|
||
|
|
break;
|
||
|
|
case GST_MESSAGE_ERROR:
|
||
|
|
gst_message_parse_error(msg, &err, &debug_info);
|
||
|
|
GST_ERROR("Error received from element %s: %s\n",
|
||
|
|
GST_OBJECT_NAME(msg->src), err->message);
|
||
|
|
GST_ERROR("Debugging information: %s\n",
|
||
|
|
debug_info ? debug_info : "none");
|
||
|
|
g_clear_error(&err);
|
||
|
|
g_free(debug_info);
|
||
|
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (main_ctx->playbin),
|
||
|
|
GST_DEBUG_GRAPH_SHOW_ALL, "gst-play.error");
|
||
|
|
g_main_loop_quit (main_ctx->main_loop);
|
||
|
|
break;
|
||
|
|
case GST_MESSAGE_EOS:
|
||
|
|
GST_ERROR("EOS");
|
||
|
|
g_main_loop_quit (main_ctx->main_loop);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SECMEM
|
||
|
|
static gboolean print_free_secmem(gpointer user_data)
|
||
|
|
{
|
||
|
|
Context *main_ctx = (Context *)user_data;
|
||
|
|
gint free_size = gst_secmem_check_free_buf_size(main_ctx->allocator);
|
||
|
|
GST_DEBUG("free secmem: %d\n", free_size);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static gboolean playback_timer (gpointer user_data)
|
||
|
|
{
|
||
|
|
Context *main_ctx = (Context *)user_data;
|
||
|
|
gst_element_query_position (main_ctx->playbin, GST_FORMAT_TIME, &main_ctx->pos);
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void videosink_buffer_underflow(Context *ctx)
|
||
|
|
{
|
||
|
|
(void *)ctx;
|
||
|
|
GST_WARNING("buffer underflow\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
static void videosink_first_frame(Context *ctx)
|
||
|
|
{
|
||
|
|
(void *)ctx;
|
||
|
|
GST_WARNING("first video frame\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
int playback(Context *main_ctx)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
GstStateChangeReturn res;
|
||
|
|
GstElement *audiosink;
|
||
|
|
guint watcher = 0;
|
||
|
|
guint timer = 0;
|
||
|
|
GstElement *videosink;
|
||
|
|
const gchar *sinkname = NULL;
|
||
|
|
const char *video_sink = "westerossink";
|
||
|
|
|
||
|
|
main_ctx->pos = GST_CLOCK_TIME_NONE;
|
||
|
|
main_ctx->playbin = gst_element_factory_make ("playbin", "playbin");
|
||
|
|
|
||
|
|
if (!main_ctx->playbin) {
|
||
|
|
GST_ERROR("playbin create failed");
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
sinkname = g_getenv ("GST_CFG_VIDEO_SINK");
|
||
|
|
if (sinkname)
|
||
|
|
{
|
||
|
|
if (strstr(sinkname, "westerossink"))
|
||
|
|
video_sink = "westerossink";
|
||
|
|
else if(strstr(sinkname, "amlvideosink"))
|
||
|
|
video_sink = "amlvideosink";
|
||
|
|
else if(strstr(sinkname, "clutterautovideosink"))
|
||
|
|
video_sink = "clutterautovideosink";
|
||
|
|
else
|
||
|
|
video_sink = "westerossink";
|
||
|
|
}
|
||
|
|
videosink = gst_element_factory_make (video_sink, "video-sink");
|
||
|
|
if (!videosink) {
|
||
|
|
GST_ERROR("videosink create failed");
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
g_signal_connect_swapped(videosink, "buffer-underflow-callback", G_CALLBACK(videosink_buffer_underflow), main_ctx);
|
||
|
|
g_signal_connect_swapped(videosink, "first-video-frame-callback", G_CALLBACK(videosink_first_frame), main_ctx);
|
||
|
|
g_object_set (main_ctx->playbin, "video-sink", videosink, NULL);
|
||
|
|
|
||
|
|
audiosink = gst_element_factory_make ("audiosink", "audio-sink");
|
||
|
|
// g_object_set (audiosink, "sync", TRUE, NULL);
|
||
|
|
g_object_set (main_ctx->playbin, "audio-sink", audiosink, NULL);
|
||
|
|
|
||
|
|
g_signal_connect(main_ctx->playbin, "element-added", G_CALLBACK(on_element_added), main_ctx);
|
||
|
|
g_signal_connect (main_ctx->playbin, "element-setup", G_CALLBACK (on_element_setup), main_ctx);
|
||
|
|
g_object_set (main_ctx->playbin, "uri", main_ctx->uri, NULL);
|
||
|
|
|
||
|
|
watcher = gst_bus_add_watch (GST_ELEMENT_BUS (main_ctx->playbin), (GstBusFunc)handle_message, main_ctx);
|
||
|
|
res = gst_element_set_state(main_ctx->playbin, GST_STATE_PLAYING);
|
||
|
|
if (res == GST_STATE_CHANGE_FAILURE) {
|
||
|
|
GST_ERROR("playback failed");
|
||
|
|
gst_object_unref(main_ctx->playbin);
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
main_ctx->main_loop = g_main_loop_new (NULL, FALSE);
|
||
|
|
timer = g_timeout_add (100, playback_timer, main_ctx);
|
||
|
|
#ifdef SECMEM
|
||
|
|
g_timeout_add(1000, print_free_secmem, main_ctx);
|
||
|
|
#endif
|
||
|
|
g_main_loop_run (main_ctx->main_loop);
|
||
|
|
ret = 0;
|
||
|
|
|
||
|
|
exit:
|
||
|
|
if (main_ctx->main_loop) {
|
||
|
|
g_main_loop_unref(main_ctx->main_loop);
|
||
|
|
main_ctx->main_loop = NULL;
|
||
|
|
}
|
||
|
|
if (main_ctx->playbin) {
|
||
|
|
gst_element_set_state(main_ctx->playbin, GST_STATE_NULL);
|
||
|
|
g_source_remove(watcher);
|
||
|
|
gst_object_unref(main_ctx->playbin);
|
||
|
|
main_ctx->playbin = NULL;
|
||
|
|
}
|
||
|
|
if (main_ctx->allocator) {
|
||
|
|
gst_object_unref(main_ctx->allocator);
|
||
|
|
main_ctx->allocator = NULL;
|
||
|
|
}
|
||
|
|
if (timer)
|
||
|
|
g_source_remove(timer);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char *argv[])
|
||
|
|
{
|
||
|
|
Context main_ctx = {0};
|
||
|
|
int opt;
|
||
|
|
gboolean endurance_test = FALSE;
|
||
|
|
|
||
|
|
g_mutex_init(&main_ctx.lock);
|
||
|
|
gst_init (&argc, &argv);
|
||
|
|
|
||
|
|
for (int i = 0; i < argc; i++) {
|
||
|
|
gst_print("%s\n", argv[i]);
|
||
|
|
}
|
||
|
|
while ((opt = getopt(argc, argv, "l:p:t:e")) != -1) {
|
||
|
|
switch (opt) {
|
||
|
|
case 'l':
|
||
|
|
main_ctx.license_url = optarg;
|
||
|
|
break;
|
||
|
|
case 'p':
|
||
|
|
main_ctx.proxy = optarg;
|
||
|
|
break;
|
||
|
|
case 't':
|
||
|
|
main_ctx.key_type = atoi(optarg);
|
||
|
|
break;
|
||
|
|
case 'e':
|
||
|
|
endurance_test = TRUE;
|
||
|
|
break;
|
||
|
|
case 'r':
|
||
|
|
main_ctx.need_parser = TRUE;
|
||
|
|
break;
|
||
|
|
case '?':
|
||
|
|
default:
|
||
|
|
gst_print("Usage: %s [-e] [-l license_server] [-p proxy] [-t key_type] [-r need_parser] uri\n", argv[0]);
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (optind == argc) {
|
||
|
|
gst_print("No file to play\n");
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
} else {
|
||
|
|
main_ctx.uri = argv[optind];
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
main_ctx.pos = 0;
|
||
|
|
playback(&main_ctx);
|
||
|
|
if (GST_CLOCK_TIME_IS_VALID(main_ctx.pos)) {
|
||
|
|
gst_print("played %" PRIu64 ".%09" PRIu64 "\n", main_ctx.pos / GST_SECOND, main_ctx.pos % GST_SECOND);
|
||
|
|
} else {
|
||
|
|
gst_print("played 0\n");
|
||
|
|
}
|
||
|
|
} while(endurance_test);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|