2009-05-08 23:51:13 +02:00
|
|
|
/*
|
|
|
|
* This file is part of MPlayer.
|
|
|
|
*
|
|
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2002-02-06 21:15:36 +01:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2012-02-03 08:05:11 +01:00
|
|
|
#include "osdep/io.h"
|
|
|
|
|
2011-09-02 07:02:08 +02:00
|
|
|
#include "talloc.h"
|
2002-02-06 21:15:36 +01:00
|
|
|
#include "config.h"
|
2013-08-06 22:41:30 +02:00
|
|
|
#include "mpvcore/mp_msg.h"
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2007-03-15 19:36:36 +01:00
|
|
|
#include "stream/stream.h"
|
2012-11-09 01:06:43 +01:00
|
|
|
#include "demux.h"
|
2002-02-06 21:15:36 +01:00
|
|
|
#include "stheader.h"
|
|
|
|
#include "mf.h"
|
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
#define MF_MAX_FILE_SIZE (1024 * 1024 * 256)
|
2012-11-16 19:12:56 +01:00
|
|
|
|
|
|
|
static void free_mf(mf_t *mf)
|
|
|
|
{
|
|
|
|
if (mf) {
|
|
|
|
for (int n = 0; n < mf->nr_of_files; n++)
|
|
|
|
free(mf->names[n]);
|
|
|
|
free(mf->names);
|
|
|
|
free(mf->streams);
|
|
|
|
free(mf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-22 19:13:29 +02:00
|
|
|
static void demux_seek_mf(demuxer_t *demuxer, float rel_seek_secs, int flags)
|
|
|
|
{
|
2013-11-11 18:48:31 +01:00
|
|
|
mf_t *mf = demuxer->priv;
|
|
|
|
int newpos = (flags & SEEK_ABSOLUTE) ? 0 : mf->curr_frame - 1;
|
|
|
|
|
|
|
|
if (flags & SEEK_FACTOR)
|
|
|
|
newpos += rel_seek_secs * (mf->nr_of_files - 1);
|
|
|
|
else
|
|
|
|
newpos += rel_seek_secs * mf->sh->fps;
|
|
|
|
if (newpos < 0)
|
|
|
|
newpos = 0;
|
|
|
|
if (newpos >= mf->nr_of_files)
|
|
|
|
newpos = mf->nr_of_files - 1;
|
|
|
|
mf->curr_frame = newpos;
|
2002-02-06 21:15:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// return value:
|
|
|
|
// 0 = EOF or no stream found
|
|
|
|
// 1 = successfully read a packet
|
2013-07-11 19:17:51 +02:00
|
|
|
static int demux_mf_fill_buffer(demuxer_t *demuxer)
|
|
|
|
{
|
2012-11-16 19:12:56 +01:00
|
|
|
mf_t *mf = demuxer->priv;
|
|
|
|
if (mf->curr_frame >= mf->nr_of_files)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
struct stream *entry_stream = NULL;
|
|
|
|
if (mf->streams)
|
|
|
|
entry_stream = mf->streams[mf->curr_frame];
|
|
|
|
struct stream *stream = entry_stream;
|
2013-03-19 01:54:45 +01:00
|
|
|
if (!stream) {
|
|
|
|
char *filename = mf->names[mf->curr_frame];
|
|
|
|
if (filename)
|
2013-07-11 21:10:42 +02:00
|
|
|
stream = stream_open(filename, demuxer->opts);
|
2013-03-19 01:54:45 +01:00
|
|
|
}
|
2012-11-16 19:12:56 +01:00
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
stream_seek(stream, 0);
|
2013-06-11 12:16:42 +02:00
|
|
|
bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE);
|
2012-11-16 19:12:56 +01:00
|
|
|
if (data.len) {
|
|
|
|
demux_packet_t *dp = new_demux_packet(data.len);
|
|
|
|
memcpy(dp->buffer, data.start, data.len);
|
|
|
|
dp->pts = mf->curr_frame / mf->sh->fps;
|
|
|
|
dp->keyframe = true;
|
2013-07-11 19:17:51 +02:00
|
|
|
demuxer_add_packet(demuxer, demuxer->streams[0], dp);
|
2012-11-16 19:12:56 +01:00
|
|
|
}
|
|
|
|
talloc_free(data.start);
|
|
|
|
}
|
|
|
|
|
2013-03-19 01:54:45 +01:00
|
|
|
if (stream && stream != entry_stream)
|
2012-11-16 19:12:56 +01:00
|
|
|
free_stream(stream);
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2012-11-16 19:12:56 +01:00
|
|
|
mf->curr_frame++;
|
|
|
|
return 1;
|
2002-02-06 21:15:36 +01:00
|
|
|
}
|
|
|
|
|
2013-02-24 17:23:38 +01:00
|
|
|
// map file extension/type to a codec name
|
2009-03-21 20:46:13 +01:00
|
|
|
|
2007-11-17 18:27:30 +01:00
|
|
|
static const struct {
|
2013-11-11 18:48:31 +01:00
|
|
|
const char *type;
|
|
|
|
const char *codec;
|
2007-11-17 18:27:30 +01:00
|
|
|
} type2format[] = {
|
2013-11-11 18:48:31 +01:00
|
|
|
{ "bmp", "bmp" },
|
|
|
|
{ "dpx", "dpx" },
|
|
|
|
{ "j2c", "jpeg2000" },
|
|
|
|
{ "j2k", "jpeg2000" },
|
|
|
|
{ "jp2", "jpeg2000" },
|
|
|
|
{ "jpc", "jpeg2000" },
|
|
|
|
{ "jpeg", "mjpeg" },
|
|
|
|
{ "jpg", "mjpeg" },
|
|
|
|
{ "jps", "mjpeg" },
|
|
|
|
{ "jls", "ljpeg" },
|
|
|
|
{ "thm", "mjpeg" },
|
|
|
|
{ "db", "mjpeg" },
|
|
|
|
{ "pcx", "pcx" },
|
|
|
|
{ "png", "png" },
|
|
|
|
{ "pns", "png" },
|
|
|
|
{ "ptx", "ptx" },
|
|
|
|
{ "tga", "targa" },
|
|
|
|
{ "tif", "tiff" },
|
|
|
|
{ "tiff", "tiff" },
|
|
|
|
{ "sgi", "sgi" },
|
|
|
|
{ "sun", "sunrast" },
|
|
|
|
{ "ras", "sunrast" },
|
|
|
|
{ "rs", "sunrast" },
|
|
|
|
{ "ra", "sunrast" },
|
|
|
|
{ "im1", "sunrast" },
|
|
|
|
{ "im8", "sunrast" },
|
|
|
|
{ "im24", "sunrast" },
|
|
|
|
{ "im32", "sunrast" },
|
|
|
|
{ "sunras", "sunrast" },
|
|
|
|
{ "xbm", "xbm" },
|
|
|
|
{ "pam", "pam" },
|
|
|
|
{ "pbm", "pbm" },
|
|
|
|
{ "pgm", "pgm" },
|
|
|
|
{ "pgmyuv", "pgmyuv" },
|
|
|
|
{ "ppm", "ppm" },
|
|
|
|
{ "pnm", "ppm" },
|
|
|
|
{ "gif", "gif" }, // usually handled by demux_lavf
|
|
|
|
{ "pix", "brender_pix" },
|
|
|
|
{ "exr", "exr" },
|
|
|
|
{ "pic", "pictor" },
|
|
|
|
{ "xface", "xface" },
|
|
|
|
{ "xwd", "xwd" },
|
|
|
|
{0}
|
2007-11-17 18:27:30 +01:00
|
|
|
};
|
|
|
|
|
2013-07-12 21:58:11 +02:00
|
|
|
static const char *probe_format(mf_t *mf, enum demux_check check)
|
2012-11-16 19:12:56 +01:00
|
|
|
{
|
2013-07-12 21:58:11 +02:00
|
|
|
if (check > DEMUX_CHECK_REQUEST)
|
2013-02-24 16:31:43 +01:00
|
|
|
return NULL;
|
2012-11-16 19:12:56 +01:00
|
|
|
char *type = mf_type;
|
|
|
|
if (!type || !type[0]) {
|
|
|
|
char *p = strrchr(mf->names[0], '.');
|
|
|
|
if (p)
|
|
|
|
type = p + 1;
|
|
|
|
}
|
2013-07-12 21:58:11 +02:00
|
|
|
for (int i = 0; type2format[i].type; i++) {
|
|
|
|
if (type && strcasecmp(type, type2format[i].type) == 0)
|
|
|
|
return type2format[i].codec;
|
2012-11-16 19:12:56 +01:00
|
|
|
}
|
2013-07-12 21:58:11 +02:00
|
|
|
if (check == DEMUX_CHECK_REQUEST) {
|
|
|
|
if (!mf_type) {
|
|
|
|
mp_msg(MSGT_DEMUX, MSGL_ERR,
|
|
|
|
"[demux_mf] file type was not set! (try --mf-type=ext)\n");
|
2013-11-11 18:48:31 +01:00
|
|
|
} else {
|
2013-07-12 21:58:11 +02:00
|
|
|
mp_msg(MSGT_DEMUX, MSGL_ERR,
|
|
|
|
"[demux_mf] --mf-type set to an unknown codec!\n");
|
|
|
|
}
|
2012-11-16 19:12:56 +01:00
|
|
|
}
|
2013-07-12 21:58:11 +02:00
|
|
|
return NULL;
|
2012-11-16 19:12:56 +01:00
|
|
|
}
|
2003-01-28 23:00:57 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
|
2013-07-11 20:08:12 +02:00
|
|
|
{
|
2013-11-11 18:48:31 +01:00
|
|
|
sh_video_t *sh_video = NULL;
|
|
|
|
mf_t *mf;
|
|
|
|
|
|
|
|
if (strncmp(demuxer->stream->url, "mf://", 5) == 0 &&
|
|
|
|
demuxer->stream->type == STREAMTYPE_MF)
|
|
|
|
mf = open_mf_pattern(demuxer->stream->url + 5);
|
|
|
|
else {
|
|
|
|
mf = open_mf_single(demuxer->stream->url);
|
|
|
|
mf->streams = calloc(1, sizeof(struct stream *));
|
|
|
|
mf->streams[0] = demuxer->stream;
|
|
|
|
}
|
2013-07-12 21:58:11 +02:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
if (!mf || mf->nr_of_files < 1)
|
|
|
|
goto error;
|
2006-02-14 10:34:30 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
const char *codec = probe_format(mf, check);
|
|
|
|
if (!codec)
|
|
|
|
goto error;
|
2003-01-28 23:00:57 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
mf->curr_frame = 0;
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
// create a new video stream header
|
|
|
|
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
|
|
|
|
sh_video = sh->video;
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
sh_video->gsh->codec = codec;
|
|
|
|
sh_video->disp_w = 0;
|
|
|
|
sh_video->disp_h = 0;
|
|
|
|
sh_video->fps = mf_fps;
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
mf->sh = sh_video;
|
|
|
|
demuxer->priv = (void *)mf;
|
|
|
|
demuxer->seekable = true;
|
2002-02-06 21:15:36 +01:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
return 0;
|
2012-11-16 19:12:56 +01:00
|
|
|
|
|
|
|
error:
|
2013-11-11 18:48:31 +01:00
|
|
|
free_mf(mf);
|
|
|
|
return -1;
|
2002-02-06 21:15:36 +01:00
|
|
|
}
|
2002-04-24 17:36:07 +02:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
static void demux_close_mf(demuxer_t *demuxer)
|
|
|
|
{
|
|
|
|
mf_t *mf = demuxer->priv;
|
2002-04-24 17:36:07 +02:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
free_mf(mf);
|
2002-04-24 17:36:07 +02:00
|
|
|
}
|
2005-08-05 21:57:47 +02:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
static int demux_control_mf(demuxer_t *demuxer, int cmd, void *arg)
|
|
|
|
{
|
|
|
|
mf_t *mf = demuxer->priv;
|
2006-07-28 20:19:44 +02:00
|
|
|
|
2013-11-11 18:48:31 +01:00
|
|
|
switch (cmd) {
|
2006-07-28 20:19:44 +02:00
|
|
|
case DEMUXER_CTRL_GET_TIME_LENGTH:
|
2013-11-11 18:48:31 +01:00
|
|
|
*((double *)arg) = (double)mf->nr_of_files / mf->sh->fps;
|
|
|
|
return DEMUXER_CTRL_OK;
|
2006-07-28 20:19:44 +02:00
|
|
|
|
|
|
|
default:
|
2013-11-11 18:48:31 +01:00
|
|
|
return DEMUXER_CTRL_NOTIMPL;
|
|
|
|
}
|
2006-07-28 20:19:44 +02:00
|
|
|
}
|
2005-08-05 21:57:47 +02:00
|
|
|
|
2008-01-13 17:00:39 +01:00
|
|
|
const demuxer_desc_t demuxer_desc_mf = {
|
2013-07-11 20:08:12 +02:00
|
|
|
.name = "mf",
|
2013-07-12 22:12:02 +02:00
|
|
|
.desc = "image files (mf)",
|
2013-07-11 20:08:12 +02:00
|
|
|
.fill_buffer = demux_mf_fill_buffer,
|
|
|
|
.open = demux_open_mf,
|
|
|
|
.close = demux_close_mf,
|
|
|
|
.seek = demux_seek_mf,
|
|
|
|
.control = demux_control_mf,
|
2005-08-05 21:57:47 +02:00
|
|
|
};
|