diff options
Diffstat (limited to 'stream')
-rw-r--r-- | stream/cache2.c | 24 | ||||
-rw-r--r-- | stream/cookies.c | 2 | ||||
-rw-r--r-- | stream/dvb_tune.c | 4 | ||||
-rw-r--r-- | stream/http.c | 2 | ||||
-rw-r--r-- | stream/stream.c | 16 | ||||
-rw-r--r-- | stream/stream.h | 11 | ||||
-rw-r--r-- | stream/stream_bluray.c | 85 | ||||
-rw-r--r-- | stream/stream_cdda.c | 33 | ||||
-rw-r--r-- | stream/stream_dvd.c | 45 | ||||
-rw-r--r-- | stream/tvi_v4l2.c | 27 |
10 files changed, 158 insertions, 91 deletions
diff --git a/stream/cache2.c b/stream/cache2.c index 7744f2cba9..38c57d0af9 100644 --- a/stream/cache2.c +++ b/stream/cache2.c @@ -93,6 +93,7 @@ typedef struct { volatile int control_res; volatile double stream_time_length; volatile double stream_time_pos; + volatile double stream_start_time; volatile int idle; } cache_vars_t; @@ -284,6 +285,10 @@ static int cache_execute_control(cache_vars_t *s) { s->stream_time_pos = pos; else s->stream_time_pos = MP_NOPTS_VALUE; + if (s->stream->control(s->stream, STREAM_CTRL_GET_START_TIME, &pos) == STREAM_OK) + s->stream_start_time = pos; + else + s->stream_start_time = MP_NOPTS_VALUE; #if FORKED_CACHE // if parent PID changed, main process was killed -> exit if (s->ppid != getppid()) { @@ -297,9 +302,11 @@ static int cache_execute_control(cache_vars_t *s) { switch (s->control) { case STREAM_CTRL_SEEK_TO_TIME: needs_flush = 1; - double_res = s->control_double_arg; case STREAM_CTRL_GET_CURRENT_TIME: case STREAM_CTRL_GET_ASPECT_RATIO: + case STREAM_CTRL_GET_START_TIME: + case STREAM_CTRL_GET_CHAPTER_TIME: + double_res = s->control_double_arg; s->control_res = s->stream->control(s->stream, s->control, &double_res); s->control_double_arg = double_res; break; @@ -323,6 +330,9 @@ static int cache_execute_control(cache_vars_t *s) { case STREAM_CTRL_GET_LANG: s->control_res = s->stream->control(s->stream, s->control, (void *)&s->control_lang_arg); break; + case STREAM_CTRL_MANAGES_TIMELINE: + s->control_res = s->stream->control(s->stream, s->control, NULL); + break; default: s->control_res = STREAM_UNSUPPORTED; break; @@ -649,6 +659,13 @@ int cache_do_control(stream_t *stream, int cmd, void *arg) { case STREAM_CTRL_GET_CURRENT_TIME: *(double *)arg = s->stream_time_pos; return s->stream_time_pos != MP_NOPTS_VALUE ? STREAM_OK : STREAM_UNSUPPORTED; + case STREAM_CTRL_GET_START_TIME: + *(double *)arg = s->stream_start_time; + return s->stream_start_time != MP_NOPTS_VALUE ? STREAM_OK : STREAM_UNSUPPORTED; + case STREAM_CTRL_GET_CHAPTER_TIME: + s->control_double_arg = *(double *)arg; + s->control = cmd; + break; case STREAM_CTRL_GET_LANG: s->control_lang_arg = *(struct stream_lang_req *)arg; case STREAM_CTRL_GET_NUM_TITLES: @@ -659,6 +676,7 @@ int cache_do_control(stream_t *stream, int cmd, void *arg) { case STREAM_CTRL_GET_NUM_ANGLES: case STREAM_CTRL_GET_ANGLE: case STREAM_CTRL_GET_SIZE: + case STREAM_CTRL_MANAGES_TIMELINE: case -2: s->control = cmd; break; @@ -691,6 +709,8 @@ int cache_do_control(stream_t *stream, int cmd, void *arg) { case STREAM_CTRL_GET_TIME_LENGTH: case STREAM_CTRL_GET_CURRENT_TIME: case STREAM_CTRL_GET_ASPECT_RATIO: + case STREAM_CTRL_GET_START_TIME: + case STREAM_CTRL_GET_CHAPTER_TIME: *(double *)arg = s->control_double_arg; break; case STREAM_CTRL_GET_NUM_TITLES: @@ -707,6 +727,8 @@ int cache_do_control(stream_t *stream, int cmd, void *arg) { case STREAM_CTRL_GET_LANG: *(struct stream_lang_req *)arg = s->control_lang_arg; break; + case STREAM_CTRL_MANAGES_TIMELINE: + break; } return s->control_res; } diff --git a/stream/cookies.c b/stream/cookies.c index 5d772b8a6a..a7f01ef420 100644 --- a/stream/cookies.c +++ b/stream/cookies.c @@ -112,7 +112,7 @@ static int parse_line(char **ptr, char *cols[6]) /* Loads a file into RAM */ static char *load_file(const char *filename, int64_t * length) { - int fd = -1; + int fd; char *buffer = NULL; mp_msg(MSGT_NETWORK, MSGL_V, "Loading cookie file: %s\n", filename); diff --git a/stream/dvb_tune.c b/stream/dvb_tune.c index d5ea8c7b1a..8b24e88ede 100644 --- a/stream/dvb_tune.c +++ b/stream/dvb_tune.c @@ -325,7 +325,7 @@ static int tune_it(int fd_frontend, int fd_sec, unsigned int freq, unsigned int fe_transmit_mode_t TransmissionMode, fe_guard_interval_t guardInterval, fe_bandwidth_t bandwidth, fe_code_rate_t LP_CodeRate, fe_hierarchy_t hier, int timeout) { - int res, hi_lo = 0, dfd; + int hi_lo = 0, dfd; struct dvb_frontend_parameters feparams; struct dvb_frontend_info fe_info; @@ -334,7 +334,7 @@ static int tune_it(int fd_frontend, int fd_sec, unsigned int freq, unsigned int memset(&feparams, 0, sizeof(feparams)); - if ( (res = ioctl(fd_frontend,FE_GET_INFO, &fe_info) < 0)) + if ( ioctl(fd_frontend,FE_GET_INFO, &fe_info) < 0) { mp_msg(MSGT_DEMUX, MSGL_FATAL, "FE_GET_INFO FAILED\n"); return -1; diff --git a/stream/http.c b/stream/http.c index eb0ea701b6..2ec7c9d58c 100644 --- a/stream/http.c +++ b/stream/http.c @@ -187,9 +187,9 @@ static int scast_streaming_start(stream_t *stream) { int metaint; scast_data_t *scast_data; HTTP_header_t *http_hdr = stream->streaming_ctrl->data; - int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0; if (!stream || stream->fd < 0 || !http_hdr) return -1; + int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0; if (is_ultravox) metaint = 0; else { diff --git a/stream/stream.c b/stream/stream.c index 75c7d4fb5c..36594d8556 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -331,10 +331,6 @@ int stream_read_internal(stream_t *s, void *buf, int len) len = read(s->fd, buf, len); } break; - case STREAMTYPE_DS: - len = demux_read_data((demux_stream_t *)s->priv, buf, len); - break; - default: len = s->fill_buffer ? s->fill_buffer(s, buf, len) : 0; @@ -590,13 +586,6 @@ void free_stream(stream_t *s) talloc_free(s); } -stream_t *new_ds_stream(demux_stream_t *ds) -{ - stream_t *s = new_stream(-1, STREAMTYPE_DS); - s->priv = ds; - return s; -} - void stream_set_interrupt_callback(int (*cb)(struct input_ctx *, int), struct input_ctx *ctx) { @@ -780,3 +769,8 @@ struct bstr stream_read_complete(struct stream *s, void *talloc_ctx, buf, total_read }; } + +bool stream_manages_timeline(struct stream *s) +{ + return stream_control(s, STREAM_CTRL_MANAGES_TIMELINE, NULL) == STREAM_OK; +} diff --git a/stream/stream.h b/stream/stream.h index 746667c564..df4188ed94 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -42,7 +42,6 @@ #define STREAMTYPE_DVD 3 // libdvdread #define STREAMTYPE_MEMORY 4 // read data from memory area #define STREAMTYPE_PLAYLIST 6 // FIXME!!! same as STREAMTYPE_FILE now -#define STREAMTYPE_DS 8 // read from a demuxer stream #define STREAMTYPE_CDDA 10 // raw audio CD reader #define STREAMTYPE_SMB 11 // smb:// url, using libsmbclient (samba) #define STREAMTYPE_VCDBINCUE 12 // vcd directly from bin/cue files @@ -102,6 +101,10 @@ #define STREAM_CTRL_GET_CACHE_FILL 16 #define STREAM_CTRL_GET_CACHE_IDLE 17 #define STREAM_CTRL_RECONNECT 18 +// DVD/Bluray, signal general support for GET_CURRENT_TIME etc. +#define STREAM_CTRL_MANAGES_TIMELINE 19 +#define STREAM_CTRL_GET_START_TIME 20 +#define STREAM_CTRL_GET_CHAPTER_TIME 21 struct stream_lang_req { int type; // STREAM_AUDIO, STREAM_SUB @@ -393,7 +396,6 @@ stream_t *open_stream(const char *filename, struct MPOpts *options, int *file_format); stream_t *open_output_stream(const char *filename, struct MPOpts *options); struct demux_stream; -struct stream *new_ds_stream(struct demux_stream *ds); /// Set the callback to be used by libstream to check for user /// interruption during long blocking operations (cache filling, etc). @@ -408,11 +410,12 @@ int stream_read_internal(stream_t *s, void *buf, int len); /// Internal seek function bypassing the stream buffer int stream_seek_internal(stream_t *s, int64_t newpos); -extern int bluray_angle; -extern int bluray_chapter; +bool stream_manages_timeline(stream_t *s); + extern int dvd_title; extern int dvd_angle; +extern int bluray_angle; extern char *bluray_device; typedef struct { diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c index 8ee69aaa80..06d1e88b99 100644 --- a/stream/stream_bluray.c +++ b/stream/stream_bluray.c @@ -48,14 +48,17 @@ #define BLURAY_DEFAULT_CHAPTER 0 #define BLURAY_DEFAULT_TITLE 0 +// 90khz ticks +#define BD_TIMEBASE (90000) +#define BD_TIME_TO_MP(x) ((x) / (double)(BD_TIMEBASE)) +#define BD_TIME_FROM_MP(x) ((uint64_t)(x * BD_TIMEBASE)) + char *bluray_device = NULL; int bluray_angle = 0; -int bluray_chapter = 0; struct bluray_priv_s { BLURAY *bd; int current_angle; - int current_chapter; int current_title; }; @@ -129,13 +132,34 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) return 1; } + case STREAM_CTRL_GET_CHAPTER_TIME: { + BLURAY_TITLE_INFO *ti; + int chapter = *(double *)arg; + double time = MP_NOPTS_VALUE; + + ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); + if (!ti) + return STREAM_UNSUPPORTED; + + if (chapter >= 0 || chapter < ti->chapter_count) { + time = BD_TIME_TO_MP(ti->chapters[chapter].start); + } + bd_free_title_info(ti); + + if (time != MP_NOPTS_VALUE) { + *(double *)arg = time; + return STREAM_OK; + } + return STREAM_ERROR; + } + case STREAM_CTRL_GET_CURRENT_TITLE: { *((unsigned int *) arg) = b->current_title; return 1; } case STREAM_CTRL_GET_CURRENT_CHAPTER: { - *((unsigned int *) arg) = b->current_chapter; + *((unsigned int *) arg) = bd_get_current_chapter(b->bd); return 1; } @@ -161,6 +185,31 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) return r ? 1 : STREAM_UNSUPPORTED; } + case STREAM_CTRL_GET_TIME_LENGTH: { + BLURAY_TITLE_INFO *ti; + + ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); + if (!ti) + return STREAM_UNSUPPORTED; + + *((double *) arg) = BD_TIME_TO_MP(ti->duration); + return STREAM_OK; + } + + case STREAM_CTRL_GET_CURRENT_TIME: { + *((double *) arg) = BD_TIME_TO_MP(bd_tell_time(b->bd)); + return STREAM_OK; + } + + case STREAM_CTRL_SEEK_TO_TIME: { + double pts = *((double *) arg); + bd_seek_time(b->bd, BD_TIME_FROM_MP(pts)); + // Reset mpv internal stream position. + stream_seek(s, bd_tell(b->bd)); + // API makes it hard to determine seeking success + return STREAM_OK; + } + case STREAM_CTRL_GET_NUM_ANGLES: { BLURAY_TITLE_INFO *ti; @@ -215,18 +264,25 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) si = ti->clips[0].pg_streams; break; } - while (count-- > 0) { - if (si->pid == req->id) { - snprintf(req->name, sizeof(req->name), "%.4s", si->lang); + for (int n = 0; n < count; n++) { + BLURAY_STREAM_INFO *i = &si[n]; + if (i->pid == req->id) { + snprintf(req->name, sizeof(req->name), "%.4s", i->lang); bd_free_title_info(ti); return STREAM_OK; } - si++; } } bd_free_title_info(ti); return STREAM_ERROR; } + case STREAM_CTRL_GET_START_TIME: + { + *((double *)arg) = 0; + return STREAM_OK; + } + case STREAM_CTRL_MANAGES_TIMELINE: + return STREAM_OK; default: break; @@ -247,9 +303,8 @@ static int bluray_stream_open(stream_t *s, int mode, int title, title_guess, title_count; uint64_t title_size; - unsigned int chapter = 0, angle = 0; + unsigned int angle = 0; uint64_t max_duration = 0; - int64_t chapter_pos = 0; char *device = NULL; int i; @@ -328,16 +383,6 @@ static int bluray_stream_open(stream_t *s, int mode, if (!info) goto err_no_info; - /* Select chapter */ - chapter = bluray_chapter ? bluray_chapter : BLURAY_DEFAULT_CHAPTER; - chapter = FFMIN(chapter, info->chapter_count); - - if (chapter) - chapter_pos = bd_chapter_pos(bd, chapter); - - mp_msg(MSGT_IDENTIFY, MSGL_INFO, - "ID_BLURAY_CURRENT_CHAPTER=%d\n", chapter + 1); - /* Select angle */ angle = bluray_angle ? bluray_angle : BLURAY_DEFAULT_ANGLE; angle = FFMIN(angle, info->angle_count); @@ -358,10 +403,8 @@ err_no_info: b = calloc(1, sizeof(struct bluray_priv_s)); b->bd = bd; b->current_angle = angle; - b->current_chapter = chapter; b->current_title = title; - s->start_pos = chapter_pos; s->end_pos = title_size; s->sector_size = BLURAY_SECTOR_SIZE; s->flags = mode | MP_STREAM_SEEK; diff --git a/stream/stream_cdda.c b/stream/stream_cdda.c index 2541e70174..a99150f65d 100644 --- a/stream/stream_cdda.c +++ b/stream/stream_cdda.c @@ -71,7 +71,7 @@ static struct cdda_params { int toc_offset; int no_skip; char *device; - m_span_t span; + int span[2]; } cdda_dflts = { .search_overlap = -1, }; @@ -91,11 +91,9 @@ static const m_option_t cdda_params_fields[] = { {"noskip", ST_OFF(no_skip), CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"skip", ST_OFF(no_skip), CONF_TYPE_FLAG, 0, 1, 0, NULL}, {"device", ST_OFF(device), CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"span", ST_OFF(span), CONF_TYPE_OBJ_PARAMS, 0, 0, 0, - (void *)&m_span_params_def}, + {"span", ST_OFF(span), CONF_TYPE_INT_PAIR, 0, 0, 0, NULL}, /// For url parsing - {"hostname", ST_OFF(span), CONF_TYPE_OBJ_PARAMS, 0, 0, 0, - (void *)&m_span_params_def}, + {"hostname", ST_OFF(span), CONF_TYPE_INT_PAIR, 0, 0, 0, NULL}, {"port", ST_OFF(speed), CONF_TYPE_INT, M_OPT_RANGE, 1, 100, NULL}, {"filename", ST_OFF(device), CONF_TYPE_STRING, 0, 0, 0, NULL}, {0} @@ -122,8 +120,7 @@ const m_option_t cdda_opts[] = { {"noskip", &cdda_dflts.no_skip, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"skip", &cdda_dflts.no_skip, CONF_TYPE_FLAG, 0, 1, 0, NULL}, {"device", &cdda_dflts.device, CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"span", &cdda_dflts.span, CONF_TYPE_OBJ_PARAMS, 0, 0, 0, - (void *)&m_span_params_def}, + {"span", &cdda_dflts.span, CONF_TYPE_INT_PAIR, 0, 0, 0, NULL}, {NULL, NULL, 0, 0, 0, 0, NULL} }; @@ -315,7 +312,7 @@ static int control(stream_t *stream, int cmd, void *arg) // returning error. return STREAM_OK; } - seek_sector = track <= 0 ? p->start_sector + seek_sector = track == 0 ? p->start_sector : p->cd->disc_toc[track].dwStartSector; r = seek(stream, seek_sector * CDIO_CD_FRAMESIZE_RAW); if (r) @@ -435,19 +432,19 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format) cdda_speed_set(cdd, p->speed); last_track = cdda_tracks(cdd); - if (p->span.start > last_track) - p->span.start = last_track; - if (p->span.end < p->span.start) - p->span.end = p->span.start; - if (p->span.end > last_track) - p->span.end = last_track; - if (p->span.start) - priv->start_sector = cdda_track_firstsector(cdd, p->span.start); + if (p->span[0] > last_track) + p->span[0] = last_track; + if (p->span[1] < p->span[0]) + p->span[1] = p->span[0]; + if (p->span[1] > last_track) + p->span[1] = last_track; + if (p->span[0]) + priv->start_sector = cdda_track_firstsector(cdd, p->span[0]); else priv->start_sector = cdda_disc_firstsector(cdd); - if (p->span.end) - priv->end_sector = cdda_track_lastsector(cdd, p->span.end); + if (p->span[1]) + priv->end_sector = cdda_track_lastsector(cdd, p->span[1]); else priv->end_sector = cdda_disc_lastsector(cdd); diff --git a/stream/stream_dvd.c b/stream/stream_dvd.c index 0207ae3969..dc2cdfd2ae 100644 --- a/stream/stream_dvd.c +++ b/stream/stream_dvd.c @@ -498,7 +498,8 @@ static int seek_to_chapter(stream_t *stream, ifo_handle_t *vts_file, tt_srpt_t * return chapter; } -static void list_chapters(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no) +// p: in=chapter number, out=PTS +static int get_chapter_time(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no, double *p) { unsigned int i, cell, last_cell; unsigned int t=0; @@ -507,10 +508,10 @@ static void list_chapters(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_ title_no = tt_srpt->title[title_no].vts_ttn - 1; if(vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts < 2) - return; + return 0; ptt = vts_file->vts_ptt_srpt->title[title_no].ptt; - mp_msg(MSGT_IDENTIFY, MSGL_INFO, "CHAPTERS: "); + int cur = 0; for(i=0; i<vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts; i++) { pgc = vts_file->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc; @@ -519,15 +520,35 @@ static void list_chapters(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_ last_cell = pgc->program_map[ptt[i].pgn]; else last_cell = 0; - mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02d:%02d:%02d.%03d,", t/3600000, (t/60000)%60, (t/1000)%60, t%1000); do { if(!(pgc->cell_playback[cell-1].block_type == BLOCK_TYPE_ANGLE_BLOCK && pgc->cell_playback[cell-1].block_mode != BLOCK_MODE_FIRST_CELL) - ) + ) { + if (cur == *p) { + *p = t / 1000.0; + return 1; + } t += mp_dvdtimetomsec(&pgc->cell_playback[cell-1].playback_time); + cur++; + } cell++; } while(cell < last_cell); } + return 0; +} + +static void list_chapters(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no) +{ + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "CHAPTERS: "); + for (int n = 0; ; n++) { + double p = n; + int r; + r = get_chapter_time(vts_file, tt_srpt, title_no, &p); + if (!r) + break; + int t = p * 1000; + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02d:%02d:%02d.%03d,", t/3600000, (t/60000)%60, (t/1000)%60, t%1000); + } mp_msg(MSGT_IDENTIFY, MSGL_INFO, "\n"); } @@ -624,6 +645,11 @@ static int control(stream_t *stream,int cmd,void* arg) *((double *)arg) = (double) mp_get_titleset_length(d->vts_file, d->tt_srpt, d->cur_title-1)/1000.0; return 1; } + case STREAM_CTRL_GET_START_TIME: + { + *((double *)arg) = 0; + return 1; + } case STREAM_CTRL_GET_NUM_TITLES: { *((unsigned int *)arg) = d->vmg_file->tt_srpt->nr_of_srpts; @@ -637,6 +663,13 @@ static int control(stream_t *stream,int cmd,void* arg) *((unsigned int *)arg) = r; return 1; } + case STREAM_CTRL_GET_CHAPTER_TIME: + { + int r; + r = get_chapter_time(d->vts_file, d->tt_srpt, d->cur_title-1, (double *)arg); + if(! r) return STREAM_UNSUPPORTED; + return 1; + } case STREAM_CTRL_SEEK_TO_CHAPTER: { int r; @@ -712,6 +745,8 @@ static int control(stream_t *stream,int cmd,void* arg) snprintf(req->name, sizeof(req->name), "%c%c", lang >> 8, lang); return STREAM_OK; } + case STREAM_CTRL_MANAGES_TIMELINE: + return STREAM_OK; } return STREAM_UNSUPPORTED; } diff --git a/stream/tvi_v4l2.c b/stream/tvi_v4l2.c index abe455d8bf..06344d1fa9 100644 --- a/stream/tvi_v4l2.c +++ b/stream/tvi_v4l2.c @@ -1747,29 +1747,6 @@ static int get_video_framesize(priv_t *priv) return priv->format.fmt.pix.sizeimage; } -//#define DOUBLESPEED -#ifdef DOUBLESPEED -// for testing purposes only -static void read_doublespeed(priv_t *priv) -{ - char *bufx = calloc(priv->audio_in.blocksize, 2); - short *s; - short *d; - int i; - - audio_in_read_chunk(&priv->audio_in, bufx); - audio_in_read_chunk(&priv->audio_in, bufx+priv->audio_in.blocksize); - - s = bufx; - d = priv->audio_ringbuffer+priv->audio_tail*priv->audio_in.blocksize; - for (i = 0; i < priv->audio_in.blocksize/2; i++) { - *d++ = *s++; - *s++; - } - -} -#endif - static void *audio_grabber(void *data) { priv_t *priv = (priv_t*)data; @@ -1788,12 +1765,8 @@ static void *audio_grabber(void *data) for (; !priv->shutdown;) { -#ifdef DOUBLESPEED - read_doublespeed(priv); -#else if (audio_in_read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail*priv->audio_in.blocksize) < 0) continue; -#endif pthread_mutex_lock(&priv->skew_mutex); if (priv->first_frame == 0) { // there is no first frame yet (unlikely to happen) |