最近在写我自己的一个开源项目,就是能够按设定的时间录制HLS流的一个小工具,方便自己获取视频素材。然后首先需要解决的一个问题是,如何按照设定的时间准点的执行录制任务,这个过程可以简单地描述如下:

  1. 设定定时指定录制任务的时间戳,这里的时间戳可以认为是依据本地时区的自 1970 年 1 月 1 日以来持续时间的秒数;
  2. 获取当前程序指定的时间戳;
  3. 根据以上信息获取二者相差的秒数,然后子线程睡眠相差的秒数,然后执行录制任务;

过程比较简单,其中有两个地方需要处理,一个是根据给定的时间获取时间戳,二者是当前程序执行的时间戳,并且这两个时间戳可以进行比较,并可以获得二者之间相差的秒数。

1 根据设定的指定时间获取时间戳

这里转换时间为时间戳,我们用到的是C中mktime函数

1.1 mktime函数

函数形式

time_t mktime(struct tm *timeptr)

函数功能

把 timeptr所指向的结构转换为一个依据本地时区的自 1970 年 1 月 1 日以来持续时间的秒数,如果转换错误则返回-1。

函数参数

  • timeptr:这是指向表示日历时间的 time_t 值的指针,该日历时间可被分解为以下各部分。
struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59                */
   int tm_min;         /* 分,范围从 0 到 59                */
   int tm_hour;        /* 小时,范围从 0 到 23                */
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31                    */
   int tm_mon;         /* 月份,范围从 0 到 11                */
   int tm_year;        /* 自 1900 起的年数                */
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6                */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365                    */
   int tm_isdst;       /* 夏令时                        */    
};

1.2 将指定时间转换为时间戳

我们将mktime函数进行封装,

/**
* 如果我们要转换2022年9月19日21时0分0秒为时间戳,则传参为TimeToTimeStamp(2022, 9, 19, 21,0, 0)
*/
std::time_t TimeToTimeStamp(
    int year,
    int month,
    int day,
    int hour,
    int minute,
    int second
)
{
    struct tm temp_tm;
    memset(&temp_tm, 0, sizeof(tm));
    temp_tm.tm_year = year - 1900;
    temp_tm.tm_mon = month - 1;
    temp_tm.tm_mday = day;
    temp_tm.tm_hour = hour;
    temp_tm.tm_min = minute;
    temp_tm.tm_sec = second;
    temp_tm.tm_isdst = 0;

    int ret;
    char buffer[128];
    ret = mktime(&temp_tm);
    if (ret == -1) {
        std::cout << "Error: unable to make time using mktime" << std::endl;
    }

    return ret;
}

上述函数可以将设定的时间转换为本地时区的自 1970 年 1 月 1 日以来持续时间的秒数。

2 定时执行指定程序

下面我们写一个简单的示例,定时在2022年9月19日11时20分0秒准时打印当前的时间戳

#include <iostream>
#include <chrono>
#include <time.h>
#include <thread>
#include <sstream>

/**
* 如果我们要转换2022年9月19日21时0分0秒为时间戳,则传参为TimeToTimeStamp(2022, 9, 19, 21,0, 0)
*/
std::time_t TimeToTimeStamp(
    int year,
    int month,
    int day,
    int hour,
    int minute,
    int second
)
{
    struct tm temp_tm;
    memset(&temp_tm, 0, sizeof(tm));
    temp_tm.tm_year = year - 1900;
    temp_tm.tm_mon = month - 1;
    temp_tm.tm_mday = day;
    temp_tm.tm_hour = hour;
    temp_tm.tm_min = minute;
    temp_tm.tm_sec = second;
    temp_tm.tm_isdst = 0;

    int ret;
    char buffer[128];
    ret = mktime(&temp_tm);
    if (ret == -1) {
        std::cout << "Error: unable to make time using mktime" << std::endl;
    }

    return ret;
}

std::string GetCurrentTimeStamp(int time_stamp_type = 0)
{
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();

    std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
    std::tm* now_tm = std::localtime(&now_time_t);

    char buffer[128];
    strftime(buffer, sizeof(buffer), "%F %T", now_tm);

    std::ostringstream ss;
    ss.fill('0');

    std::chrono::milliseconds ms;
    std::chrono::microseconds cs;
    std::chrono::nanoseconds ns;

    switch (time_stamp_type)
    {
    case 0:
        ss << buffer;
        break;
    case 1:
        ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
        ss << buffer << ":" << ms.count();
        break;
    case 2:
        ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
        cs = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()) % 1000000;
        ss << buffer << ":" << ms.count() << ":" << cs.count() % 1000;
        break;
    case 3:
        ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
        cs = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()) % 1000000;
        ns = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()) % 1000000000;
        ss << buffer << ":" << ms.count() << ":" << cs.count() % 1000 << ":" << ns.count() % 1000;
        break;
    default:
        ss << buffer;
        break;
    }

    return ss.str();
}



int main()
{
    // 获取指定时间的时间戳
    std::time_t input_time = TimeToTimeStamp(2022, 9, 19, 11,20, 0);
    std::cout << input_time << std::endl;

    // 获取当前时间的时间戳
    time_t now_time_t;
    time(&now_time_t);
    std::cout << now_time_t << std::endl;

    // 当前线程睡眠计算所得时间差
    std::this_thread::sleep_for(std::chrono::seconds(input_time - now_time_t));

    std::cout << "当前时间" << GetCurrentTimeStamp(0) << std::endl;

    return 0;
}

执行结果

1663557600
1663557579
当前时间2022-09-19 11:20:00