
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;
participants (1)
-
ods15