Files
2026-08-24 10:03:24 +08:00

1511 lines
38 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2017 Amlogic Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSEERROR_CODE_INVALID_OPERATION.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* DESCRIPTION
* This file implements a adaptor of audio decoder from Amlogic.
*
*/
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <gst/gst.h>
#include <stdbool.h>
#include "discoverer.h"
// #include <gst/audio/streamvolume.h>
#define DEBUG
#define GST_DEBUG_LEVEL "3,amltspasink:5"
#define GST_AUDIO_SINK "amltspasink"
#define GST_VIDEO_SINK "amltspvsink"
#define ERROR_CODE_OK 0
#define ERROR_CODE_BAD_PARAMETER -1
#define ERROR_CODE_INVALID_OPERATION -2
#define ERROR_CODE_BASE_ERROR -3
#ifdef DEBUG
#define LOG(fmt, arg...) fprintf(stdout, "[gst_test] %s:%d, " fmt, __FUNCTION__, __LINE__, ##arg);
#else
#define LOG(fmt, arg...)
#endif
#define PLAY_STATUS_NORMAL 0
#define PLAY_STATUS_STARTED 1
#define PLAY_STATUS_FINISHED 2
#define PLAY_STATUS_WARNING 3
#define PLAY_STATUS_ERROR 4
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int play_status = PLAY_STATUS_NORMAL;
int previous_play_status = PLAY_STATUS_NORMAL;
void *thread_run(void *arg)
{
GMainLoop *loop = (GMainLoop *)arg;
if (loop == NULL)
{
pthread_exit((void *)"loop is null");
}
g_main_loop_run(loop);
pthread_exit((void *)"return ok");
}
int create_main_loop(GMainLoop **loop, pthread_t *thread_id)
{
GMainLoop *local_loop = NULL;
if (loop == NULL || thread_id == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
local_loop = g_main_loop_new(NULL, FALSE);
if (local_loop == NULL)
{
LOG("create loop failed!\n");
return ERROR_CODE_BASE_ERROR;
}
if (pthread_create(thread_id, NULL, thread_run, local_loop))
{
LOG("create thread failed!\n");
g_main_loop_unref(local_loop);
return ERROR_CODE_BASE_ERROR;
}
*loop = local_loop;
return ERROR_CODE_OK;
}
int release_main_loop(GMainLoop *loop, pthread_t thread_id)
{
void *thread_return = NULL;
if (loop != NULL)
{
g_main_loop_quit(loop);
g_main_loop_unref(loop);
}
if (pthread_join(thread_id, &thread_return))
{
LOG("exit thread failed!\n");
return ERROR_CODE_BASE_ERROR;
}
LOG("thread exit: %s\n", (char *)thread_return);
return ERROR_CODE_OK;
}
int parse_file(const char *file, int32_t *has_audio,
int32_t *has_video)
{
if (file == NULL || has_audio == NULL || has_video == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
void *hdl = NULL;
char uri[1024] = {0};
snprintf(uri, sizeof(uri), "file://%s", file);
discoverer_create(&hdl);
discoverer_run(hdl, uri);
*has_video = discoverer_has_video(hdl);
*has_audio = discoverer_has_audio(hdl);
discoverer_destroy(hdl);
return ERROR_CODE_OK;
}
int create_pipeline(const char *file, GstElement **pipeline)
{
char cmd[256] = {0};
GError *error = NULL;
int32_t has_audio = 0;
int32_t has_video = 0;
if (file == NULL || pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
parse_file(file, &has_audio, &has_video);
if ((-1 == has_audio) || (-1 == has_video))
{
LOG("parse_file failed, has_audio:%d, has_video:%d!\n", has_audio, has_video);
return ERROR_CODE_BASE_ERROR;
}
sprintf(cmd,
"GST_DEBUG=\"%s\" playbin uri=file://%s %s %s",
GST_DEBUG_LEVEL, file,
has_audio ? "audio-sink=" GST_AUDIO_SINK : "",
has_video ? "video-sink=" GST_VIDEO_SINK : "");
gst_init(NULL, NULL);
*pipeline = gst_parse_launch(cmd, &error);
if (*pipeline == NULL)
{
LOG("create pipeline failed! cmd: %s, error: %s\n",
cmd, error ? error->message : "unknown");
return ERROR_CODE_BASE_ERROR;
}
return ERROR_CODE_OK;
}
int release_pipeline(GstElement *pipeline)
{
if (pipeline != NULL)
{
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
}
return ERROR_CODE_OK;
}
int signal_play_status(int new_status)
{
pthread_mutex_lock(&lock);
previous_play_status = play_status;
play_status = new_status;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
return ERROR_CODE_OK;
}
int revert_error_status()
{
pthread_mutex_lock(&lock);
if (play_status == PLAY_STATUS_ERROR)
{
play_status = previous_play_status;
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
return ERROR_CODE_OK;
}
int wait_for_status(int status)
{
pthread_mutex_lock(&lock);
while (!(play_status & status))
{
pthread_cond_wait(&cond, &lock);
}
pthread_mutex_unlock(&lock);
return ERROR_CODE_OK;
}
int check_is_status(int status)
{
return play_status & status;
}
gboolean handle_message(GstBus *bus, GstMessage *message, gpointer data)
{
if (message == NULL)
{
LOG("receive null message!\n");
return TRUE;
}
switch (GST_MESSAGE_TYPE(message))
{
case GST_MESSAGE_WARNING:
{
GError *warning = NULL;
gchar *debug_info = NULL;
signal_play_status(PLAY_STATUS_WARNING);
gst_message_parse_warning(message, &warning, &debug_info);
LOG("receive warning, %s: %s\n",
GST_OBJECT_NAME(message->src), warning->message);
LOG("debugging info: %s\n", debug_info ? debug_info : "none");
g_clear_error(&warning);
g_free(debug_info);
break;
}
case GST_MESSAGE_ERROR:
{
GError *err = NULL;
gchar *debug_info = NULL;
signal_play_status(PLAY_STATUS_ERROR);
gst_message_parse_error(message, &err, &debug_info);
LOG("receive error, %s: %s\n",
GST_OBJECT_NAME(message->src), err->message);
LOG("debugging info: %s\n", debug_info ? debug_info : "none");
g_clear_error(&err);
g_free(debug_info);
break;
}
case GST_MESSAGE_EOS:
{
signal_play_status(PLAY_STATUS_FINISHED);
LOG("receive End-Of-Stream\n");
break;
}
case GST_MESSAGE_STATE_CHANGED:
{
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(message, &old_state,
&new_state, &pending_state);
if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_PLAYING)
{
signal_play_status(PLAY_STATUS_STARTED);
}
/*
LOG("state changed: %s ---> %s\n",
gst_element_state_get_name (old_state),
gst_element_state_get_name (new_state));
*/
break;
}
case GST_MESSAGE_DURATION_CHANGED:
{
// GstFormat format;
// gint64 duration_ns;
// gst_message_parse_duration(message, &format, &duration_ns);
// LOG("receive duration: %lld ms, type: %s\n",
// duration_ns, gst_format_get_name(format));
break;
}
case GST_MESSAGE_BUFFERING:
{
gint percent = 0;
gst_message_parse_buffering(message, &percent);
LOG("receive bufffering: %d ms\n", percent);
break;
}
case GST_MESSAGE_STREAM_START:
{
// GstStreamStatusType status_type;
// gst_message_parse_stream_status(message, &status_type, NULL);
// LOG("receive stream start, status: %d\n", status_type);
break;
}
case GST_MESSAGE_STREAMS_SELECTED:
{
guint index = 0;
guint streams_num = 0;
guint collection_num = 0;
GstStreamCollection *collection = NULL;
GstStream *stream = NULL;
GstStructure *caps_structure = NULL;
GstCaps *caps = NULL;
const gchar *name = NULL;
gint width = 0;
gint height = 0;
LOG("receive stream info:\n");
streams_num = gst_message_streams_selected_get_size(message);
for (index = 0; index < streams_num; index++)
{
GstStream *stream = gst_message_streams_selected_get_stream(message, index);
LOG("stream %u: %s %s\n", index,
gst_stream_type_get_name(gst_stream_get_stream_type(stream)),
gst_stream_get_stream_id(stream));
}
gst_message_parse_streams_selected(message, &collection);
collection_num = gst_stream_collection_get_size(collection);
for (index = 0; index < collection_num; index++)
{
stream = gst_stream_collection_get_stream(collection, index);
caps = gst_stream_get_caps(stream);
caps_structure = gst_caps_get_structure(caps, 0);
if (gst_stream_get_stream_type(stream) == GST_STREAM_TYPE_VIDEO)
{
gst_structure_get_int(caps_structure, "width", &width);
gst_structure_get_int(caps_structure, "height", &height);
LOG("video width: %d, height: %d\n", width, height);
}
else
{
name = gst_structure_get_name(caps_structure);
LOG("name: %s\n", name);
}
}
break;
}
case GST_MESSAGE_TAG:
{
GstTagList *tag_list;
gchar *value;
gst_message_parse_tag(message, &tag_list);
if (gst_tag_list_get_string(tag_list, GST_TAG_TITLE, &value))
{
LOG("receive tag-title: %s\n", value);
g_free(value);
}
else if (gst_tag_list_get_string(tag_list, GST_TAG_AUDIO_CODEC, &value))
{
// LOG("receive tag-audio codec: %s\n", value);
g_free(value);
}
else if (gst_tag_list_get_string(tag_list, GST_TAG_VIDEO_CODEC, &value))
{
// LOG("receive tag-video codec: %s\n", value);
g_free(value);
}
else if (gst_tag_list_get_string(tag_list, GST_TAG_BITRATE, &value))
{
LOG("receive tag-bitrate: %s\n", value);
g_free(value);
}
else if (gst_tag_list_get_string(tag_list, GST_TAG_LANGUAGE_CODE, &value))
{
LOG("receive tag-language: %s\n", value);
g_free(value);
}
else
{
// LOG("receive tags from %s\n", GST_OBJECT_NAME (message->src));
}
gst_tag_list_unref(tag_list);
break;
}
default:
{
// LOG("receive msg: %s\n", GST_MESSAGE_TYPE_NAME(message));
break;
}
}
return TRUE;
}
int add_message_watch(GstElement *pipeline, guint *watch_id)
{
GstBus *bus = NULL;
if (pipeline == NULL || watch_id == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
bus = gst_element_get_bus(pipeline);
*watch_id = gst_bus_add_watch(bus, handle_message, NULL);
gst_object_unref(bus);
return ERROR_CODE_OK;
}
int remove_message_watch(guint watch_id)
{
g_source_remove(watch_id);
return ERROR_CODE_OK;
}
int create(const char *file, GMainLoop **loop,
pthread_t *thread_id, GstElement **pipeline, guint *watch_id)
{
int ret = ERROR_CODE_OK;
ret = create_main_loop(loop, thread_id);
if (ret != ERROR_CODE_OK)
{
return ret;
}
ret = create_pipeline(file, pipeline);
if (ret != ERROR_CODE_OK)
{
goto release_main_loop_point;
}
signal_play_status(PLAY_STATUS_NORMAL);
ret = add_message_watch(*pipeline, watch_id);
if (ret != ERROR_CODE_OK)
{
goto release_pipeline_point;
}
return ERROR_CODE_OK;
release_pipeline_point:
release_pipeline(*pipeline);
*pipeline = NULL;
release_main_loop_point:
release_main_loop(*loop, *thread_id);
*loop = NULL;
*thread_id = 0;
return ret;
}
int release(GMainLoop *loop, pthread_t thread_id,
GstElement *pipeline, guint watch_id)
{
remove_message_watch(watch_id);
release_pipeline(pipeline);
release_main_loop(loop, thread_id);
return ERROR_CODE_OK;
}
int start(GstElement *pipeline)
{
GstStateChangeReturn ret;
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
/* Start playing */
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE)
{
LOG("start failed!\n");
return ERROR_CODE_BASE_ERROR;
}
return ERROR_CODE_OK;
}
int pause_(GstElement *pipeline)
{
GstStateChangeReturn ret;
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
/* Start playing */
ret = gst_element_set_state(pipeline, GST_STATE_PAUSED);
if (ret == GST_STATE_CHANGE_FAILURE)
{
LOG("start failed!\n");
return ERROR_CODE_BASE_ERROR;
}
return ERROR_CODE_OK;
}
int stop(GstElement *pipeline)
{
GstStateChangeReturn ret;
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
/* Start playing */
ret = gst_element_set_state(pipeline, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_FAILURE)
{
LOG("start failed!\n");
return ERROR_CODE_BASE_ERROR;
}
signal_play_status(PLAY_STATUS_FINISHED);
return ERROR_CODE_OK;
}
int seek(GstElement *pipeline, gdouble rate, int64_t position_ns)
{
GstQuery *query;
gboolean seekable = FALSE;
gint64 start = position_ns;
gint64 stop = GST_CLOCK_TIME_NONE;
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
query = gst_query_new_seeking(GST_FORMAT_TIME);
if (!gst_element_query(pipeline, query))
{
gst_query_unref(query);
LOG("query failed!\n");
return ERROR_CODE_BASE_ERROR;
}
gst_query_parse_seeking(query, NULL, &seekable, NULL, NULL);
gst_query_unref(query);
if (!seekable)
{
LOG("media is unseekable!\n");
return ERROR_CODE_INVALID_OPERATION;
}
if (rate < 0)
{
start = 0;
stop = position_ns;
}
if (!gst_element_seek(pipeline,
rate, GST_FORMAT_TIME,
(GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
GST_SEEK_TYPE_SET, start,
GST_SEEK_TYPE_SET, stop))
{
LOG("seek failed!\n");
return ERROR_CODE_BASE_ERROR;
};
/*
if (!gst_element_seek_simple(pipeline,
GST_FORMAT_TIME,
GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
position_ns)) {
LOG("seek failed!\n");
return ERROR_CODE_BASE_ERROR;
};
*/
return ERROR_CODE_OK;
}
int set_rate(GstElement *pipeline, gdouble new_rate)
{
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
if (!gst_element_seek(pipeline,
new_rate, GST_FORMAT_TIME,
// GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
// GST_SEEK_FLAG_FLUSH,
GST_SEEK_FLAG_NONE,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
{
pthread_mutex_unlock(&lock);
LOG("set rate failed!\n");
return ERROR_CODE_BASE_ERROR;
};
return ERROR_CODE_OK;
}
int get_duration(GstElement *pipeline, int64_t *duration_ns)
{
if (pipeline == NULL || duration_ns == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
if (!gst_element_query_duration(pipeline,
GST_FORMAT_TIME, duration_ns))
{
LOG("get duration failed!\n");
return ERROR_CODE_BASE_ERROR;
}
return ERROR_CODE_OK;
}
int get_position(GstElement *pipeline, int64_t *position_ns)
{
if (pipeline == NULL || position_ns == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
if (!gst_element_query_position(pipeline,
GST_FORMAT_TIME, position_ns))
{
LOG("get position failed!\n");
return ERROR_CODE_BASE_ERROR;
}
return ERROR_CODE_OK;
}
int get_tracks_num(const char *track, GstElement *pipeline,
gint *num)
{
char name[16] = {0};
if (track == NULL || pipeline == NULL || num == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
sprintf(name, "n-%s", track);
g_object_get(pipeline, name, num, NULL);
return ERROR_CODE_OK;
}
int get_audio_tracks_num(GstElement *pipeline, gint *num)
{
return get_tracks_num("audio", pipeline, num);
}
int set_track_index(const char *track, GstElement *pipeline,
gint index)
{
char name[16] = {0};
if (track == NULL || pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
sprintf(name, "current-%s", track);
g_object_set(pipeline, name, index, NULL);
return ERROR_CODE_OK;
}
int set_audio_track_index(GstElement *pipeline, gint index)
{
return set_track_index("audio", pipeline, index);
}
int set_volume(GstElement *pipeline, gdouble volume)
{
GstElement *element = NULL;
if (pipeline == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
// g_object_set(pipeline, "volume", volume, NULL);
// gst_stream_volume_set_volume (GST_STREAM_VOLUME (pipeline),
// GST_STREAM_VOLUME_FORMAT_CUBIC, volume); // gstreamer-audio-1.0
g_object_get(pipeline, "audio-sink", &element, NULL);
if (element == NULL)
{
LOG("can not get %s!\n", GST_AUDIO_SINK);
return ERROR_CODE_INVALID_OPERATION;
}
gst_object_ref(element);
g_object_set(element, "volume", (int)volume, NULL);
gst_object_unref(element);
return ERROR_CODE_OK;
}
int wait_for_start()
{
return wait_for_status(PLAY_STATUS_STARTED | PLAY_STATUS_ERROR | PLAY_STATUS_FINISHED);
}
int wait_for_finish()
{
return wait_for_status(PLAY_STATUS_ERROR | PLAY_STATUS_FINISHED);
}
int play_is_started()
{
return check_is_status(PLAY_STATUS_STARTED);
}
int play_is_error()
{
return check_is_status(PLAY_STATUS_ERROR);
}
//------------------------------------------------------
#define END NULL
#define PLAY_CMD_PLAY "play"
#define PLAY_CMD_START "start"
#define PLAY_CMD_PAUSE "pause"
#define PLAY_CMD_STOP "stop"
#define PLAY_CMD_SET_RATE "set_rate"
#define PLAY_CMD_SET_VOLUME "set_volume"
#define PLAY_CMD_SEEK_NS "seek_ns"
#define PLAY_CMD_SWITCH_AUDIO_TRACK "switch_audio_index"
#define PLAY_CMD_GET_DURATION_NS "get_duration_ns"
#define PLAY_CMD_GET_POSITION_NS "get_position_ns"
#define PLAY_CMD_SLEEP_US "sleep_us"
#define PLAY_CMD_WAIT_FOR_START "wait_for_start"
#define PLAY_CMD_WAIT_FOR_FINISH "wait_for_finish"
int check_error(int ret, int32_t enabled)
{
if (!enabled)
{
return ERROR_CODE_OK;
}
if (ret != ERROR_CODE_OK || play_is_error())
{
char input = 0;
int32_t c_char_hit_num = 0;
int32_t c_char_hit_max_num = 3;
int32_t e_char_hit_num = 0;
int32_t e_char_hit_max_num = 3;
LOG("find error! "
"input 'c' %d times to continue, "
"input 'e' %d times to eixt !\n",
c_char_hit_max_num, e_char_hit_max_num);
while (true)
{
input = getchar();
switch (input)
{
case 'c':
c_char_hit_num++;
e_char_hit_num = 0;
if (c_char_hit_num >= c_char_hit_max_num)
{
revert_error_status();
return ERROR_CODE_OK;
}
break;
case 'e':
c_char_hit_num = 0;
e_char_hit_num++;
if (e_char_hit_num >= e_char_hit_max_num)
{
return ERROR_CODE_BAD_PARAMETER;
}
break;
default:
c_char_hit_num = 0;
e_char_hit_num = 0;
break;
}
usleep(20000);
}
}
return ERROR_CODE_OK;
}
int rand_int()
{
srand((unsigned)time(NULL));
return rand();
}
int process_cmd(const char *cmds[], int32_t cmd_num, char **files,
int32_t file_num, int32_t random_the_cmd, int32_t random_the_file,
int32_t sleep_is_enabled, int32_t check_error_is_enabled)
{
int ret = ERROR_CODE_OK;
GMainLoop *loop = NULL;
pthread_t thread_id = 0;
GstElement *pipeline = NULL;
guint watch_id = 0;
int32_t cmd_index = 0;
int32_t file_index = -1;
char *file_path = NULL;
int32_t paused = 1;
gdouble rate = 1.0;
gint audio_track_num = 0;
int64_t max_seek_time_ns = 1000000000;
int64_t max_sleep_time_us = 1000000;
if (cmd_num < 1 || file_num < 1)
{
LOG("no cmd or no file!\n");
return ERROR_CODE_BAD_PARAMETER;
}
LOG("------test begin...\n");
while (true)
{
if (strcasecmp(cmds[cmd_index], PLAY_CMD_PLAY) == 0)
{
if (pipeline != NULL)
{
LOG("---release %s\n", file_path);
ret = release(loop, thread_id, pipeline, watch_id);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
loop = NULL;
thread_id = 0;
pipeline = NULL;
watch_id = 0;
}
if (file_index >= file_num && !random_the_file)
{
LOG("------test end!\n");
return ERROR_CODE_OK;
}
if (random_the_file)
{
file_index = rand_int() % file_num;
}
else
{
file_index++;
if (file_index >= file_num)
{
LOG("------test end!\n");
return ERROR_CODE_OK;
}
}
file_path = files[file_index];
LOG("---play %s\n", file_path);
ret = create(file_path, &loop, &thread_id,
&pipeline, &watch_id);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_START) == 0)
{
LOG("---start\n");
ret = start(pipeline);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
if (random_the_cmd && check_error_is_enabled)
{
// to avoid calling other func before started which leads to error.
ret = wait_for_start();
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
paused = 0;
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_PAUSE) == 0)
{
LOG("---pause\n");
ret = pause_(pipeline);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
paused = 1;
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_STOP) == 0)
{
LOG("---stop\n");
ret = stop(pipeline);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
paused = 1;
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_SET_RATE) == 0)
{
rate = (gdouble)(rand_int() % 101) / 50.0;
LOG("---set rate %f\n", rate);
ret = set_rate(pipeline, rate);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_SET_VOLUME) == 0)
{
gdouble volume = (gdouble)(rand_int() % 33);
LOG("---set volume %f\n", volume);
ret = set_volume(pipeline, volume);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_SEEK_NS) == 0)
{
int64_t seek_time_ns = rand_int() % max_seek_time_ns;
LOG("---seek %lld us\n", seek_time_ns);
GST_INFO("---seek %lld us\n", seek_time_ns);
ret = seek(pipeline, rate, seek_time_ns);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_SWITCH_AUDIO_TRACK) == 0)
{
gint audio_track_index = rand_int() % audio_track_num;
LOG("---switch audio track %d\n", audio_track_index);
ret = set_audio_track_index(pipeline, audio_track_index);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_GET_DURATION_NS) == 0)
{
int64_t duration_ns = -1;
ret = get_duration(pipeline, &duration_ns);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
LOG("---get position: %lld ns\n", duration_ns);
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_GET_POSITION_NS) == 0)
{
int64_t position_ns = -1;
ret = get_position(pipeline, &position_ns);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
LOG("---get position: %lld ns\n", position_ns);
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_SLEEP_US) == 0)
{
if (sleep_is_enabled)
{
int64_t sleep_time_us = rand_int() % max_sleep_time_us;
LOG("---sleep %lld us\n", sleep_time_us);
usleep(sleep_time_us);
if (check_error(ERROR_CODE_OK, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
}
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_WAIT_FOR_START) == 0)
{
int64_t duration_ns = 0;
LOG("---wait for start, now is %s\n", paused ? "paused" : "playing");
ret = wait_for_start();
LOG("---wait for start done.\n");
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
ret = get_audio_tracks_num(pipeline, &audio_track_num);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
ret = get_duration(pipeline, &duration_ns);
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
max_seek_time_ns = duration_ns;
max_sleep_time_us = duration_ns >> 9;
LOG("---audio track num: %d\n", audio_track_num);
LOG("---max seek time: %lld ns, max sleep time: %lld us\n",
max_seek_time_ns, max_sleep_time_us);
}
else if (strcasecmp(cmds[cmd_index], PLAY_CMD_WAIT_FOR_FINISH) == 0)
{
if (random_the_cmd & paused)
{
LOG("---wait for finish was ignored in random cmd mode.");
}
else
{
LOG("---wait for finish, now is %s\n", paused ? "paused" : "playing");
ret = wait_for_finish();
if (check_error(ret, check_error_is_enabled) != ERROR_CODE_OK)
{
break;
}
LOG("---wait for finish done.\n");
}
}
else
{
LOG("---unkown cmd: %s\n", cmds[cmd_index]);
}
if (random_the_cmd)
{
cmd_index = rand_int() % cmd_num;
}
else
{
cmd_index++;
if (cmd_index >= cmd_num)
{
cmd_index = 0;
}
}
}
if (pipeline != NULL)
{
LOG("---release %s\n", file_path);
release(loop, thread_id, pipeline, watch_id);
loop = NULL;
thread_id = 0;
pipeline = NULL;
watch_id = 0;
}
return ERROR_CODE_OK;
}
//------------------------------------------------------
#define PATH_MAX_LEN 128
int parse_argv(int argc, char *argv[],
char *media_path,
int32_t *random_the_cmd,
int32_t *random_the_file,
int32_t *sleep_is_enabled,
int32_t *check_error_is_enabled)
{
int index = 0;
size_t size = sizeof("media_path=");
if (argv == NULL)
{
return ERROR_CODE_OK;
}
if (media_path == NULL || random_the_cmd == NULL || random_the_file == NULL || sleep_is_enabled == NULL || check_error_is_enabled == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
for (index = 1; index < argc; index++)
{
if (strcasecmp(argv[index], "--help") == 0)
{
LOG("%s [media_path=/media/] [random_the_cmd=1] "
"[random_the_file=0] [disable_sleep=1] "
"[disable_check_error=1]\n",
argv[0]);
continue;
}
if (strncasecmp(argv[index], "media_path=", size) == 0)
{
size_t len = strlen(argv[index]) - size;
if (len >= PATH_MAX_LEN)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
strncpy(media_path, argv[index] + size, len);
continue;
}
if (strcasecmp(argv[index], "random_the_cmd=1") == 0)
{
*random_the_cmd = 1;
continue;
}
if (strcasecmp(argv[index], "random_the_file=0") == 0)
{
*random_the_file = 0;
continue;
}
if (strcasecmp(argv[index], "disable_sleep=1") == 0)
{
*sleep_is_enabled = 0;
continue;
}
if (strcasecmp(argv[index], "disable_check_error=1") == 0)
{
*check_error_is_enabled = 0;
continue;
}
}
return ERROR_CODE_OK;
}
int path_was_filtered(char *path, const char *filter[],
int32_t filter_len)
{
int32_t index = 0;
size_t path_size;
size_t filter_size;
if (path == NULL || filter == NULL || filter_len < 1)
{
return ERROR_CODE_OK;
}
path_size = strlen(path);
for (index = 0; index < filter_len; index++)
{
filter_size = strlen(filter[index]);
if (path_size < filter_size)
{
continue;
}
if (strcasecmp(path + path_size - filter_size,
filter[index]))
{
continue;
}
return ERROR_CODE_OK;
}
return ERROR_CODE_BAD_PARAMETER;
}
int scan_media_files(char path[], const char *filter[],
int32_t filter_len, char ***files, int32_t *num)
{
char **dir_cache = NULL;
size_t dir_cache_index = 0;
size_t dir_cache_len = 300;
char *path_cache = NULL;
char *curr_path = NULL;
size_t curr_path_len = 0;
char **file_list = NULL;
size_t file_index = 0;
size_t max_file_num = 100;
DIR *dir = NULL;
struct dirent *entry = NULL;
struct stat status;
size_t char_size = sizeof(char);
if (path == NULL || files == NULL || num == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
dir_cache = (char **)calloc(dir_cache_len, char_size);
if (dir_cache == NULL)
{
LOG("calloc failed!\n");
return ERROR_CODE_BASE_ERROR;
}
file_list = (char **)calloc(max_file_num, char_size);
if (file_list == NULL)
{
LOG("calloc failed!\n");
free(dir_cache);
return ERROR_CODE_BASE_ERROR;
}
path_cache = (char *)calloc(strlen(path) + 1, char_size);
if (path_cache == NULL)
{
LOG("calloc failed!\n");
free(dir_cache);
free(file_list);
return ERROR_CODE_BASE_ERROR;
}
strcpy(path_cache, path);
dir_cache[dir_cache_index++] = path_cache;
while (dir_cache_index >= 1)
{
curr_path = dir_cache[--dir_cache_index];
curr_path_len = strlen(curr_path);
dir = opendir(curr_path);
if (dir == NULL)
{
stat(curr_path, &status);
if (!S_ISDIR(status.st_mode))
{
if (path_was_filtered(curr_path, filter, filter_len) == ERROR_CODE_OK)
{
file_list[file_index++] = curr_path;
}
else
{
free(curr_path);
}
continue;
}
LOG("opendir failed! path: %s\n", curr_path);
free(curr_path);
continue;
}
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type == DT_DIR)
{
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
{
if (dir_cache_index >= dir_cache_len)
{
LOG("dir cache is full, discard: %s/%s\n",
curr_path, entry->d_name);
}
else
{
path_cache = (char *)calloc(
curr_path_len + strlen(entry->d_name) + 2,
char_size);
if (path_cache == NULL)
{
LOG("calloc failed! discard: %s/%s\n",
curr_path, entry->d_name);
}
else
{
sprintf(path_cache, "%s/%s", curr_path, entry->d_name);
dir_cache[dir_cache_index++] = path_cache;
}
}
}
}
else
{
if (file_index >= max_file_num)
{
LOG("too many files, discard: %s/%s\n",
curr_path, entry->d_name);
closedir(dir);
free(curr_path);
break;
}
else
{
path_cache = (char *)calloc(
curr_path_len + strlen(entry->d_name) + 2,
char_size);
if (path_cache == NULL)
{
LOG("calloc failed! discard: %s/%s\n",
curr_path, entry->d_name);
}
else
{
sprintf(path_cache, "%s/%s", curr_path, entry->d_name);
if (path_was_filtered(path_cache, filter, filter_len) == ERROR_CODE_OK)
{
file_list[file_index++] = path_cache;
}
else
{
free(path_cache);
}
}
}
}
}
closedir(dir);
free(curr_path);
}
while (dir_cache_index >= 1)
{
free(dir_cache[--dir_cache_index]);
}
free(dir_cache);
*files = file_list;
*num = (int32_t)file_index;
return ERROR_CODE_OK;
}
int free_media_files(char **files, int32_t num)
{
int32_t index = 0;
if (files != NULL)
{
for (index = 0; index < num; index++)
{
if (files[index] != NULL)
{
free(files[index]);
files[index] = NULL;
}
}
free(files);
}
return ERROR_CODE_OK;
}
int get_char_array_len(const char *array[], int32_t *num)
{
int32_t index = 0;
if (array == NULL || num == NULL)
{
LOG("bad parameter!\n");
return ERROR_CODE_BAD_PARAMETER;
}
while (array[index] != END)
{
index++;
}
*num = index;
return ERROR_CODE_OK;
}
const char *cmds[] = {
PLAY_CMD_PLAY,
PLAY_CMD_START,
PLAY_CMD_WAIT_FOR_START,
PLAY_CMD_SET_VOLUME,
PLAY_CMD_SLEEP_US,
END};
const char *filter[] = {
".aac",
".ts",
".mkv",
".mp4",
END};
//------------------------------------------------------
int main(int argc, char *argv[])
{
char media_path[PATH_MAX_LEN] = "/media";
char **files = NULL;
int32_t cmd_num = 0;
int32_t filter_len = 0;
int32_t file_num = 0;
int32_t random_the_cmd = 0;
int32_t random_the_file = 0;
int32_t sleep_is_enabled = 1;
int32_t check_error_is_enabled = 1;
parse_argv(argc, argv, media_path,
&random_the_cmd, &random_the_file,
&sleep_is_enabled, &check_error_is_enabled);
get_char_array_len(filter, &filter_len);
scan_media_files(media_path, filter, filter_len, &files, &file_num);
get_char_array_len(cmds, &cmd_num);
process_cmd(cmds, cmd_num, files, file_num,
random_the_cmd, random_the_file, sleep_is_enabled,
check_error_is_enabled);
free_media_files(files, file_num);
return 0;
}