/* 
 | 
 * 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; 
 | 
} 
 |