[NUT-devel] [nut]: r130 - in trunk/libnut: demuxer.c priv.h
ods15
subversion at mplayerhq.hu
Thu Sep 14 12:10:20 CEST 2006
Author: ods15
Date: Thu Sep 14 12:10:20 2006
New Revision: 130
Modified:
trunk/libnut/demuxer.c
trunk/libnut/priv.h
Log:
deobfuscate linear seeking algo using proper struct
fix bug related to find_syncpoint and EAGAIN
Modified: trunk/libnut/demuxer.c
==============================================================================
--- trunk/libnut/demuxer.c (original)
+++ trunk/libnut/demuxer.c Thu Sep 14 12:10:20 2006
@@ -375,7 +375,10 @@
int after_seek = nut->last_syncpoint ? 0 : 1;
input_buffer_t itmp, * tmp = new_mem_buffer(&itmp);
- nut->last_syncpoint = s.pos = bctello(nut->i) - 8;
+ s.pos = bctello(nut->i) - 8;
+
+ if (nut->last_syncpoint == s.pos) after_seek = 1; // don't go through the same syncpoint twice
+ nut->last_syncpoint = s.pos;
CHECK(get_header(nut->i, tmp));
@@ -973,26 +976,19 @@
return err;
}
-static int linear_search_seek(nut_context_t * nut, int backwards, uint64_t * pts, off_t start, off_t end, syncpoint_t * stopper) {
+static int linear_search_seek(nut_context_t * nut, int backwards, seek_state_t * state, off_t start, off_t end, syncpoint_t * stopper) {
syncpoint_list_t * sl = &nut->syncpoints;
int i, err = 0;
- off_t good_key[nut->stream_count]; // lsb is "has pts been found bigger than requested pts"
off_t min_pos = 0;
- uint64_t old_last_pts[nut->stream_count];
off_t buf_before = 0;
off_t stopper_syncpoint = 0;
- for (i = 0; i < nut->stream_count; i++) {
- good_key[i] = nut->seek_state ? nut->seek_state[i].good_key : 0;
- old_last_pts[i] = nut->seek_state ? nut->seek_state[i].old_last_pts : nut->sc[i].last_pts;
- }
-
if (nut->seek_status <= 1) {
syncpoint_t s;
if (!nut->seek_status) seek_buf(nut->i, start, SEEK_SET);
nut->seek_status = 1;
// find closest syncpoint by linear search, SHOULD be one pointed by back_ptr...
- // FIXME EAGAIN you should rewind here
+ buf_before = bctello(nut->i);
CHECK(find_syncpoint(nut, 0, &s, 0));
clear_dts_cache(nut);
nut->last_syncpoint = 0; // last_key is invalid
@@ -1010,31 +1006,24 @@
}
}
if (back_ptr > (nut->seek_status>>1)) stopper = NULL; // bad stopper, it points to a different back_ptr FIXME so?..
+ if (stopper_syncpoint > bctello(nut->i)) stopper_syncpoint = 0; // don't premature
}
if (!(nut->seek_status & 1)) while (bctello(nut->i) < end || !end) {
int saw_syncpoint;
nut_packet_t pd;
buf_before = bctello(nut->i);
- CHECK(get_packet(nut, &pd, &saw_syncpoint)); // FIXME we're counting on syncpoint cache!!
+ CHECK(get_packet(nut, &pd, &saw_syncpoint)); // FIXME we're counting on syncpoint cache!! for the good_key later, and stopper_syncpoint
if (saw_syncpoint) {
- int dont_flush = 0;
- if (stopper) {
- if ((!stopper_syncpoint && buf_before > stopper->pos - stopper->back_ptr + 15) || stopper_syncpoint == buf_before) {
- int n = 1;
- stopper_syncpoint = buf_before;
- for (i = 0; i < nut->stream_count; i++) {
- if (!(pts[i] & 1)) {
- if (good_key[i]>>1) n = 0;
- else good_key[i] |= 1; // flag that we don't care about this stream
- }
- }
- if (n) break; // no inactive streams, stop now
- dont_flush = 1; // give it a chance, we might be able to do this in a single seek
- }
+ if (stopper && !stopper_syncpoint && buf_before > stopper->pos - stopper->back_ptr + 15) {
+ int n = 1;
+ stopper_syncpoint = buf_before;
+ for (i = 0; i < nut->stream_count; i++) if (!state[i].active && state[i].good_key) n = 0;
+ if (n) break; // no inactive streams have keyframes between stopper and stopper_syncpoint, stop now
}
- if (!dont_flush) { // flush at every syncpoint
+ if (buf_before != stopper_syncpoint) { // flush at every syncpoint - except stopper_syncpoint
+ // give it a chance, we might be able to do this in a single seek
int header_size = bctello(nut->i) - buf_before;
nut->i->buf_ptr -= header_size;
flush_buf(nut->i);
@@ -1042,42 +1031,41 @@
}
}
- if (pts[pd.stream] & 1) { // active stream
- if (end && pd.pts > pts[pd.stream]>>1) {
+ if (state[pd.stream].active) {
+ if (end && pd.pts > state[pd.stream].pts) { // higher than requested pts
int n = 1;
- good_key[pd.stream] |= 1;
- for (i = 0; i < nut->stream_count; i++) {
- if ((pts[i] & 1) && !(good_key[i] & 1)) n = 0;
- }
+ state[pd.stream].pts_higher = 1;
+ for (i = 0; i < nut->stream_count; i++) if (state[i].active && !state[i].pts_higher) n = 0;
if (n) break; // pts for all active streams higher than requested pts
}
if (pd.flags & NUT_FLAG_KEY) {
- if (pd.pts <= pts[pd.stream]>>1) {
- good_key[pd.stream] = buf_before<<1;
- if (pd.flags & NUT_FLAG_EOR) good_key[pd.stream] = 0;
+ if (pd.pts <= state[pd.stream].pts) {
+ state[pd.stream].good_key = buf_before;
+ if (pd.flags & NUT_FLAG_EOR) state[pd.stream].good_key = 0;
}
- if (!end && pd.pts >= pts[pd.stream]>>1) { // forward seek end
- if (saw_syncpoint) nut->last_syncpoint = 0;
+ if (!end && pd.pts >= state[pd.stream].pts) { // forward seek end
nut->i->buf_ptr -= bctello(nut->i) - buf_before;
break;
}
}
- } else if (stopper && pd.flags&NUT_FLAG_KEY && !(good_key[pd.stream]&1)) {
+ } else if (stopper && pd.flags&NUT_FLAG_KEY) {
+ off_t back_ptr = stopper->pos - stopper->back_ptr;
TO_PTS(stopper, stopper->pts)
- if (compare_ts(nut, stopper_p, nut->tb[stopper_t], pd.pts, TO_TB(pd.stream)) > 0) {
- good_key[pd.stream] = buf_before<<1;
- if (stopper_syncpoint) {
+ // only relavent if pts is smaller than stopper, and we passed stopper's back_ptr
+ if (compare_ts(nut, pd.pts, TO_TB(pd.stream), stopper_p, nut->tb[stopper_t]) < 0 && buf_before >= back_ptr) {
+ if (!stopper_syncpoint) state[pd.stream].good_key = 1;
+ else if (state[pd.stream].good_key) {
int n = 1;
- for (i = 0; i < nut->stream_count; i++) {
- if (!(pts[i] & 1) && !(good_key[i]&1) && good_key[i]>>1 < stopper_syncpoint) n = 0;
- }
- // smart linear search stop, keyframe for every inactive stream after stopper_syncpoint
+ state[pd.stream].good_key = 0;
+ for (i = 0; i < nut->stream_count; i++) if (!state[i].active && state[i].good_key) n = 0;
+ // smart linear search stop
+ // keyframe for every inactive stream (which had a keyframe in stopper area), after stopper_syncpoint
if (n) break;
}
}
}
// dts higher than requested pts
- if (end && peek_dts(nut->sc[pd.stream].sh.decode_delay, nut->sc[pd.stream].pts_cache, pd.pts) > (int64_t)(pts[pd.stream]>>1)) break;
+ if (end && peek_dts(nut->sc[pd.stream].sh.decode_delay, nut->sc[pd.stream].pts_cache, pd.pts) > (int64_t)state[pd.stream].pts) break;
CHECK(skip_buffer(nut->i, pd.len));
push_frame(nut, &pd);
@@ -1085,12 +1073,12 @@
if (!end) goto err_out; // forward seek
for (i = 0; i < nut->stream_count; i++) {
- if (!(pts[i] & 1)) continue;
- if (good_key[i]>>1 && (!min_pos || good_key[i]>>1 < min_pos)) min_pos = good_key[i]>>1;
+ if (!state[i].active) continue;
+ if (state[i].good_key && (!min_pos || state[i].good_key < min_pos)) min_pos = state[i].good_key;
}
if (!min_pos) {
fprintf(stderr, "BIG FAT WARNING\n");
- for (i = 0; i < nut->stream_count; i++) fprintf(stderr, "%d: %d\n", i, (int)good_key[i]);
+ for (i = 0; i < nut->stream_count; i++) fprintf(stderr, "%d: %d\n", i, (int)state[i].good_key);
min_pos = nut->seek_status >> 1;
}
@@ -1118,9 +1106,7 @@
if (err != 2) { // unless EAGAIN
if (err) {
if (err == -ERR_NOT_SEEKABLE) { // a failed seek - then go back to before everything started
- for (i = 0; i < nut->stream_count; i++) {
- nut->sc[i].last_pts = old_last_pts[i];
- }
+ for (i = 0; i < nut->stream_count; i++) nut->sc[i].last_pts = state[i].old_last_pts;
seek_buf(nut->i, nut->before_seek, SEEK_SET);
} else { // some NUT error, let's just go back to last good syncpoint
err = 0;
@@ -1130,61 +1116,55 @@
clear_dts_cache(nut);
}
nut->seek_status = 0;
- free(nut->seek_state);
- nut->seek_state = NULL;
- } else {
- // for EAGAIN, store state
- if (!nut->seek_state) nut->seek_state = malloc(nut->stream_count * 2 * sizeof(*nut->seek_state));
- for (i = 0; i < nut->stream_count; i++) {
- nut->seek_state[i].good_key = good_key[i];
- nut->seek_state[i].old_last_pts = old_last_pts[i];
- }
- if (buf_before) nut->i->buf_ptr -= bctello(nut->i) - buf_before; // rewind
+ } else if (buf_before) {
+ nut->i->buf_ptr -= bctello(nut->i) - buf_before; // rewind
+ if (nut->i->buf_ptr < nut->i->buf) nut->i->buf_ptr = nut->i->buf; // special case, possible with find_syncpoint
}
return err;
}
-static void req_to_pts(nut_context_t * nut, double * time_pos, int flags, uint64_t * pts, const int * active_streams) {
- uint64_t orig_pts = 0;
- int orig_timebase = 0;
- int i;
-
- for (i = 0; i < nut->stream_count; i++) pts[i] = 0;
- if (active_streams) for (i = 0; active_streams[i] != -1; i++) pts[active_streams[i]] = 1;
- else for (i = 0; i < nut->stream_count; i++) pts[i] = 1;
-
- if (flags & 1) {
- for (i = 0; i < nut->stream_count; i++) {
- uint64_t dts = nut->sc[i].last_dts != -1 ? nut->sc[i].last_dts : nut->sc[i].last_pts;
- int FIXME; // this is completely wrong with EAGAIN
- if (!pts[i]) continue;
- if (compare_ts(nut, orig_pts, nut->tb[orig_timebase], dts, TO_TB(i)) < 0) {
- orig_pts = dts;
- orig_timebase = nut->sc[i].timebase_id;
- }
- }
- *time_pos += TO_DOUBLE(orig_timebase, orig_pts);
- }
- if (*time_pos < 0.) *time_pos = 0.;
-
- for (i = 0; i < nut->stream_count; i++) {
- pts[i] |= (uint64_t)(*time_pos / TO_TB(i).nom * TO_TB(i).den) << 1;
- }
-}
-
int nut_seek(nut_context_t * nut, double time_pos, int flags, const int * active_streams) {
int err = 0;
off_t start = 0, end = 0;
- uint64_t pts[nut->stream_count];
+ seek_state_t state[nut->stream_count];
int backwards = flags & 1 ? time_pos < 0 : 1;
syncpoint_t stopper = { 0, 0, 0, 0, 0 };
if (!nut->i->isc.seek) return -ERR_NOT_SEEKABLE;
- req_to_pts(nut, &time_pos, flags, pts, active_streams);
-
if (!nut->before_seek) nut->before_seek = bctello(nut->i);
+ if (!nut->seek_state) {
+ int i;
+ for (i = 0; i < nut->stream_count; i++) {
+ state[i].old_last_pts = nut->sc[i].last_pts;
+ state[i].active = active_streams ? 0 : 1;
+ state[i].good_key = state[i].pts_higher = 0;
+ }
+ if (active_streams) for (i = 0; active_streams[i] != -1; i++) state[active_streams[i]].active = 1;
+
+ if (flags & 1) { // relative seek
+ uint64_t orig_pts = 0;
+ int orig_timebase = 0;
+ for (i = 0; i < nut->stream_count; i++) {
+ uint64_t dts = nut->sc[i].last_dts != -1 ? nut->sc[i].last_dts : nut->sc[i].last_pts;
+ if (!state[i].active) continue;
+ if (compare_ts(nut, orig_pts, nut->tb[orig_timebase], dts, TO_TB(i)) < 0) {
+ orig_pts = dts;
+ orig_timebase = nut->sc[i].timebase_id;
+ }
+ }
+ time_pos += TO_DOUBLE(orig_timebase, orig_pts);
+ }
+ if (time_pos < 0.) time_pos = 0.;
+
+ for (i = 0; i < nut->stream_count; i++) state[i].pts = (uint64_t)(time_pos / TO_TB(i).nom * TO_TB(i).den);
+ nut->seek_time_pos = time_pos;
+ } else {
+ memcpy(state, nut->seek_state, sizeof state);
+ time_pos = nut->seek_time_pos;
+ }
+
if (nut->syncpoints.len) {
syncpoint_list_t * sl = &nut->syncpoints;
int i;
@@ -1199,18 +1179,18 @@
if (!sl->s[i].pts_valid) continue;
for (j = 0; j < nut->stream_count; j++) {
uint64_t tmp;
- if (!(pts[j]&1)) continue;
+ if (!state[j].active) continue;
tmp = sl->pts[i * nut->stream_count + j];
if (tmp--) { // -- because all pts array os off-by-one. zero indicate no keyframe.
- if (tmp > (pts[j]>>1)) { if (!last_sync) last_sync = i; }
+ if (tmp > state[j].pts) { if (!last_sync) last_sync = i; }
else sync[j] = (i-1);
}
tmp = sl->eor[i * nut->stream_count + j];
- if (tmp--) if (tmp <= (pts[j]>>1)) sync[j] = -(i+1); // flag stream eor
+ if (tmp--) if (tmp <= state[j].pts) sync[j] = -(i+1); // flag stream eor
}
}
for (i = 0; i < nut->stream_count; i++) {
- if (!(pts[i] & 1)) continue;
+ if (!state[i].active) continue;
if (sync[i] < -1) { backup = MAX(backup, -sync[i] - 1); continue; } // eor stream
if (good_sync == -2 || good_sync > sync[i]) good_sync = sync[i];
}
@@ -1232,9 +1212,9 @@
if (start) { // "unsuccessful" seek needs no linear search
if (!(flags & 2)) { // regular seek
- CHECK(linear_search_seek(nut, backwards, pts, start, end, stopper.pos ? &stopper : NULL));
+ CHECK(linear_search_seek(nut, backwards, state, start, end, stopper.pos ? &stopper : NULL));
} else { // forwards seek, find keyframe
- CHECK(linear_search_seek(nut, backwards, pts, end, 0, NULL));
+ CHECK(linear_search_seek(nut, backwards, state, end, 0, NULL));
}
}
fprintf(stderr, "DONE SEEK\n");
@@ -1242,6 +1222,11 @@
if (err != 2) { // unless EAGAIN
flush_buf(nut->i);
nut->before_seek = 0;
+ free(nut->seek_state);
+ nut->seek_state = NULL;
+ } else {
+ if (!nut->seek_state) nut->seek_state = malloc(sizeof state);
+ memcpy(nut->seek_state, state, sizeof state);
}
return err;
}
Modified: trunk/libnut/priv.h
==============================================================================
--- trunk/libnut/priv.h (original)
+++ trunk/libnut/priv.h Thu Sep 14 12:10:20 2006
@@ -122,6 +122,14 @@
int total_frames;
} stream_context_t;
+typedef struct {
+ int active;
+ uint64_t pts; // requested pts;
+ uint64_t old_last_pts;
+ off_t good_key;
+ int pts_higher; // for active streams
+} seek_state_t;
+
struct nut_context_s {
nut_muxer_opts_t mopts;
nut_demuxer_opts_t dopts;
@@ -147,10 +155,8 @@
off_t before_seek; // position before any seek mess
off_t seek_status;
- struct {
- off_t good_key;
- uint64_t old_last_pts;
- } * seek_state; // for linear search, so we can go back as if nothing happenned
+ seek_state_t * seek_state; // array per stream count
+ double seek_time_pos;
syncpoint_list_t syncpoints;
More information about the NUT-devel
mailing list