2025-05-10 21:49:39 +08:00

583 lines
14 KiB
C++

/*
* Copyright 2015 Rockchip Electronics Co. LTD
*
* 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/LICENSE-2.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.
*/
#define MODULE_TAG "mpp_time"
#include <errno.h>
#include <string.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include "mpp_mem.h"
#include "mpp_time.h"
#include "mpp_debug.h"
#include "mpp_common.h"
#include "mpp_thread.h"
#if _WIN32
#include <sys/types.h>
#include <sys/timeb.h>
RK_S64 mpp_time()
{
struct timeb tb;
ftime(&tb);
return ((RK_S64)tb.time * 1000 + (RK_S64)tb.millitm) * 1000;
}
#else
#include <time.h>
RK_S64 mpp_time()
{
struct timespec time = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &time);
return (RK_S64)time.tv_sec * 1000000 + (RK_S64)time.tv_nsec / 1000;
}
#endif
void mpp_time_diff(RK_S64 start, RK_S64 end, RK_S64 limit, const char *fmt)
{
RK_S64 diff = end - start;
if (diff >= limit)
mpp_dbg(MPP_DBG_TIMING, "%s timing %lld us\n", fmt, diff);
}
typedef struct MppClockImpl_t {
const char *check;
char name[16];
RK_U32 enable;
RK_S64 base;
RK_S64 time;
RK_S64 sum;
RK_S64 count;
} MppClockImpl;
static const char *clock_name = "mpp_clock";
MPP_RET check_is_mpp_clock(void *clock)
{
if (clock && ((MppClockImpl*)clock)->check == clock_name)
return MPP_OK;
mpp_err_f("pointer %p failed on check\n", clock);
mpp_abort();
return MPP_NOK;
}
MppClock mpp_clock_get(const char *name)
{
MppClockImpl *impl = mpp_calloc(MppClockImpl, 1);
if (impl) {
impl->check = clock_name;
snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
} else
mpp_err_f("malloc failed\n");
return impl;
}
void mpp_clock_put(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return ;
}
mpp_free(clock);
}
void mpp_clock_enable(MppClock clock, RK_U32 enable)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
} else {
MppClockImpl *p = (MppClockImpl *)clock;
p->enable = (enable) ? (1) : (0);
}
}
RK_S64 mpp_clock_start(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return 0;
}
MppClockImpl *p = (MppClockImpl *)clock;
if (!p->enable)
return 0;
p->base = mpp_time();
p->time = 0;
return p->base;
}
RK_S64 mpp_clock_pause(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return 0;
}
MppClockImpl *p = (MppClockImpl *)clock;
if (!p->enable)
return 0;
RK_S64 time = mpp_time();
if (!p->time) {
// first pause after start
p->sum += time - p->base;
p->count++;
}
p->time = time;
return p->time - p->base;
}
RK_S64 mpp_clock_reset(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
} else {
MppClockImpl *p = (MppClockImpl *)clock;
p->base = 0;
p->time = 0;
p->sum = 0;
p->count = 0;
}
return 0;
}
RK_S64 mpp_clock_get_sum(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return 0;
}
MppClockImpl *p = (MppClockImpl *)clock;
return (p->enable) ? (p->sum) : (0);
}
RK_S64 mpp_clock_get_count(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return 0;
}
MppClockImpl *p = (MppClockImpl *)clock;
return (p->enable) ? (p->count) : (0);
}
const char *mpp_clock_get_name(MppClock clock)
{
if (NULL == clock || check_is_mpp_clock(clock)) {
mpp_err_f("invalid clock %p\n", clock);
return NULL;
}
MppClockImpl *p = (MppClockImpl *)clock;
return p->name;
}
typedef struct MppTimerImpl_t {
const char *check;
char name[16];
RK_S32 enabled;
RK_S32 initial;
RK_S32 interval;
RK_S32 timer_fd;
RK_S32 epoll_fd;
MppThread *thd;
MppThreadFunc func;
void *ctx;
} MppTimerImpl;
static const char *timer_name = "mpp_timer";
MPP_RET check_is_mpp_timer(void *timer)
{
if (timer && ((MppTimerImpl*)timer)->check == timer_name)
return MPP_OK;
mpp_err_f("pointer %p failed on check\n", timer);
mpp_abort();
return MPP_NOK;
}
static void *mpp_timer_thread(void *ctx)
{
struct itimerspec ts;
RK_S32 ret = 0;
MppTimerImpl *impl = (MppTimerImpl *)ctx;
MppThread *thd = impl->thd;
RK_S32 timer_fd = impl->timer_fd;
// first expire time
ts.it_value.tv_sec = impl->initial / 1000;
ts.it_value.tv_nsec = (impl->initial % 1000) * 1000;
// last expire time
ts.it_interval.tv_sec = impl->interval / 1000;
ts.it_interval.tv_nsec = (impl->interval % 1000) * 1000 * 1000;
ret = timerfd_settime(timer_fd, 0, &ts, NULL);
if (ret < 0) {
mpp_err("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno));
return NULL;
}
while (1) {
if (MPP_THREAD_RUNNING != thd->get_status())
break;
struct epoll_event events;
memset(&events, 0, sizeof(events));
/* wait epoll event */
RK_S32 fd_cnt = epoll_wait(impl->epoll_fd, &events, 1, 500);
if (fd_cnt && (events.events & EPOLLIN) && (events.data.fd == timer_fd)) {
RK_U64 exp = 0;
ssize_t cnt = read(timer_fd, &exp, sizeof(exp));
mpp_assert(cnt == sizeof(exp));
impl->func(impl->ctx);
}
}
return NULL;
}
MppTimer mpp_timer_get(const char *name)
{
RK_S32 timer_fd = -1;
RK_S32 epoll_fd = -1;
MppTimerImpl *impl = NULL;
do {
struct epoll_event event;
impl = mpp_calloc(MppTimerImpl, 1);
if (NULL == impl) {
mpp_err_f("malloc failed\n");
break;
}
timer_fd = timerfd_create(CLOCK_REALTIME, 0);
if (timer_fd < 0)
break;
epoll_fd = epoll_create(1);
if (epoll_fd < 0)
break;
memset(&event, 0, sizeof(event));
event.data.fd = timer_fd;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &event) < 0)
break;
impl->timer_fd = timer_fd;
impl->epoll_fd = epoll_fd;
/* default 1 second (1000ms) looper */
impl->initial = 1000;
impl->interval = 1000;
impl->check = timer_name;
snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
return impl;
} while (0);
mpp_err_f("failed to create timer\n");
if (impl) {
mpp_free(impl);
impl = NULL;
}
if (timer_fd >= 0) {
close(timer_fd);
timer_fd = -1;
}
if (epoll_fd >= 0) {
close(epoll_fd);
epoll_fd = -1;
}
return NULL;
}
void mpp_timer_set_callback(MppTimer timer, MppThreadFunc func, void *ctx)
{
if (NULL == timer || check_is_mpp_timer(timer)) {
mpp_err_f("invalid timer %p\n", timer);
return ;
}
if (NULL == func) {
mpp_err_f("invalid NULL callback\n");
return ;
}
MppTimerImpl *impl = (MppTimerImpl *)timer;
impl->func = func;
impl->ctx = ctx;
}
void mpp_timer_set_timing(MppTimer timer, RK_S32 initial, RK_S32 interval)
{
if (NULL == timer || check_is_mpp_timer(timer)) {
mpp_err_f("invalid timer %p\n", timer);
return ;
}
MppTimerImpl *impl = (MppTimerImpl *)timer;
impl->initial = initial;
impl->interval = interval;
}
void mpp_timer_set_enable(MppTimer timer, RK_S32 enable)
{
if (NULL == timer || check_is_mpp_timer(timer)) {
mpp_err_f("invalid timer %p\n", timer);
return ;
}
MppTimerImpl *impl = (MppTimerImpl *)timer;
if (NULL == impl->func || impl->initial < 0 || impl->interval < 0) {
mpp_err_f("invalid func %p initial %d interval %d\n",
impl->func, impl->initial, impl->interval);
return ;
}
if (enable) {
if (!impl->enabled && NULL == impl->thd) {
MppThread *thd = new MppThread(mpp_timer_thread, impl, impl->name);
if (thd) {
impl->thd = thd;
impl->enabled = 1;
thd->start();
}
}
} else {
if (impl->enabled && impl->thd) {
impl->thd->stop();
impl->enabled = 0;
}
}
}
void mpp_timer_put(MppTimer timer)
{
if (NULL == timer || check_is_mpp_timer(timer)) {
mpp_err_f("invalid timer %p\n", timer);
return ;
}
MppTimerImpl *impl = (MppTimerImpl *)timer;
if (impl->enabled)
mpp_timer_set_enable(timer, 0);
if (impl->timer_fd >= 0) {
close(impl->timer_fd);
impl->timer_fd = -1;
}
if (impl->epoll_fd >= 0) {
close(impl->epoll_fd);
impl->epoll_fd = -1;
}
if (impl->thd) {
delete impl->thd;
impl->thd = NULL;
}
if (impl) {
mpp_free(impl);
impl = NULL;
}
}
AutoTiming::AutoTiming(const char *name)
{
mStart = mpp_time();
mName = name;
}
AutoTiming::~AutoTiming()
{
mEnd = mpp_time();
mpp_log("%s timing %lld us\n", mName, mEnd - mStart);
}
#define STOPWATCH_TRACE_STR_LEN 64
typedef struct MppStopwatchNode_t {
char event[STOPWATCH_TRACE_STR_LEN];
RK_S64 time;
} MppStopwatchNode;
typedef struct MppStopwatchImpl_t {
const char *check;
char name[STOPWATCH_TRACE_STR_LEN];
RK_S32 max_count;
RK_S32 filled_count;
RK_S32 show_on_exit;
RK_S32 log_len;
RK_S64 time_elipsed;
MppStopwatchNode *nodes;
} MppStopwatchImpl;
static const char *stopwatch_name = "mpp_stopwatch";
MPP_RET check_is_mpp_stopwatch(void *stopwatch)
{
if (stopwatch && ((MppStopwatchImpl*)stopwatch)->check == stopwatch_name)
return MPP_OK;
mpp_err_f("pointer %p failed on check\n", stopwatch);
mpp_abort();
return MPP_NOK;
}
MppStopwatch mpp_stopwatch_get(const char *name)
{
MppStopwatchImpl *impl = mpp_calloc(MppStopwatchImpl, 1);
MppStopwatchNode *nodes = mpp_calloc(MppStopwatchNode, 8);
if (impl && nodes) {
impl->check = stopwatch_name;
snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
impl->nodes = nodes;
impl->max_count = 8;
} else {
mpp_err_f("malloc failed\n");
MPP_FREE(impl);
MPP_FREE(nodes);
}
return impl;
}
void mpp_stopwatch_set_show_on_exit(MppStopwatch stopwatch, RK_S32 show_on_exit)
{
if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) {
mpp_err_f("invalid stopwatch %p\n", stopwatch);
return ;
}
MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
impl->show_on_exit = show_on_exit;
}
void mpp_stopwatch_record(MppStopwatch stopwatch, const char *event)
{
/* do not print noisy log */
if (NULL == stopwatch)
return ;
if (check_is_mpp_stopwatch(stopwatch)) {
mpp_err_f("invalid stopwatch %p on %s\n", stopwatch, event);
return ;
}
MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
if (impl->filled_count >= impl->max_count) {
RK_S32 max_count = impl->max_count * 2;
MppStopwatchNode *nodes = mpp_realloc(impl->nodes, MppStopwatchNode,
max_count);
if (nodes) {
impl->nodes = nodes;
impl->max_count = max_count;
}
}
if (impl->filled_count < impl->max_count) {
MppStopwatchNode *node = impl->nodes + impl->filled_count;
node->time = mpp_time();
if (event) {
RK_S32 len = snprintf(node->event, sizeof(node->event) - 1,
"%s", event);
if (len > impl->log_len)
impl->log_len = len;
}
impl->filled_count++;
}
}
void mpp_stopwatch_put(MppStopwatch stopwatch)
{
if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) {
mpp_err_f("invalid stopwatch %p\n", stopwatch);
return ;
}
MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
if (impl->show_on_exit && impl->nodes && impl->filled_count) {
MppStopwatchNode *node = impl->nodes;
RK_S64 last_time = node->time;
RK_S32 i;
char fmt[32];
snprintf(fmt, sizeof(fmt) - 1, "%%s %%-%ds: %%6.2f\n", impl->log_len);
node++;
for (i = 1; i < impl->filled_count; i++) {
mpp_log(fmt, impl->name, node->event,
(float)(node->time - last_time) / 1000);
last_time = node->time;
node++;
}
}
MPP_FREE(impl->nodes);
MPP_FREE(impl);
}
RK_S64 mpp_stopwatch_elapsed_time(MppStopwatch stopwatch)
{
if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) {
mpp_err_f("invalid stopwatch %p\n", stopwatch);
return 0;
}
MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
if (impl->filled_count < 2)
return 0;
RK_S64 base_time = impl->nodes[0].time;
RK_S64 curr_time = impl->nodes[impl->filled_count - 1].time;
RK_S64 elapsed_time = curr_time - base_time;
return elapsed_time;
}