diff options
Diffstat (limited to 'demux/demux_mf.c')
-rw-r--r-- | demux/demux_mf.c | 178 |
1 files changed, 126 insertions, 52 deletions
diff --git a/demux/demux_mf.c b/demux/demux_mf.c index c4995a66c5..d971f04c96 100644 --- a/demux/demux_mf.c +++ b/demux/demux_mf.c @@ -15,6 +15,8 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ +#include <math.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> @@ -55,8 +57,9 @@ static void mf_add(mf_t *mf, const char *fname) MP_TARRAY_APPEND(mf, mf->names, mf->nr_of_files, entry); } -static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filename) +static mf_t *open_mf_pattern(void *talloc_ctx, struct demuxer *d, char *filename) { + struct mp_log *log = d->log; int error_count = 0; int count = 0; @@ -64,23 +67,34 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam mf->log = log; if (filename[0] == '@') { - FILE *lst_f = fopen(filename + 1, "r"); - if (lst_f) { - char *fname = talloc_size(mf, 512); - while (fgets(fname, 512, lst_f)) { - /* remove spaces from end of fname */ - char *t = fname + strlen(fname) - 1; - while (t > fname && mp_isspace(*t)) - *(t--) = 0; - if (!mp_path_exists(fname)) { - mp_verbose(log, "file not found: '%s'\n", fname); - } else { - mf_add(mf, fname); + struct stream *s = stream_create(filename + 1, + d->stream_origin | STREAM_READ, d->cancel, d->global); + if (s) { + while (1) { + char buf[512]; + int len = stream_read_peek(s, buf, sizeof(buf)); + if (!len) + break; + bstr data = (bstr){buf, len}; + int pos = bstrchr(data, '\n'); + data = bstr_splice(data, 0, pos < 0 ? data.len : pos + 1); + bstr fname = bstr_strip(data); + if (fname.len) { + if (bstrchr(fname, '\0') >= 0) { + mp_err(log, "invalid filename\n"); + break; + } + char *entry = bstrto0(mf, fname); + if (!mp_path_exists(entry)) { + mp_verbose(log, "file not found: '%s'\n", entry); + } else { + MP_TARRAY_APPEND(mf, mf->names, mf->nr_of_files, entry); + } } + stream_seek_skip(s, stream_tell(s) + data.len); } - fclose(lst_f); + free_stream(s); - mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } mp_info(log, "%s is not indirect filelist\n", filename + 1); @@ -102,19 +116,18 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam } talloc_free(fname2); } - mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } - char *fname = talloc_size(mf, strlen(filename) + 32); + size_t fname_avail = strlen(filename) + 32; + char *fname = talloc_size(mf, fname_avail); #if HAVE_GLOB if (!strchr(filename, '%')) { - strcpy(fname, filename); - if (!strchr(filename, '*')) - strcat(fname, "*"); - + // append * if none present + snprintf(fname, fname_avail, "%s%c", filename, + strchr(filename, '*') ? 0 : '*'); mp_info(log, "search expr: %s\n", fname); glob_t gg; @@ -128,16 +141,64 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam continue; mf_add(mf, gg.gl_pathv[i]); } - mp_info(log, "number of files: %d\n", mf->nr_of_files); globfree(&gg); goto exit_mf; } #endif + // We're using arbitrary user input as printf format with 1 int argument. + // Any format which uses exactly 1 int argument would be valid, but for + // simplicity we reject all conversion specifiers except %% and simple + // integer specifier: %[.][NUM]d where NUM is 1-3 digits (%.d is valid) + const char *f = filename; + int MAXDIGS = 3, nspec = 0, c; + bool bad_spec = false; + + while (nspec < 2 && (c = *f++)) { + if (c != '%') + continue; + + if (*f == '%') { + // '%%', which ends up as an explicit % in the output. + // Skipping forwards as it doesn't require further attention. + f++; + continue; + } + + // Now c == '%' and *f != '%', thus we have entered territory of format + // specifiers which we are interested in. + nspec++; + + if (*f == '.') + f++; + + for (int ndig = 0; mp_isdigit(*f) && ndig < MAXDIGS; ndig++, f++) + /* no-op */; + + if (*f != 'd') { + bad_spec = true; // not int, or beyond our validation capacity + break; + } + + // *f is 'd' + f++; + } + + // nspec==0 (zero specifiers) is rejected because fname wouldn't advance. + if (bad_spec || nspec != 1) { + mp_err(log, + "unsupported expr format: '%s' - exactly one format specifier of the form %%[.][NUM]d is expected\n", + filename); + goto exit_mf; + } + mp_info(log, "search expr: %s\n", filename); while (error_count < 5) { - sprintf(fname, filename, count++); + if (snprintf(fname, fname_avail, filename, count++) >= fname_avail) { + mp_err(log, "format result too long: '%s'\n", filename); + goto exit_mf; + } if (!mp_path_exists(fname)) { error_count++; mp_verbose(log, "file not found: '%s'\n", fname); @@ -146,9 +207,8 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam } } - mp_info(log, "number of files: %d\n", mf->nr_of_files); - exit_mf: + mp_info(log, "number of files: %d\n", mf->nr_of_files); return mf; } @@ -163,24 +223,24 @@ static mf_t *open_mf_single(void *talloc_ctx, struct mp_log *log, char *filename static void demux_seek_mf(demuxer_t *demuxer, double seek_pts, int flags) { mf_t *mf = demuxer->priv; - int newpos = seek_pts * mf->sh->codec->fps; + double newpos = seek_pts * mf->sh->codec->fps; if (flags & SEEK_FACTOR) newpos = seek_pts * (mf->nr_of_files - 1); - if (newpos < 0) - newpos = 0; - if (newpos >= mf->nr_of_files) - newpos = mf->nr_of_files; - mf->curr_frame = newpos; + if (flags & SEEK_FORWARD) { + newpos = ceil(newpos); + } else { + newpos = MPMIN(floor(newpos), mf->nr_of_files - 1); + } + mf->curr_frame = MPCLAMP((int)newpos, 0, mf->nr_of_files); } -// return value: -// 0 = EOF or no stream found -// 1 = successfully read a packet -static int demux_mf_fill_buffer(demuxer_t *demuxer) +static bool demux_mf_read_packet(struct demuxer *demuxer, + struct demux_packet **pkt) { mf_t *mf = demuxer->priv; if (mf->curr_frame >= mf->nr_of_files) - return 0; + return false; + bool ok = false; struct stream *entry_stream = NULL; if (mf->streams) @@ -188,8 +248,10 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer) struct stream *stream = entry_stream; if (!stream) { char *filename = mf->names[mf->curr_frame]; - if (filename) - stream = stream_open(filename, demuxer->global); + if (filename) { + stream = stream_create(filename, demuxer->stream_origin | STREAM_READ, + demuxer->cancel, demuxer->global); + } } if (stream) { @@ -201,7 +263,9 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer) memcpy(dp->buffer, data.start, data.len); dp->pts = mf->curr_frame / mf->sh->codec->fps; dp->keyframe = true; - demux_add_packet(mf->sh, dp); + dp->stream = mf->sh->index; + *pkt = dp; + ok = true; } } talloc_free(data.start); @@ -211,7 +275,11 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer) free_stream(stream); mf->curr_frame++; - return 1; + + if (!ok) + MP_ERR(demuxer, "error reading image file\n"); + + return true; } // map file extension/type to a codec name @@ -232,6 +300,10 @@ static const struct { { "jls", "ljpeg" }, { "thm", "mjpeg" }, { "db", "mjpeg" }, + { "pcd", "photocd" }, + { "pfm", "pfm" }, + { "phm", "phm" }, + { "hdr", "hdr" }, { "pcx", "pcx" }, { "png", "png" }, { "pns", "png" }, @@ -260,8 +332,12 @@ static const struct { { "pix", "brender_pix" }, { "exr", "exr" }, { "pic", "pictor" }, + { "qoi", "qoi" }, { "xface", "xface" }, { "xwd", "xwd" }, + { "svg", "svg" }, + { "webp", "webp" }, + { "jxl", "jpegxl" }, {0} }; @@ -296,7 +372,7 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check) if (strncmp(demuxer->stream->url, "mf://", 5) == 0 && demuxer->stream->info && strcmp(demuxer->stream->info->name, "mf") == 0) { - mf = open_mf_pattern(demuxer, demuxer->log, demuxer->stream->url + 5); + mf = open_mf_pattern(demuxer, demuxer, demuxer->stream->url + 5); } else { mf = open_mf_single(demuxer, demuxer->log, demuxer->stream->url); int bog = 0; @@ -306,15 +382,9 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check) if (!mf || mf->nr_of_files < 1) goto error; - double mf_fps; - char *mf_type; - mp_read_option_raw(demuxer->global, "mf-fps", &m_option_type_double, &mf_fps); - mp_read_option_raw(demuxer->global, "mf-type", &m_option_type_string, &mf_type); - const char *codec = mp_map_mimetype_to_video_codec(demuxer->stream->mime_type); - if (!codec || (mf_type && mf_type[0])) - codec = probe_format(mf, mf_type, check); - talloc_free(mf_type); + if (!codec || (demuxer->opts->mf_type && demuxer->opts->mf_type[0])) + codec = probe_format(mf, demuxer->opts->mf_type, check); if (!codec) goto error; @@ -322,12 +392,16 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check) // create a new video stream header struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO); - struct mp_codec_params *c = sh->codec; + if (mf->nr_of_files == 1) { + MP_VERBOSE(demuxer, "Assuming this is an image format.\n"); + sh->image = true; + } + struct mp_codec_params *c = sh->codec; c->codec = codec; c->disp_w = 0; c->disp_h = 0; - c->fps = mf_fps; + c->fps = demuxer->opts->mf_fps; c->reliable_fps = true; demux_add_sh_stream(demuxer, sh); @@ -350,7 +424,7 @@ static void demux_close_mf(demuxer_t *demuxer) const demuxer_desc_t demuxer_desc_mf = { .name = "mf", .desc = "image files (mf)", - .fill_buffer = demux_mf_fill_buffer, + .read_packet = demux_mf_read_packet, .open = demux_open_mf, .close = demux_close_mf, .seek = demux_seek_mf, |