Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / libzmpeg3 / zio.C
diff --git a/cinelerra-5.1/libzmpeg3/zio.C b/cinelerra-5.1/libzmpeg3/zio.C
new file mode 100644 (file)
index 0000000..a0dee45
--- /dev/null
@@ -0,0 +1,927 @@
+#include "libzmpeg3.h"
+
+#ifdef USE_FUTEX
+typedef zmpeg3_t::zloc_t zzloc_t;
+
+int zzloc_t::
+zyield()
+{
+  return syscall(SYS_sched_yield);
+}
+
+int zzloc_t::
+zgettid()
+{
+  return syscall(SYS_gettid);
+}
+
+int zzloc_t::
+zwake(int nwakeups)
+{
+  int ret;
+  while( (ret=zfutex(FUTEX_WAKE, nwakeups)) < 0 );
+  return ret;
+}
+
+int zzloc_t::
+zwait(int val)
+{
+  return zfutex(FUTEX_WAIT, val);
+}
+
+int zzlock_t::
+zemsg1()
+{
+  fprintf(stderr,"unlocking and not locked\n");
+  return -1;
+}
+
+int zzlock_t::
+zlock(int v)
+{
+  if( v || zxchg(1,loc) >= 0 ) do {
+    zwait(1);
+  } while ( zxchg(1,loc) >= 0 );
+  return 1;
+}
+
+int zzlock_t::
+zunlock(int nwakeups)
+{
+  loc = -1;
+  return zwake(1);
+}
+
+void zzrwlock_t::
+zenter()
+{
+  zdecr(loc); lk.lock();
+  zincr(loc); lk.unlock();
+}
+
+void zzrwlock_t::
+zleave()
+{
+  if( lk.loc >= 0 )
+    zwake(1);
+}
+
+void zzrwlock_t::
+zwrite_enter(int r)
+{
+  lk.lock();
+  if( r < 0 ) zdecr(loc);
+  int v;  while( (v=loc) >= 0 ) zwait(v);
+}
+
+void zzrwlock_t::
+zwrite_leave(int r)
+{
+  if( r < 0 ) zincr(loc);
+  lk.unlock();
+}
+
+#else
+
+void zzrwlock_t::
+enter()
+{
+  lock();
+  while( blocking ) { unlock(); lk.lock(); lk.unlock(); lock(); }
+  ++users;
+  unlock();
+}
+
+void zzrwlock_t::
+leave()
+{
+  lock();
+  if( !--users && blocking ) wake();
+  unlock();
+}
+
+void zzrwlock_t::
+zwrite_enter(int r)
+{
+  lk.lock();
+  blocking = pthread_self();
+  lock();
+  if( r < 0 ) --users;
+  while( users ) { unlock(); wait(); lock(); }
+  unlock();
+}
+
+void zzrwlock_t::
+zwrite_leave(int r)
+{
+  if( r < 0 ) ++users;
+  blocking = 0; lk.unlock();
+}
+
+#endif
+
+zbuffer_t::
+buffer_t(zmpeg3_t *zsrc, int access)
+{
+  src = zsrc;
+  access_type = access;
+  alloc = !(access_type & io_SEQUENTIAL) ? IO_SIZE : SEQ_IO_SIZE;
+  reader_done = -1;
+  writer_done = -1;
+  ref_count = 1;
+  fd = -1;
+  reset();
+  owner = pthread_self();
+}
+
+zbuffer_t::
+~buffer_t()
+{
+  if( (access_type & io_THREADED) )
+    stop_reader();
+  if( data ) delete [] data;
+}
+
+void zbuffer_t::
+unblock()
+{
+  io_lock.unlock();
+  io_block.unblock();
+  ::sched_yield();
+  io_lock.lock();
+}
+
+void zbuffer_t::
+block()
+{
+  io_block.block();
+}
+
+void zbuffer_t::
+reader()
+{
+  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
+  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
+  lock();
+  while( !reader_done ) {
+    if( do_restart ) {
+      do_restart = 0;
+      restart(0);
+      restarted = 1;
+    }
+    if( size+MAX_IO_SIZE > alloc )
+      size = alloc - MAX_IO_SIZE;
+    unlock();
+    int64_t count = read_in(zmpeg3_t::MAX_IO_SIZE);
+    lock();
+    if( count > 0 ) {
+      file_pos += count;
+      fin = in;
+      if( (size+=count) > alloc )
+        size = alloc;
+      if( src->recd_fd >= 0 && the_writer )
+        write_lock.unblock();
+    }
+    unblock();
+  }
+  unlock();
+  reader_done = -1;
+}
+
+void *zbuffer_t::
+reader(void *the_buffer)
+{
+  buffer_t *b = (buffer_t *)the_buffer;
+  b->reader();
+  return 0;
+}
+
+void zbuffer_t::
+writer()
+{
+  const int mx_blksz = 0x100000;
+
+  while( !writer_done ) {
+    write_record(mx_blksz,0xfff);
+    if( file_pos - write_pos < mx_blksz )
+      write_lock.block();
+    else
+      sched_yield();
+  }
+
+  write_record(INT_MAX, 0);
+  writer_done = -1;
+}
+
+void *zbuffer_t::
+writer(void *the_buffer)
+{
+  buffer_t *b = (buffer_t *)the_buffer;
+  b->writer();
+  return 0;
+}
+
+int zbuffer_t::
+open_file(char *path)
+{
+//zmsgs("1 %s\n", path);
+  if( !open_count ) {
+    if( (access_type & io_THREADED) )
+      access_type |= io_SINGLE_ACCESS + io_SEQUENTIAL;
+    if( !(access_type & io_UNBUFFERED) ) {
+      if( fp == 0 ) {
+        if( !(fp=::fopen(path, "rb")) ) {
+          perrs("%s",path);
+          return 1;
+        }
+      }
+    }
+    else {
+      if( fd < 0 ) {
+        int mode = (access_type & io_NONBLOCK) ? O_RDONLY+O_NONBLOCK : O_RDONLY;
+        if( (fd=::open(path, mode)) < 0 ) {
+          perrs("%s",path);
+          return 1;
+        }
+      }
+    }
+    if( (access_type & io_THREADED) )
+      start_reader();
+    else
+      restart();
+  }
+  ++open_count;
+  return 0;
+}
+
+void zbuffer_t::
+reset()
+{
+  in = fin = out = size = 0;
+  file_pos = out_pos = file_nudge = 0;
+}
+
+void zbuffer_t::
+restart(int lk)
+{
+  if( lk ) lock();
+  reset();
+  if( !(access_type & io_SEQUENTIAL) ) {
+    if( !(access_type & io_UNBUFFERED) ) {
+      if( ::fseek(fp,0,SEEK_SET) < 0 )
+        perrs("fseek "_LD, file_pos);
+    }
+    else {
+      if( ::lseek(fd,0,SEEK_SET) < 0 )
+        perrs("lseek "_LD, file_pos);
+    }
+  }
+  if( lk ) unlock();
+}
+
+void zbuffer_t::
+start_reader()
+{
+  restart();
+  reader_done = 0;
+  pthread_create(&the_reader,0,reader,this);
+}
+
+void zbuffer_t::
+stop_reader()
+{
+  if( reader_done || !the_reader ) return;
+  reader_done = 1;
+  io_block.unblock();
+  int tmo = 10;
+  while( reader_done >= 0 && --tmo >= 0 ) usleep(100000);
+  if( tmo < 0 ) pthread_cancel(the_reader);
+  the_reader = 0;
+  stop_record();
+}
+
+int zbuffer_t::
+start_record()
+{
+  writer_done = 0;
+  pthread_create(&the_writer,0,writer,this);
+  return 0;
+}
+
+int zbuffer_t::
+stop_record()
+{
+  if( writer_done || !the_writer ) return 1;
+  writer_done = 1;
+  write_lock.unblock();
+  pthread_join(the_writer,0);
+  int tmo = 10;
+  while( writer_done >= 0 && --tmo >= 0 ) usleep(100000);
+  if( tmo < 0 ) pthread_cancel(the_writer);
+  the_writer = 0;
+  return 0;
+}
+
+int zbuffer_t::
+write_align(int sz)
+{
+  int isz = 2*src->packet_size;
+  if( sz > isz ) isz = sz;
+  uint8_t *pat = 0;
+  int psz = 0;
+  if( src->is_program_stream() ) {
+    static uint8_t pack_start[4] = { 0x00, 0x00, 0x01, 0xba };
+    pat = pack_start;  psz = sizeof(pack_start);
+  }
+  else if( src->is_transport_stream() ) {
+    static uint8_t sync_start[1] = { 0x47 };
+    pat = sync_start;  psz = sizeof(sync_start);
+  }
+  if( isz > out_pos ) isz = out_pos;
+  int64_t bsz = file_pos - out_pos;
+  int i = bsz>alloc ? 0 : alloc-bsz;
+  if( isz > i ) isz = i;
+  i = out - isz;
+  if( i < 0 ) i += alloc;
+  uint8_t *bfr = &data[i];
+  uint8_t *lmt = &data[alloc];
+  int64_t len = isz + bsz;
+  i = 0;
+  if( pat ) {
+    while( --len >= 0 ) {
+      if( *bfr++ != pat[i] ) { i = 0; }
+      else if( ++i >= psz ) break;
+      if( bfr >= lmt ) bfr = &data[0];
+    }
+  }
+  if( len < 0 ) return 1;
+  len += psz;
+  if( (bfr-=psz) < &data[0] ) bfr += alloc;
+  wout = bfr - data;
+  write_pos = file_pos - len;
+  return 0;
+}
+
+// write blocks of (mask+1) bytes of data at data+wout, update wout
+//   only write full blocks, fragments cause disk thrashing
+void zbuffer_t::
+write_record(int sz, int mask)
+{
+  int isz = file_pos - write_pos;
+  if( isz > sz ) isz = sz;
+  if( !(isz &= ~mask) ) return;
+//zmsgs(" isz=%d, file_pos=%ld, write_pos=%ld\n", isz, file_pos, write_pos);
+  write_pos += isz;
+  int len = isz;
+  int n = alloc - wout;
+  if( n > len ) n = len;
+  if( n > 0 ) {
+    src->write_record(&data[wout],n);
+    len -= n;
+  }
+  if( len > 0 )
+    src->write_record(&data[0],wout=len);
+  else
+    wout += n;
+}
+
+void zbuffer_t::
+close_file()
+{
+  if( open_count > 0 && --open_count == 0 ) {
+    if( (access_type & io_THREADED) )
+      stop_reader();
+    if( !src || !src->iopened ) {
+      if( !(access_type & io_UNBUFFERED) ) {
+        if( fp != 0 ) { fclose(fp); fp = 0; }
+      }
+      else {
+        if( fd >= 0 ) { close(fd); fd = -1; }
+      }
+    }
+  }
+}
+
+/* sole user of in ptr (except restart) */
+int64_t zbuffer_t::
+read_in(int64_t len)
+{
+  int64_t count = 0;
+  while( count < len ) {
+    int xfr = len - count;
+    int avl = alloc - in;
+    if( avl < xfr ) xfr = avl;
+    if( access_type & io_UNBUFFERED ) {
+      if( access_type & io_NONBLOCK ) {
+        avl = -1;
+        for( int i=10; avl<0 && --i>=0; ) { // 10 retries, 2 seconds
+          struct timeval tv;  tv.tv_sec = 0;  tv.tv_usec = 200000;
+          fd_set rd_fd;  FD_ZERO(&rd_fd);  FD_SET(fd, &rd_fd);
+          int ret = select(fd+1, &rd_fd, 0, 0, &tv);
+          if( !ret ) continue;
+          if( ret < 0 ) break;
+          if( !FD_ISSET(fd, &rd_fd) ) continue;
+          if( (ret=::read(fd, &data[in], xfr)) > 0 ) avl = ret;
+        }
+      }
+      else
+        avl = ::read(fd, &data[in], xfr);
+    }
+    else {
+      avl = ::fread(&data[in], 1, xfr, fp);
+      if( !avl && ferror(fp) ) avl = -1;
+    }
+    if( avl < 0 ) {
+      if( errno == EOVERFLOW ) {
+       zerr("Overflow\n");
+       continue;
+      }
+      ++errs;
+      if( !(access_type & io_ERRFAIL) ) {
+        if( errs < IO_ERR_LIMIT && xfr > ERR_PACKET_SIZE )
+          xfr = ERR_PACKET_SIZE;
+        memset(&data[in],0,xfr);
+        if( !(access_type & io_THREADED) ) {
+          int64_t pos = file_pos + count + xfr;
+          if( (access_type & io_UNBUFFERED) ) {
+            if( ::lseek(fd,pos,SEEK_SET) < 0 )
+              perrs("lseek pos "_LX,pos);
+          }
+          else {
+            if( ::fseek(fp,pos,SEEK_SET) < 0 )
+              perrs("fseek pos "_LX,pos);
+          }
+        }
+        avl = xfr;
+      }
+      else {
+        perrs("read pos "_LX,file_pos + count);
+        avl = 0;
+      }
+    }
+    else
+      errs = 0;
+    if( avl == 0 ) break;
+    if( paused ) continue;
+    count += avl;
+    in += avl;
+    if( (avl=in-alloc) >= 0 ) in = avl;
+  }
+  return count;
+}
+
+int zbuffer_t::
+wait_in(int64_t pos)
+{
+  int result = 0;
+  if( (access_type & io_THREADED) && !restarted ) {
+    while( !reader_done && !restarted && file_pos <= pos ) {
+      io_lock.unlock();
+      io_block.block();
+      io_lock.lock();
+    }
+  }
+  if( reader_done || restarted ) result = 1;
+  restarted = 0;
+  return result;
+}
+
+int zbuffer_t::
+seek_in(int64_t pos)
+{
+  int result = 0;
+  if( file_pos != pos ) {
+    if( !(access_type & io_SEQUENTIAL) ) {
+      result = (access_type & io_UNBUFFERED) ?
+        (lseek64(fd, pos, SEEK_SET) >= 0 ? 0 : 1) :
+        (fseeko(fp, pos, SEEK_SET) == 0 ? 0 : 1) ;
+    }
+    else if( pos != 0 ) {
+      if( (access_type & io_THREADED) )
+        src->restart();
+      zerrs("seek on sequential from " _LD " to " _LD "\n", file_pos, pos);
+      result = 1;
+    }
+    else {
+      restart(0);
+      result = 1;
+    }
+  }
+  return result;
+}
+
+int zbuffer_t::
+read_fin(int64_t len)
+{
+  int64_t count = read_in(len);
+  /* must already be locked */
+  fin = in;
+  file_pos += count;
+  if( (size+=count) > alloc )
+    size = alloc;
+  return len && count ? 0 : 1;
+}
+
+int zbuffer_t::
+seek_to(int64_t pos, int64_t len)
+{
+//zmsgs(" pos=%ld, len=%ld\n", pos, len);
+  int result = seek_in(pos);
+  if( !result ) {
+    file_pos = out_pos = pos;
+    in = size = 0;
+    result = read_fin(len);
+  }
+  return result;
+}
+
+int zbuffer_t::
+read_to(int64_t pos)
+{
+//zmsgs(" pos=%ld\n", pos);
+  int result = 0;
+  int64_t len = pos - file_pos;
+  if( len < 0 ) {
+    zerrs("reversed seq read ("_LD" < "_LD")\n", pos, file_pos);
+    result = 1;
+  }
+  else
+    result = read_fin(len);
+  return result;
+}
+
+int zbuffer_t::
+sync(int64_t pos)
+{
+  int result = 1;
+  pos += file_nudge;
+  int64_t start_pos = file_pos - size;
+  if( pos < start_pos ) { /* before buffer */
+    if( (access_type & io_THREADED) ) {
+      if( pos ) {
+        zerrs("threaded sync before buffer "_LD" < "_LD"\n", pos, start_pos);
+        int64_t mid_pos = start_pos + size/2;
+        file_nudge += mid_pos - pos;
+        pos = mid_pos;
+      }
+      else {
+        restart(0);  /* allow fake seek to start */
+        result = -1;
+      }
+    }
+    else
+      result = -1;
+  }
+  else
+    result = pos < file_pos ? 0 : /* in buffer */
+      (access_type & io_THREADED) ?
+        wait_in(pos) : -1; /* after buffer */
+  if( result < 0 ) { /* out of buffer */
+//zmsgs("out of buffer pos=%ld, start_pos=%ld, file_pos=%ld, out_pos=%ld\n",
+//  pos, start_pos, file_pos, out_pos);
+    int64_t seek_pos = pos - alloc/2;
+    int64_t end_pos = seek_pos + alloc;
+    if( seek_pos < 0 ) seek_pos = 0;
+    result = (access_type & io_SEQUENTIAL) ||
+        (seek_pos < file_pos && end_pos >= file_pos) ?
+      read_to(end_pos) : seek_to(seek_pos, alloc);
+  }
+  if( !result ) {
+    int64_t offset = file_pos - pos;
+    if( offset >= 0 && offset <= size ) {
+      if( (offset=fin-offset) < 0 ) offset += alloc;
+      out = offset;  out_pos = pos;
+    }
+    else
+      result = 1;
+  }
+  return result;
+}
+
+int zbuffer_t::
+read_out(uint8_t *bfr,int len)
+{
+  int count = 0;
+  while( count < len ) {
+    int avail = file_pos - out_pos;
+    if( avail <= 0 ) break;
+    int fragment_size = alloc - out;
+    if( fragment_size > avail ) fragment_size = avail;
+    avail = len - count;
+    if( fragment_size > avail ) fragment_size = avail;
+    memcpy(bfr, &data[out], fragment_size);
+    bfr += fragment_size;
+    out += fragment_size;
+    if( out >= alloc ) out = 0;
+    out_pos += fragment_size;
+    count += fragment_size;
+  }
+  return count;
+}
+
+static int fd_name(int fd,char *nm,int sz)
+{
+  char pfn[PATH_MAX];
+  snprintf(&pfn[0],sizeof(pfn),"/proc/self/fd/%d",fd);
+  return readlink(&pfn[0],nm,sz);
+}
+
+zfs_t::
+fs_t(zmpeg3_t *zsrc, const char *fpath, int access)
+{
+  src = zsrc;
+  strcpy(path, fpath);
+  buffer = new buffer_t(src, access);
+  if( (access & io_SINGLE_ACCESS) )
+    open_file();
+}
+
+zfs_t::
+fs_t(zmpeg3_t *zsrc, int fd, int access)
+{
+  src = zsrc;
+  access |= io_UNBUFFERED;
+  access |= io_SINGLE_ACCESS;
+  if( (access & io_THREADED) ) access |= io_SEQUENTIAL;
+  buffer = new buffer_t(src, access);
+  if( !fd_name(fd,&path[0],sizeof(path)) )
+    css.get_keys(&path[0]);
+  is_open = 1;
+  buffer->fd = fd;
+  buffer->open_count = 1;
+  get_total_bytes();
+  if( (buffer->access_type & io_THREADED) )
+    buffer->start_reader();
+}
+
+zfs_t::
+fs_t(zmpeg3_t *zsrc, FILE *fp, int access)
+{
+  src = zsrc;
+  access &= ~io_UNBUFFERED;
+  access |= io_SINGLE_ACCESS;
+  if( (access & io_THREADED) ) access |= io_SEQUENTIAL;
+  buffer = new buffer_t(src, access);
+  int fd = fileno(fp);
+  if( !fd_name(fd,&path[0],sizeof(path)) )
+    css.get_keys(&path[0]);
+  is_open = 1;
+  buffer->fp = fp;
+  buffer->open_count = 1;
+  get_total_bytes();
+  if( (buffer->access_type & io_THREADED) )
+    buffer->start_reader();
+}
+
+zfs_t::
+~fs_t()
+{
+  close_file();
+  close_buffer();
+}
+
+zfs_t::
+fs_t(zfs_t &fs)
+{
+  src = fs.src;
+  strcpy(path, fs.path);
+  total_bytes = fs.total_bytes;
+  css = fs.css;
+  if( !fs.buffer ) return;
+  int access = fs.buffer->access_type;
+  if( (access & io_SINGLE_ACCESS) ) {
+    buffer = fs.buffer;
+    ++buffer->ref_count;
+    is_open = fs.is_open;
+    if( is_open )
+      ++buffer->open_count;
+  }
+  else
+    buffer = new buffer_t(src, access);
+}
+
+uint8_t zfs_t::
+next_char()
+{
+  enter();
+  uint32_t ret = buffer->next_byte();
+  leave();
+  return ret;
+}
+
+uint8_t zfs_t::
+read_char()
+{
+  enter();
+  uint32_t result = buffer->get_byte();
+  ++current_byte;
+  leave();
+  return result;
+}
+
+uint32_t zfs_t::
+read_uint16()
+{
+  uint32_t a, b;
+  enter();
+  if( current_byte+2 < buffer->file_tell() ) {
+    b = buffer->get_byte();
+    a = buffer->get_byte();
+    current_byte += 2;
+  }
+  else {
+    b = buffer->get_byte(); chk_next();
+    a = buffer->get_byte(); ++current_byte;
+  }
+  leave();
+  uint32_t result = (b << 8) | (a);
+  return result;
+}
+
+uint32_t zfs_t::
+read_uint24()
+{
+  uint32_t a, b, c;
+  enter();
+  if( current_byte+3 < buffer->file_tell() ) {
+    c = buffer->get_byte();
+    b = buffer->get_byte();
+    a = buffer->get_byte();
+    current_byte += 3;
+  }
+  else {
+    c = buffer->get_byte(); chk_next();
+    b = buffer->get_byte(); chk_next();
+    a = buffer->get_byte(); ++current_byte;
+  }
+  leave();
+  uint32_t result = (c << 16) | (b << 8) | (a);
+  return result;
+}
+
+uint32_t zfs_t::
+read_uint32()
+{
+  uint32_t a, b, c, d;
+  enter();
+  if( current_byte+4 < buffer->file_tell() ) {
+    d = buffer->get_byte();
+    c = buffer->get_byte();
+    b = buffer->get_byte();
+    a = buffer->get_byte();
+    current_byte += 4;
+  }
+  else {
+    d = buffer->get_byte(); chk_next();
+    c = buffer->get_byte(); chk_next();
+    b = buffer->get_byte(); chk_next();
+    a = buffer->get_byte(); ++current_byte;
+  }
+  leave();
+  uint32_t result = (d << 24) | (c << 16) | (b << 8) | (a);
+  return result;
+}
+
+uint64_t zfs_t::
+read_uint64()
+{
+  uint32_t a, b, c, d;
+  uint64_t result;
+  enter();
+  if( current_byte+8 < buffer->file_tell() ) {
+    d = buffer->get_byte();
+    c = buffer->get_byte();
+    b = buffer->get_byte();
+    a = buffer->get_byte();
+    result = (d << 24) | (c << 16) | (b << 8) | (a);
+    d = buffer->get_byte();
+    c = buffer->get_byte();
+    b = buffer->get_byte();
+    a = buffer->get_byte();
+    current_byte += 8;
+  }
+  else {
+    d = buffer->get_byte(); chk_next();
+    c = buffer->get_byte(); chk_next();
+    b = buffer->get_byte(); chk_next();
+    a = buffer->get_byte(); chk_next();
+    result = (d << 24) | (c << 16) | (b << 8) | (a);
+    d = buffer->get_byte(); chk_next();
+    c = buffer->get_byte(); chk_next();
+    b = buffer->get_byte(); chk_next();
+    a = buffer->get_byte(); ++current_byte;
+  }
+  result = (result <<32 ) | (d << 24) | (c << 16) | (b << 8) | (a);
+  leave();
+  return result;
+}
+
+int64_t zfs_t::
+get_total_bytes()
+{
+  total_bytes = (buffer->access_type & io_SEQUENTIAL) ?
+    INT64_MAX : path_total_bytes(path);
+  return total_bytes;
+}
+
+int zfs_t::
+open_file()
+{
+  /* Need to perform authentication before reading a single byte. */
+//zmsgs("1 %s\n", path);
+  if( !is_open ) {
+    css.get_keys(path);
+    if( buffer->open_file(path) ) return 1; 
+    if( !get_total_bytes() ) {
+      buffer->close_file();
+      return 1;
+    }
+    current_byte = 0;
+    is_open = 1;
+  } 
+  return 0;
+}
+
+void zfs_t::
+close_file()
+{
+  if( is_open ) {
+    if( buffer ) 
+      buffer->close_file();
+    is_open = 0;
+  }
+}
+
+int zfs_t::
+read_data(uint8_t *bfr, int64_t len)
+{
+  int64_t n, count = 0;
+//zmsgs("1 %d\n", len);
+  int result = enter();
+  if( !result ) do {
+    n = buffer->read_out(&bfr[count],len-count);
+    current_byte += n;  count += n;
+  } while( n > 0 && count < len && !(result=sync()) );
+  leave();
+
+//zmsgs("100 %d %d\n", result, count);
+  return (result && count < len) ? 1 : 0;
+}
+
+int zfs_t::
+seek(int64_t byte)
+{
+//zmsgs("1 " _LD "\n", byte);
+  current_byte = byte;
+  int result = (current_byte < 0) || (current_byte > total_bytes);
+  return result;
+}
+
+int zfs_t::
+seek_relative(int64_t bytes)
+{
+//zmsgs("1 " _LD "\n", bytes);
+  current_byte += bytes;
+  int result = (current_byte < 0) || (current_byte > total_bytes);
+  return result;
+}
+
+void zfs_t::restart()
+{
+  if( !buffer ) return;
+  if( !(buffer->access_type & io_SINGLE_ACCESS) ) return;
+  if( (buffer->access_type & io_THREADED) )
+    buffer->do_restart = 1;
+  else
+    buffer->restart();
+}
+
+int zfs_t::
+pause_reader(int v)
+{
+  if( !buffer ) return 1;
+  buffer->paused = v;
+  return 0;
+}
+
+int zfs_t::
+start_record(int bsz)
+{
+  if( !buffer ) return -1;
+  buffer->write_align(bsz);
+  return buffer->start_record();
+}
+
+int zfs_t::
+stop_record()
+{
+  return buffer ? buffer->stop_record() : -1;
+}
+
+void zfs_t::
+close_buffer()
+{
+  if( !buffer || --buffer->ref_count > 0 ) return;
+  delete buffer;  buffer = 0;
+}
+