예제 #1
0
// Iterate entries. The first call establishes the first entry. Returns false
// if no entry found, otherwise returns true and sets mpa->entry/entry_filename.
bool mp_archive_next_entry(struct mp_archive *mpa)
{
    mpa->entry = NULL;
    talloc_free(mpa->entry_filename);
    mpa->entry_filename = NULL;

    while (!mp_cancel_test(mpa->primary_src->cancel)) {
        struct archive_entry *entry;
        int r = archive_read_next_header(mpa->arch, &entry);
        if (r == ARCHIVE_EOF)
            break;
        if (r < ARCHIVE_OK)
            MP_ERR(mpa, "%s\n", archive_error_string(mpa->arch));
        if (r < ARCHIVE_WARN) {
            MP_FATAL(mpa, "could not read archive entry\n");
            break;
        }
        if (archive_entry_filetype(entry) != AE_IFREG)
            continue;
        // Some archives may have no filenames, or libarchive won't return some.
        const char *fn = archive_entry_pathname(entry);
        char buf[64];
        if (!fn || bstr_validate_utf8(bstr0(fn)) < 0) {
            snprintf(buf, sizeof(buf), "mpv_unknown#%d", mpa->entry_num);
            fn = buf;
        }
        mpa->entry = entry;
        mpa->entry_filename = talloc_strdup(mpa, fn);
        mpa->entry_num += 1;
        return true;
    }

    return false;
}
예제 #2
0
static int archive_entry_seek(stream_t *s, int64_t newpos)
{
    struct priv *p = s->priv;
    if (!p->mpa)
        return -1;
    if (archive_seek_data(p->mpa->arch, newpos, SEEK_SET) >= 0)
        return 1;
    // libarchive can't seek in most formats.
    if (newpos < s->pos) {
        // Hack seeking backwards into working by reopening the archive and
        // starting over.
        MP_VERBOSE(s, "trying to reopen archive for performing seek\n");
        if (reopen_archive(s) < STREAM_OK)
            return -1;
        s->pos = 0;
    }
    if (newpos > s->pos) {
        // For seeking forwards, just keep reading data (there's no libarchive
        // skip function either).
        char buffer[4096];
        while (newpos > s->pos) {
            if (mp_cancel_test(s->cancel))
                return -1;

            int size = MPMIN(newpos - s->pos, sizeof(buffer));
            int r = archive_read_data(p->mpa->arch, buffer, size);
            if (r < 0) {
                MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
                return -1;
            }
            s->pos += r;
        }
    }
    return 1;
}
예제 #3
0
파일: cache.c 프로젝트: ThreeGe/mpv
static int cache_fill_buffer(struct stream *cache, char *buffer, int max_len)
{
    struct priv *s = cache->priv;
    assert(s->cache_thread_running);

    pthread_mutex_lock(&s->mutex);

    if (cache->pos != s->read_filepos)
        MP_ERR(s, "!!! read_filepos differs !!! report this bug...\n");

    int readb = 0;
    if (max_len > 0) {
        double retry_time = 0;
        int64_t retry = s->reads - 1; // try at least 1 read on EOF
        while (1) {
            readb = read_buffer(s, buffer, max_len, s->read_filepos);
            s->read_filepos += readb;
            if (readb > 0)
                break;
            if (s->eof && s->read_filepos >= s->max_filepos && s->reads >= retry)
                break;
            s->idle = false;
            if (mp_cancel_test(s->cache->cancel))
                break;
            cache_wakeup_and_wait(s, &retry_time);
        }
    }

    // wakeup the cache thread, possibly make it read more data ahead
    pthread_cond_signal(&s->wakeup);
    pthread_mutex_unlock(&s->mutex);
    return readb;
}
예제 #4
0
static int archive_entry_seek(stream_t *s, int64_t newpos)
{
    struct priv *p = s->priv;
    if (p->mpa && !p->broken_seek) {
        locale_t oldlocale = uselocale(p->mpa->locale);
        int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET);
        uselocale(oldlocale);
        if (r >= 0)
            return 1;
        MP_WARN(s, "possibly unsupported seeking - switching to reopening\n");
        p->broken_seek = true;
        if (reopen_archive(s) < STREAM_OK)
            return -1;
    }
    // libarchive can't seek in most formats.
    if (newpos < s->pos) {
        // Hack seeking backwards into working by reopening the archive and
        // starting over.
        MP_VERBOSE(s, "trying to reopen archive for performing seek\n");
        if (reopen_archive(s) < STREAM_OK)
            return -1;
        s->pos = 0;
    }
    if (newpos > s->pos) {
        // For seeking forwards, just keep reading data (there's no libarchive
        // skip function either).
        char buffer[4096];
        while (newpos > s->pos) {
            if (mp_cancel_test(s->cancel))
                return -1;

            int size = MPMIN(newpos - s->pos, sizeof(buffer));
            locale_t oldlocale = uselocale(p->mpa->locale);
            int r = archive_read_data(p->mpa->arch, buffer, size);
            if (r <= 0) {
                if (r == 0 && newpos > p->entry_size) {
                    MP_ERR(s, "demuxer trying to seek beyond end of archive "
                           "entry\n");
                } else if (r == 0) {
                    MP_ERR(s, "end of archive entry reached while seeking\n");
                } else {
                    MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
                }
                uselocale(oldlocale);
                if (mp_archive_check_fatal(p->mpa, r)) {
                    mp_archive_free(p->mpa);
                    p->mpa = NULL;
                }
                return -1;
            }
            uselocale(oldlocale);
            s->pos += r;
        }
    }
    return 1;
}
예제 #5
0
파일: demux_playlist.c 프로젝트: 2ion/mpv
// Return true if this was a readable directory.
static bool scan_dir(struct pl_parser *p, char *path,
                     struct stat *dir_stack, int num_dir_stack,
                     char ***files, int *num_files)
{
    if (strlen(path) >= 8192 || num_dir_stack == MAX_DIR_STACK)
        return false; // things like mount bind loops

    DIR *dp = opendir(path);
    if (!dp) {
        MP_ERR(p, "Could not read directory.\n");
        return false;
    }

    struct dirent *ep;
    while ((ep = readdir(dp))) {
        if (ep->d_name[0] == '.')
            continue;

        if (mp_cancel_test(p->s->cancel))
            break;

        char *file = mp_path_join(p, path, ep->d_name);

        struct stat st;
        if (stat(file, &st) == 0 && S_ISDIR(st.st_mode)) {
            for (int n = 0; n < num_dir_stack; n++) {
                if (same_st(&dir_stack[n], &st)) {
                    MP_VERBOSE(p, "Skip recursive entry: %s\n", file);
                    goto skip;
                }
            }

            dir_stack[num_dir_stack] = st;
            scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files);
        } else {
            MP_TARRAY_APPEND(p, *files, *num_files, file);
        }

        skip: ;
    }

    closedir(dp);
    return true;
}
예제 #6
0
파일: cache.c 프로젝트: ThreeGe/mpv
static int cache_control(stream_t *cache, int cmd, void *arg)
{
    struct priv *s = cache->priv;
    int r = STREAM_ERROR;

    assert(cmd > 0);

    pthread_mutex_lock(&s->mutex);

    r = cache_get_cached_control(cache, cmd, arg);
    if (r != STREAM_ERROR)
        goto done;

    MP_VERBOSE(s, "blocking for STREAM_CTRL %d\n", cmd);

    s->control = cmd;
    s->control_arg = arg;
    double retry = 0;
    while (s->control != CACHE_CTRL_NONE) {
        if (mp_cancel_test(s->cache->cancel)) {
            s->eof = 1;
            r = STREAM_UNSUPPORTED;
            goto done;
        }
        cache_wakeup_and_wait(s, &retry);
    }
    r = s->control_res;
    if (s->control_flush) {
        stream_drop_buffers(cache);
        cache->pos = s->read_filepos;
    }

done:
    pthread_mutex_unlock(&s->mutex);
    return r;
}
예제 #7
0
파일: cache.c 프로젝트: ThreeGe/mpv
// return 1 on success, 0 if the cache is disabled/not needed, and -1 on error
// or if the cache is disabled
int stream_cache_init(stream_t *cache, stream_t *stream,
                      struct mp_cache_opts *opts)
{
    if (opts->size < 1)
        return 0;

    struct priv *s = talloc_zero(NULL, struct priv);
    s->log = cache->log;
    s->eof_pos = -1;

    cache_drop_contents(s);

    s->seek_limit = opts->seek_min * 1024ULL;
    s->back_size = opts->back_buffer * 1024ULL;

    int64_t cache_size = opts->size * 1024ULL;

    int64_t file_size = stream_get_size(stream);
    if (file_size >= 0)
        cache_size = MPMIN(cache_size, file_size);

    if (resize_cache(s, cache_size) != STREAM_OK) {
        MP_ERR(s, "Failed to allocate cache buffer.\n");
        talloc_free(s);
        return -1;
    }

    MP_VERBOSE(cache, "Cache size set to %lld KiB (%lld KiB backbuffer)\n",
               (long long)(s->buffer_size / 1024),
               (long long)(s->back_size / 1024));

    pthread_mutex_init(&s->mutex, NULL);
    pthread_cond_init(&s->wakeup, NULL);

    cache->priv = s;
    s->cache = cache;
    s->stream = stream;

    cache->seek = cache_seek;
    cache->fill_buffer = cache_fill_buffer;
    cache->control = cache_control;
    cache->close = cache_uninit;

    int64_t min = opts->initial * 1024ULL;
    if (min > s->buffer_size - FILL_LIMIT)
        min = s->buffer_size - FILL_LIMIT;

    s->seekable = stream->seekable;

    if (pthread_create(&s->cache_thread, NULL, cache_thread, s) != 0) {
        MP_ERR(s, "Starting cache thread failed.\n");
        return -1;
    }
    s->cache_thread_running = true;

    // wait until cache is filled with at least min bytes
    if (min < 1)
        return 1;
    for (;;) {
        if (mp_cancel_test(cache->cancel))
            return -1;
        int64_t fill;
        int idle;
        if (stream_control(s->cache, STREAM_CTRL_GET_CACHE_FILL, &fill) < 0)
            break;
        if (stream_control(s->cache, STREAM_CTRL_GET_CACHE_IDLE, &idle) < 0)
            break;
        MP_INFO(s, "\rCache fill: %5.2f%% "
                "(%" PRId64 " bytes)   ", 100.0 * fill / s->buffer_size, fill);
        if (fill >= min)
            break;
        if (idle)
            break;    // file is smaller than prefill size
        // Wake up if the cache is done reading some data (or on timeout/abort)
        pthread_mutex_lock(&s->mutex);
        s->control = CACHE_CTRL_PING;
        pthread_cond_signal(&s->wakeup);
        cache_wakeup_and_wait(s, &(double){0});
        pthread_mutex_unlock(&s->mutex);
    }
예제 #8
0
파일: cache.c 프로젝트: ThreeGe/mpv
// Runs in the cache thread.
// Returns true if reading was attempted, and the mutex was shortly unlocked.
static bool cache_fill(struct priv *s)
{
    int64_t read = s->read_filepos;
    int len = 0;

    // drop cache contents only if seeking backward or too much fwd.
    // This is also done for on-disk files, since it loses the backseek cache.
    // That in turn can cause major bandwidth increase and performance
    // issues with e.g. mov or badly interleaved files
    if (read < s->min_filepos || read > s->max_filepos + s->seek_limit) {
        MP_VERBOSE(s, "Dropping cache at pos %"PRId64", "
                   "cached range: %"PRId64"-%"PRId64".\n", read,
                   s->min_filepos, s->max_filepos);
        cache_drop_contents(s);
    }

    if (stream_tell(s->stream) != s->max_filepos && s->seekable) {
        MP_VERBOSE(s, "Seeking underlying stream: %"PRId64" -> %"PRId64"\n",
                   stream_tell(s->stream), s->max_filepos);
        stream_seek(s->stream, s->max_filepos);
        if (stream_tell(s->stream) != s->max_filepos)
            goto done;
    }

    if (mp_cancel_test(s->cache->cancel))
        goto done;

    // number of buffer bytes which should be preserved in backwards direction
    int64_t back = MPCLAMP(read - s->min_filepos, 0, s->back_size);

    // limit maximum readahead so that the backbuffer space is reserved, even
    // if the backbuffer is not used. limit it to ensure that we don't stall the
    // network when starting a file, or we wouldn't download new data until we
    // get new free space again. (unless everything fits in the cache.)
    if (s->stream_size > s->buffer_size)
        back = MPMAX(back, s->back_size);

    // number of buffer bytes that are valid and can be read
    int64_t newb = FFMAX(s->max_filepos - read, 0);

    // max. number of bytes that can be written (starting from max_filepos)
    int64_t space = s->buffer_size - (newb + back);

    // offset into the buffer that maps to max_filepos
    int64_t pos = s->max_filepos - s->offset;
    if (pos >= s->buffer_size)
        pos -= s->buffer_size; // wrap-around

    if (space < FILL_LIMIT) {
        s->idle = true;
        s->reads++; // don't stuck main thread
        return false;
    }

    // limit to end of buffer (without wrapping)
    if (pos + space >= s->buffer_size)
        space = s->buffer_size - pos;

    // limit read size (or else would block and read the entire buffer in 1 call)
    space = FFMIN(space, s->stream->read_chunk);

    // back+newb+space <= buffer_size
    int64_t back2 = s->buffer_size - (space + newb); // max back size
    if (s->min_filepos < (read - back2))
        s->min_filepos = read - back2;

    // The read call might take a long time and block, so drop the lock.
    pthread_mutex_unlock(&s->mutex);
    len = stream_read_partial(s->stream, &s->buffer[pos], space);
    pthread_mutex_lock(&s->mutex);

    // Do this after reading a block, because at least libdvdnav updates the
    // stream position only after actually reading something after a seek.
    if (s->start_pts == MP_NOPTS_VALUE) {
        double pts;
        if (stream_control(s->stream, STREAM_CTRL_GET_CURRENT_TIME, &pts) > 0)
            s->start_pts = pts;
    }

    s->max_filepos += len;
    if (pos + len == s->buffer_size)
        s->offset += s->buffer_size; // wrap...

done:
    s->eof = len <= 0;
    s->idle = s->eof;
    s->reads++;
    if (s->eof) {
        s->eof_pos = stream_tell(s->stream);
        MP_TRACE(s, "EOF reached.\n");
    }

    pthread_cond_signal(&s->wakeup);

    return true;
}