summaryrefslogtreecommitdiffstats
path: root/demux/demux_mf.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux_mf.c')
-rw-r--r--demux/demux_mf.c178
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,