You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
13 KiB
C

// RFC 2326 Real Time Streaming Protocol (RTSP)
//
// 12.29 Range(p53)
// parameter: time. in UTC, specifying the time at which the operation is to be made effective
// Ranges are half-open intervals, including the lower point, but excluding the upper point.
// byte ranges MUST NOT be used
// Range = "Range" ":" 1#ranges-specifier [ ";" "time" "=" utc-time ]
// ranges-specifier = npt-range | utc-range | smpte-range
// smpte/smpte-30-drop/smpte-25: hh:mm:ss[:frames][.subframes] - [hh:mm:ss[:frames][.subframes]] (p17)
// npt: hh:mm:ss[.ms]-[hh:mm:ss[.ms]] | -hh:mm:ss[.ms] | now- (p17)
// clock: YYYYMMDDThhmmss[.fraction]Z - [YYYYMMDDThhmmss[.fraction]Z] (p18)
//
// e.g.
// Range: clock=19960213T143205Z-;time=19970123T143720Z
// Range: smpte-25=10:07:00-10:07:33:05.01,smpte-25=11:07:00-11:07:33:05.01
#include "rtsp-header-range.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#if defined(_WIN32) || defined(_WIN64) || defined(OS_WINDOWS)
#define strncasecmp _strnicmp
#endif
struct time_smpte_t
{
int second; // [0,24*60*60)
int frame; // [0,99]
int subframe; // [0,99]
};
struct time_npt_t
{
int64_t second; // [0,---)
int fraction; // [0,999]
};
struct time_clock_t
{
int second; // [0,24*60*60)
int fraction; // [0,999]
int day; // [0,30]
int month; // [0,11]
int year; // [xxxx]
};
#define RANGE_SPECIAL ",;\r\n"
static uint64_t utc_mktime(const struct tm *t)
{
int mon = t->tm_mon+1, year = t->tm_year+1900;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((uint64_t)
(year/4 - year/100 + year/400 + 367*mon/12 + t->tm_mday) +
year*365 - 719499
)*24 + t->tm_hour /* now have hours */
)*60 + t->tm_min /* now have minutes */
)*60 + t->tm_sec; /* finally seconds */
}
static inline const char* string_token_int(const char* str, int *value)
{
*value = 0;
while ('0' <= *str && *str <= '9')
{
*value = (*value * 10) + (*str - '0');
++str;
}
return str;
}
static inline const char* string_token_int64(const char* str, int64_t *value)
{
*value = 0;
while ('0' <= *str && *str <= '9')
{
*value = (*value * 10) + (*str - '0');
++str;
}
return str;
}
// smpte-time = 1*2DIGIT ":" 1*2DIGIT ":" 1*2DIGIT [ ":" 1*2DIGIT ][ "." 1*2DIGIT ]
// hours:minutes:seconds:frames.subframes
static const char* rtsp_header_range_smpte_time(const char* str, int *hours, int *minutes, int *seconds, int *frames, int *subframes)
{
const char* p;
assert(str);
p = string_token_int(str, hours);
if(*p != ':')
return NULL;
p = string_token_int(p+1, minutes);
if(*p != ':')
return NULL;
p = string_token_int(p+1, seconds);
*frames = 0;
*subframes = 0;
if(*p == ':')
{
p = string_token_int(p+1, frames);
}
if(*p == '.')
{
p = string_token_int(p+1, subframes);
}
return p;
}
// 3.5 SMPTE Relative Timestamps (p16)
// smpte-range = smpte-type "=" smpte-time "-" [ smpte-time ]
// smpte-type = "smpte" | "smpte-30-drop" | "smpte-25"
// Examples:
// smpte=10:12:33:20-
// smpte=10:07:33-
// smpte=10:07:00-10:07:33:05.01
// smpte-25=10:07:00-10:07:33:05.01
static int rtsp_header_range_smpte(const char* fields, struct rtsp_header_range_t* range)
{
const char *p;
int hours, minutes, seconds, frames, subframes;
assert(fields);
p = rtsp_header_range_smpte_time(fields, &hours, &minutes, &seconds, &frames, &subframes);
if(!p || '-' != *p)
return -1;
range->from_value = RTSP_RANGE_TIME_NORMAL;
range->from = (hours%24)*3600 + (minutes%60)*60 + seconds;
range->from = range->from * 1000 + frames % 1000;
assert('-' == *p);
if('\0' == p[1] || strchr(RANGE_SPECIAL, p[1]))
{
range->to_value = RTSP_RANGE_TIME_NOVALUE;
range->to = 0;
}
else
{
p = rtsp_header_range_smpte_time(p+1, &hours, &minutes, &seconds, &frames, &subframes);
if(!p ) return -1;
assert('\0' == p[0] || strchr(RANGE_SPECIAL, p[0]));
range->to_value = RTSP_RANGE_TIME_NORMAL;
range->to = (hours%24)*3600 + (minutes%60)*60 + seconds;
range->to = range->to * 1000 + frames % 1000;
}
return 0;
}
// npt-time = "now" | npt-sec | npt-hhmmss
// npt-sec = 1*DIGIT [ "." *DIGIT ]
// npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT ]
// npt-hh = 1*DIGIT ; any positive number
// npt-mm = 1*2DIGIT ; 0-59
// npt-ss = 1*2DIGIT ; 0-59
static const char* rtsp_header_range_npt_time(const char* str, uint64_t *seconds, int *fraction)
{
const char* p;
int v1, v2;
assert(str);
str += strspn(str, " \t");
p = strpbrk(str, "-\r\n");
if(!str || (p-str==3 && 0==strncasecmp(str, "now", 3)))
{
*seconds = 0; // now
*fraction = -1;
}
else
{
p = string_token_int64(str, (int64_t*)seconds);
if(*p == ':')
{
// npt-hhmmss
p = string_token_int(p+1, &v1);
if(*p != ':')
return NULL;
p = string_token_int(p+1, &v2);
assert(0 <= v1 && v1 < 60);
assert(0 <= v2 && v2 < 60);
*seconds = *seconds * 3600 + (v1%60)*60 + v2%60;
}
else
{
// npt-sec
//*seconds = hours;
}
if(*p == '.')
p = string_token_int(p+1, fraction);
else
*fraction = 0;
}
return p;
}
// 3.6 Normal Play Time (p17)
// npt-range = ( npt-time "-" [ npt-time ] ) | ( "-" npt-time )
// Examples:
// npt=123.45-125
// npt=12:05:35.3-
// npt=now-
static int rtsp_header_range_npt(const char* fields, struct rtsp_header_range_t* range)
{
const char* p;
uint64_t seconds;
int fraction;
p = fields;
if('-' != *p)
{
p = rtsp_header_range_npt_time(p, &seconds, &fraction);
if(!p || '-' != *p)
return -1;
if(0 == seconds && -1 == fraction)
{
range->from_value = RTSP_RANGE_TIME_NOW;
range->from = 0L;
}
else
{
range->from_value = RTSP_RANGE_TIME_NORMAL;
range->from = seconds;
range->from = range->from * 1000 + fraction % 1000;
}
}
else
{
range->from_value = RTSP_RANGE_TIME_NOVALUE;
range->from = 0;
}
assert('-' == *p);
if('\0' == p[1] || strchr(RANGE_SPECIAL, p[1]))
{
assert('-' != *fields);
range->to_value = RTSP_RANGE_TIME_NOVALUE;
range->to = 0;
}
else
{
p = rtsp_header_range_npt_time(p+1, &seconds, &fraction);
if( !p ) return -1;
assert('\0' == p[0] || strchr(RANGE_SPECIAL, p[0]));
range->to_value = RTSP_RANGE_TIME_NORMAL;
range->to = seconds;
range->to = range->to * 1000 + fraction % 1000;
}
return 0;
}
// utc-time = utc-date "T" utc-time "Z"
// utc-date = 8DIGIT ; < YYYYMMDD >
// utc-time = 6DIGIT [ "." fraction ] ; < HHMMSS.fraction >
static const char* rtsp_header_range_clock_time(const char* str, uint64_t *second, int *fraction)
{
struct tm t;
const char* p;
int year, month, day;
int hour, minute;
assert(str);
if(!str || 5 != sscanf(str, "%4d%2d%2dT%2d%2d", &year, &month, &day, &hour, &minute))
return NULL;
*second = 0;
*fraction = 0;
p = string_token_int64(str + 13, (int64_t*)second);
assert(p);
if(*p == '.')
{
p = string_token_int(p+1, fraction);
}
memset(&t, 0, sizeof(t));
t.tm_year = year - 1900;
t.tm_mon = month - 1;
t.tm_mday = day;
t.tm_hour = hour;
t.tm_min = minute;
// *second += mktime(&t);
*second += utc_mktime(&t);
// assert('Z' == *p);
// assert('\0' == p[1] || strchr(RANGE_SPECIAL"-", p[1]));
return 'Z'==*p ? p+1 : p;
}
// 3.7 Absolute Time (p18)
// utc-range = "clock" "=" utc-time "-" [ utc-time ]
// Range: clock=19961108T143720.25Z-
// Range: clock=19961110T1925-19961110T2015 (p72)
static int rtsp_header_range_clock(const char* fields, struct rtsp_header_range_t* range)
{
const char* p;
uint64_t second;
int fraction;
p = rtsp_header_range_clock_time(fields, &second, &fraction);
if(!p || '-' != *p)
return -1;
range->from_value = RTSP_RANGE_TIME_NORMAL;
range->from = second * 1000;
range->from += fraction % 1000;
assert('-' == *p);
if('\0'==p[1] || strchr(RANGE_SPECIAL, p[1]))
{
range->to_value = RTSP_RANGE_TIME_NOVALUE;
range->to = 0;
}
else
{
p = rtsp_header_range_clock_time(p+1, &second, &fraction);
if( !p ) return -1;
range->to_value = RTSP_RANGE_TIME_NORMAL;
range->to = second * 1000;
range->to += (unsigned int)fraction % 1000;
}
return 0;
}
int rtsp_header_range(const char* field, struct rtsp_header_range_t* range)
{
int r = 0;
range->time = 0L;
while(field && 0 == r)
{
if(0 == strncasecmp("clock=", field, 6))
{
range->type = RTSP_RANGE_CLOCK;
r = rtsp_header_range_clock(field+6, range);
}
else if(0 == strncasecmp("npt=", field, 4))
{
range->type = RTSP_RANGE_NPT;
r = rtsp_header_range_npt(field+4, range);
}
else if(0 == strncasecmp("smpte=", field, 6))
{
range->type = RTSP_RANGE_SMPTE;
r = rtsp_header_range_smpte(field+6, range);
if(RTSP_RANGE_TIME_NORMAL == range->from_value)
range->from = (range->from/1000 * 1000) + (1000/30 * (range->from%1000)); // frame to ms
if(RTSP_RANGE_TIME_NORMAL == range->to_value)
range->to = (range->to/1000 * 1000) + (1000/30 * (range->to%1000)); // frame to ms
}
else if(0 == strncasecmp("smpte-30-drop=", field, 15))
{
range->type = RTSP_RANGE_SMPTE_30;
r = rtsp_header_range_smpte(field+15, range);
if(RTSP_RANGE_TIME_NORMAL == range->from_value)
range->from = (range->from/1000 * 1000) + (1000/30 * (range->from%1000)); // frame to ms
if(RTSP_RANGE_TIME_NORMAL == range->to_value)
range->to = (range->to/1000 * 1000) + (1000/30 * (range->to%1000)); // frame to ms
}
else if(0 == strncasecmp("smpte-25=", field, 9))
{
range->type = RTSP_RANGE_SMPTE_25;
r = rtsp_header_range_smpte(field+9, range);
if(RTSP_RANGE_TIME_NORMAL == range->from_value)
range->from = (range->from/1000 * 1000) + (1000/25 * (range->from%1000)); // frame to ms
if(RTSP_RANGE_TIME_NORMAL == range->to_value)
range->to = (range->to/1000 * 1000) + (1000/25 * (range->to%1000)); // frame to ms
}
else if(0 == strncasecmp("time=", field, 5))
{
if (rtsp_header_range_clock_time(field + 5, &range->time, &r))
range->time = range->time * 1000 + r % 1000;
}
field = strchr(field, ';');
if(field)
++field;
}
return r;
}
#if defined(DEBUG) || defined(_DEBUG)
void rtsp_header_range_test(void)
{
struct tm t;
struct rtsp_header_range_t range;
// smpte
assert(0==rtsp_header_range("smpte=10:12:33:20-", &range));
assert(range.type == RTSP_RANGE_SMPTE && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NOVALUE);
assert(range.from == (10*3600+12*60+33)*1000 + 20*33);
assert(0==rtsp_header_range("smpte=10:07:33-", &range));
assert(range.type == RTSP_RANGE_SMPTE && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NOVALUE);
assert(range.from == (10*3600+7*60+33)*1000);
assert(0==rtsp_header_range("smpte=10:07:00-10:07:33:05.01", &range));
assert(range.type == RTSP_RANGE_SMPTE && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NORMAL);
assert(range.from == (10*3600+7*60)*1000);
assert(range.to == (10*3600+7*60+33)*1000 + 5*33);
assert(0==rtsp_header_range("smpte-25=10:07:00-10:07:33:05.01", &range));
assert(range.type == RTSP_RANGE_SMPTE_25 && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NORMAL);
assert(range.from == (10*3600+7*60)*1000);
assert(range.to == (10*3600+7*60+33)*1000 + 5*40);
// npt
assert(0==rtsp_header_range("npt=now-", &range));
assert(range.type == RTSP_RANGE_NPT && range.from_value == RTSP_RANGE_TIME_NOW && range.to_value == RTSP_RANGE_TIME_NOVALUE);
assert(0==rtsp_header_range("npt=12:05:35.3-", &range));
assert(range.type == RTSP_RANGE_NPT && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NOVALUE);
assert(range.from == (12*3600+5*60+35)*1000 + 3);
assert(0==rtsp_header_range("npt=123.45-125", &range));
assert(range.type == RTSP_RANGE_NPT && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NORMAL);
assert(range.from == 123*1000+45);
assert(range.to == 125*1000);
// clock
assert(-1==rtsp_header_range("clock=-19961108T143720.25Z", &range));
assert(0==rtsp_header_range("clock=19961108T143720.25Z-", &range));
assert(range.type == RTSP_RANGE_CLOCK && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NOVALUE);
t.tm_year = 1996-1900; t.tm_mon = 11-1; t.tm_mday = 8; t.tm_hour = 14; t.tm_min = 37; t.tm_sec = 20;
assert(range.from == utc_mktime(&t)*1000 + 25);
assert(0==rtsp_header_range("clock=19961110T1925-19961110T2015", &range)); // rfc2326 (p72)
assert(range.type == RTSP_RANGE_CLOCK && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NORMAL);
t.tm_year = 1996-1900; t.tm_mon = 11-1; t.tm_mday = 10; t.tm_hour = 19; t.tm_min = 25; t.tm_sec = 00;
assert(range.from == utc_mktime(&t)*1000);
t.tm_year = 1996-1900; t.tm_mon = 11-1; t.tm_mday = 10; t.tm_hour = 20; t.tm_min = 15; t.tm_sec = 00;
assert(range.to == utc_mktime(&t)*1000);
// time
assert(0 == rtsp_header_range("smpte=0:10:20-;time=19970123T153600Z", &range)); // rfc2326 (p35)
assert(range.type == RTSP_RANGE_SMPTE && range.from_value == RTSP_RANGE_TIME_NORMAL && range.to_value == RTSP_RANGE_TIME_NOVALUE);
assert(range.from == (10 * 60 + 20) * 1000);
t.tm_year = 1997 - 1900; t.tm_mon = 1 - 1; t.tm_mday = 23; t.tm_hour = 15; t.tm_min = 36; t.tm_sec = 00;
assert(range.time == utc_mktime(&t) * 1000);
}
#endif