1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-11 04:01:31 +00:00

demux, stream: remove unneeded crap (esp. optical discs and Linux TV)

I don't ever use them, so kill them.

Linux TV is excessively complex, and whenever I attempted to use it, it
didn't work well or would have required some major work to update it.
(For example, when I tried to use a webcam-type device with tv://, it
worked badly; even the libavdevice garbage worked better.)

The "program" property was rather complex and rather obscure. I didn't
ever use it. Should there ever be a proper use for it (maybe HLS stream
selection?), it should be rewritten anyway.
This commit is contained in:
wm4 2018-08-31 13:41:28 +02:00
parent 559a400ac3
commit 8f4844b35c
44 changed files with 9 additions and 13254 deletions

View File

@ -920,8 +920,7 @@ Input Commands that are Possibly Subject to Change
``--glsl-shader=file.glsl``.
Undocumented commands: ``tv-last-channel`` (TV/DVB only),
``ao-reload`` (experimental/internal).
Undocumented commands: ``ao-reload`` (experimental/internal).
Hooks
~~~~~
@ -1181,8 +1180,6 @@ Property list
``media-title``
If the currently played file has a ``title`` tag, use that.
Otherwise, if the media type is DVD, return the volume ID of DVD.
Otherwise, return the ``filename`` property.
``file-format``
@ -1295,40 +1292,6 @@ Property list
Current MKV edition number. Setting this property to a different value will
restart playback. The number of the first edition is 0.
``disc-titles``
Number of BD/DVD titles.
This has a number of sub-properties. Replace ``N`` with the 0-based edition
index.
``disc-titles/count``
Number of titles.
``disc-titles/id``
Title ID as integer. Currently, this is the same as the title index.
``disc-titles/length``
Length in seconds. Can be unavailable in a number of cases (currently
it works for libdvdnav only).
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
::
MPV_FORMAT_NODE_ARRAY
MPV_FORMAT_NODE_MAP (for each edition)
"id" MPV_FORMAT_INT64
"length" MPV_FORMAT_DOUBLE
``disc-title-list``
List of BD/DVD titles.
``disc-title`` (RW)
Current BD/DVD title number. Writing works only for ``dvdnav://`` and
``bd://`` (and aliases for these).
``chapters``
Number of chapters.
@ -1368,9 +1331,6 @@ Property list
"title" MPV_FORMAT_STRING
"default" MPV_FORMAT_FLAG
``angle`` (RW)
Current DVD angle.
``metadata``
Metadata key/value pairs.
@ -1843,18 +1803,6 @@ Property list
``osd-par``
Last known OSD display pixel aspect (can be 0).
``program`` (W)
Switch TS program (write-only).
``dvb-channel`` (W)
Pair of integers: card,channel of current DVB stream.
Can be switched to switch to another channel on the same card.
``dvb-channel-name`` (RW)
Name of current DVB program.
On write, a channel-switch to the named channel on the same
card is performed. Can also be used for channel switching.
``sub-text``
Return the current subtitle text. Formatting is stripped. If a subtitle
is selected, but no text is currently visible, or the subtitle is not
@ -1862,9 +1810,6 @@ Property list
This property is experimental and might be removed in the future.
``tv-brightness``, ``tv-contrast``, ``tv-saturation``, ``tv-hue`` (RW)
TV stuff.
``playlist-pos`` (RW)
Current position on playlist. The first entry is on position 0. Writing
to the property will restart playback at the written entry.

View File

@ -12,8 +12,8 @@ Track Selection
.. admonition:: Examples
- ``mpv dvd://1 --alang=hu,en`` chooses the Hungarian language track
on a DVD and falls back on English if Hungarian is not available.
- ``mpv file.mkv --alang=hu,en`` chooses the Hungarian language track
in a file and falls back on English if Hungarian is not available.
- ``mpv --alang=jpn example.mkv`` plays a Matroska file with Japanese
audio.
@ -25,8 +25,8 @@ Track Selection
.. admonition:: Examples
- ``mpv dvd://1 --slang=hu,en`` chooses the Hungarian subtitle track on
a DVD and falls back on English if Hungarian is not available.
- ``mpv file.mkv --slang=hu,en`` chooses the Hungarian subtitle track on
in a file and falls back on English if Hungarian is not available.
- ``mpv --slang=jpn example.mkv`` plays a Matroska file with Japanese
subtitles.
@ -281,10 +281,8 @@ Playback Control
This option does not prevent opening of paired subtitle files and such. Use
``--autoload-files=no`` to prevent this.
This option does not always work if you open non-files (for example using
``dvd://directory`` would open a whole bunch of files in the given
directory). Prefixing the filename with ``./`` if it doesn't start with
a ``/`` will avoid this.
This option does not always work if you open non-files. Prefixing the
filename with ``./`` if it doesn't start with a ``/`` will avoid this.
``--loop-playlist=<N|inf|force|no>``, ``--loop-playlist``
Loops playback ``N`` times. A value of ``1`` plays it one time (default),
@ -2613,85 +2611,6 @@ Window
``never`` asks the window manager to never disable the compositor.
Disc Devices
------------
``--cdrom-device=<path>``
Specify the CD-ROM device (default: ``/dev/cdrom``).
``--dvd-device=<path>``
Specify the DVD device or .iso filename (default: ``/dev/dvd``). You can
also specify a directory that contains files previously copied directly
from a DVD (with e.g. vobcopy).
.. admonition:: Example
``mpv dvd:// --dvd-device=/path/to/dvd/``
``--bluray-device=<path>``
(Blu-ray only)
Specify the Blu-ray disc location. Must be a directory with Blu-ray
structure.
.. admonition:: Example
``mpv bd:// --bluray-device=/path/to/bd/``
``--cdda-...``
These options can be used to tune the CD Audio reading feature of mpv.
``--cdda-speed=<value>``
Set CD spin speed.
``--cdda-paranoia=<0-2>``
Set paranoia level. Values other than 0 seem to break playback of
anything but the first track.
:0: disable checking (default)
:1: overlap checking only
:2: full data correction and verification
``--cdda-sector-size=<value>``
Set atomic read size.
``--cdda-overlap=<value>``
Force minimum overlap search during verification to <value> sectors.
``--cdda-toc-bias``
Assume that the beginning offset of track 1 as reported in the TOC
will be addressed as LBA 0. Some discs need this for getting track
boundaries correctly.
``--cdda-toc-offset=<value>``
Add ``<value>`` sectors to the values reported when addressing tracks.
May be negative.
``--cdda-skip=<yes|no>``
(Never) accept imperfect data reconstruction.
``--cdda-cdtext=<yes|no>``
Print CD text. This is disabled by default, because it ruins performance
with CD-ROM drives for unknown reasons.
``--dvd-speed=<speed>``
Try to limit DVD speed (default: 0, no change). DVD base speed is 1385
kB/s, so an 8x drive can read at speeds up to 11080 kB/s. Slower speeds
make the drive more quiet. For watching DVDs, 2700 kB/s should be quiet and
fast enough. mpv resets the speed to the drive default value on close.
Values of at least 100 mean speed in kB/s. Values less than 100 mean
multiples of 1385 kB/s, i.e. ``--dvd-speed=8`` selects 11080 kB/s.
.. note::
You need write access to the DVD device to change the speed.
``--dvd-angle=<ID>``
Some DVDs contain scenes that can be viewed from multiple angles.
This option tells mpv which angle to use (default: 1).
Equalizer
---------
@ -3085,8 +3004,7 @@ Input
``--input-cursor``, ``--no-input-cursor``
Permit mpv to receive pointer events reported by the video output
driver. Necessary to use the OSC, or to select the buttons in DVD menus.
Support depends on the VO in use.
driver. Necessary to use the OSC. Support depends on the VO in use.
``--input-media-keys=<yes|no>``
(OS X and Windows only)
@ -3648,191 +3566,6 @@ Terminal
``--msg-time``
Prepend timing information to each console message.
TV
--
``--tv-...``
These options tune various properties of the TV capture module. For
watching TV with mpv, use ``tv://`` or ``tv://<channel_number>`` or
even ``tv://<channel_name>`` (see option ``tv-channels`` for ``channel_name``
below) as a media URL. You can also use ``tv:///<input_id>`` to start
watching a video from a composite or S-Video input (see option ``input`` for
details).
``--tv-device=<value>``
Specify TV device (default: ``/dev/video0``).
``--tv-channel=<value>``
Set tuner to <value> channel.
``--no-tv-audio``
no sound
``--tv-automute=<0-255> (v4l and v4l2 only)``
If signal strength reported by device is less than this value, audio
and video will be muted. In most cases automute=100 will be enough.
Default is 0 (automute disabled).
``--tv-driver=<value>``
See ``--tv=driver=help`` for a list of compiled-in TV input drivers.
available: dummy, v4l2 (default: autodetect)
``--tv-input=<value>``
Specify input (default: 0 (TV), see console output for available
inputs).
``--tv-freq=<value>``
Specify the frequency to set the tuner to (e.g. 511.250). Not
compatible with the channels parameter.
``--tv-outfmt=<value>``
Specify the output format of the tuner with a preset value supported
by the V4L driver (YV12, UYVY, YUY2, I420) or an arbitrary format given
as hex value.
``--tv-width=<value>``
output window width
``--tv-height=<value>``
output window height
``--tv-fps=<value>``
framerate at which to capture video (frames per second)
``--tv-buffersize=<value>``
maximum size of the capture buffer in megabytes (default: dynamical)
``--tv-norm=<value>``
See the console output for a list of all available norms.
See also: ``--tv-normid``.
``--tv-normid=<value> (v4l2 only)``
Sets the TV norm to the given numeric ID. The TV norm depends on the
capture card. See the console output for a list of available TV norms.
``--tv-chanlist=<value>``
available: argentina, australia, china-bcast, europe-east,
europe-west, france, ireland, italy, japan-bcast, japan-cable,
newzealand, russia, southafrica, us-bcast, us-cable, us-cable-hrc
``--tv-channels=<chan>-<name>[=<norm>],<chan>-<name>[=<norm>],...``
Set names for channels.
.. note::
If <chan> is an integer greater than 1000, it will be treated as
frequency (in kHz) rather than channel name from frequency table.
Use _ for spaces in names (or play with quoting ;-) ). The channel
names will then be written using OSD, and the input commands
``tv_step_channel``, ``tv_set_channel`` and ``tv_last_channel``
will be usable for a remote control. Not compatible with
the ``frequency`` parameter.
.. note::
The channel number will then be the position in the 'channels'
list, beginning with 1.
.. admonition:: Examples
``tv://1``, ``tv://TV1``, ``tv_set_channel 1``,
``tv_set_channel TV1``
``--tv-[brightness|contrast|hue|saturation]=<-100-100>``
Set the image equalizer on the card.
``--tv-audiorate=<value>``
Set input audio sample rate.
``--tv-forceaudio``
Capture audio even if there are no audio sources reported by v4l.
``--tv-alsa``
Capture from ALSA.
``--tv-amode=<0-3>``
Choose an audio mode:
:0: mono
:1: stereo
:2: language 1
:3: language 2
``--tv-forcechan=<1-2>``
By default, the count of recorded audio channels is determined
automatically by querying the audio mode from the TV card. This option
allows forcing stereo/mono recording regardless of the amode option
and the values returned by v4l. This can be used for troubleshooting
when the TV card is unable to report the current audio mode.
``--tv-adevice=<value>``
Set an audio device. <value> should be ``/dev/xxx`` for OSS and a
hardware ID for ALSA. You must replace any ':' by a '.' in the
hardware ID for ALSA.
``--tv-audioid=<value>``
Choose an audio output of the capture card, if it has more than one.
``--tv-[volume|bass|treble|balance]=<0-100>``
These options set parameters of the mixer on the video capture card.
They will have no effect, if your card does not have one. For v4l2 50
maps to the default value of the control, as reported by the driver.
``--tv-gain=<0-100>``
Set gain control for video devices (usually webcams) to the desired
value and switch off automatic control. A value of 0 enables automatic
control. If this option is omitted, gain control will not be modified.
``--tv-immediatemode=<bool>``
A value of 0 means capture and buffer audio and video together. A
value of 1 (default) means to do video capture only and let the audio
go through a loopback cable from the TV card to the sound card.
``--tv-mjpeg``
Use hardware MJPEG compression (if the card supports it). When using
this option, you do not need to specify the width and height of the
output window, because mpv will determine it automatically from
the decimation value (see below).
``--tv-decimation=<1|2|4>``
choose the size of the picture that will be compressed by hardware
MJPEG compression:
:1: full size
- 704x576 PAL
- 704x480 NTSC
:2: medium size
- 352x288 PAL
- 352x240 NTSC
:4: small size
- 176x144 PAL
- 176x120 NTSC
``--tv-quality=<0-100>``
Choose the quality of the JPEG compression (< 60 recommended for full
size).
``--tv-scan-autostart``
Begin channel scanning immediately after startup (default: disabled).
``--tv-scan-period=<0.1-2.0>``
Specify delay in seconds before switching to next channel (default:
0.5). Lower values will cause faster scanning, but can detect inactive
TV channels as active.
``--tv-scan-threshold=<1-100>``
Threshold value for the signal strength (in percent), as reported by
the device (default: 50). A signal strength higher than this value will
indicate that the currently scanning channel is active.
Cache
-----
@ -3978,45 +3711,6 @@ Network
The bitrate as used is sent by the server, and there's no guarantee it's
actually meaningful.
DVB
---
``--dvbin-card=<1-4>``
Specifies using card number 1-4 (default: 1).
``--dvbin-file=<filename>``
Instructs mpv to read the channels list from ``<filename>``. The default is
in the mpv configuration directory (usually ``~/.config/mpv``) with the
filename ``channels.conf.{sat,ter,cbl,atsc}`` (based on your card type) or
``channels.conf`` as a last resort.
For DVB-S/2 cards, a VDR 1.7.x format channel list is recommended
as it allows tuning to DVB-S2 channels, enabling subtitles and
decoding the PMT (which largely improves the demuxing).
Classic mplayer format channel lists are still supported (without
these improvements), and for other card types, only limited VDR
format channel list support is implemented (patches welcome).
For channels with dynamic PID switching or incomplete
``channels.conf``, ``--dvbin-full-transponder`` or the magic PID
``8192`` are recommended.
``--dvbin-timeout=<1-30>``
Maximum number of seconds to wait when trying to tune a frequency before
giving up (default: 30).
``--dvbin-full-transponder=<yes|no>``
Apply no filters on program PIDs, only tune to frequency and pass full
transponder to demuxer.
The player frontend selects the streams from the full TS in this case,
so the program which is shown initially may not match the chosen channel.
Switching between the programs is possible by cycling the ``program``
property.
This is useful to record multiple programs on a single transponder,
or to work around issues in the ``channels.conf``.
It is also recommended to use this for channels which switch PIDs
on-the-fly, e.g. for regional news.
Default: ``no``
ALSA audio output options
-------------------------

View File

@ -49,12 +49,10 @@ extern const struct demuxer_desc demuxer_desc_edl;
extern const struct demuxer_desc demuxer_desc_cue;
extern const demuxer_desc_t demuxer_desc_rawaudio;
extern const demuxer_desc_t demuxer_desc_rawvideo;
extern const demuxer_desc_t demuxer_desc_tv;
extern const demuxer_desc_t demuxer_desc_mf;
extern const demuxer_desc_t demuxer_desc_matroska;
extern const demuxer_desc_t demuxer_desc_lavf;
extern const demuxer_desc_t demuxer_desc_playlist;
extern const demuxer_desc_t demuxer_desc_disc;
extern const demuxer_desc_t demuxer_desc_rar;
extern const demuxer_desc_t demuxer_desc_libarchive;
extern const demuxer_desc_t demuxer_desc_null;
@ -65,19 +63,14 @@ extern const demuxer_desc_t demuxer_desc_timeline;
* libraries and demuxers requiring binary support. */
const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_disc,
&demuxer_desc_edl,
&demuxer_desc_cue,
&demuxer_desc_rawaudio,
&demuxer_desc_rawvideo,
#if HAVE_TV
&demuxer_desc_tv,
#endif
&demuxer_desc_matroska,
#if HAVE_LIBARCHIVE
&demuxer_desc_libarchive,
#endif
&demuxer_desc_rar,
&demuxer_desc_lavf,
&demuxer_desc_mf,
&demuxer_desc_playlist,
@ -2309,7 +2302,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.access_references = opts->access_references,
.events = DEMUX_EVENT_ALL,
.duration = -1,
.extended_ctrls = stream->extended_ctrls,
};
demuxer->seekable = stream->seekable;

View File

@ -32,8 +32,6 @@
enum demux_ctrl {
DEMUXER_CTRL_SWITCHED_TRACKS = 1,
DEMUXER_CTRL_RESYNC,
DEMUXER_CTRL_IDENTIFY_PROGRAM,
DEMUXER_CTRL_STREAM_CTRL,
DEMUXER_CTRL_GET_READER_STATE,
DEMUXER_CTRL_GET_BITRATE_STATS, // double[STREAM_TYPE_COUNT]
@ -201,7 +199,6 @@ typedef struct demuxer {
bool fully_read;
bool is_network; // opened directly from a network stream
bool access_references; // allow opening other files/URLs
bool extended_ctrls; // supports some of BD/DVD/DVB/TV controls
// Bitmask of DEMUX_EVENT_*
int events;
@ -243,11 +240,6 @@ typedef struct demuxer {
struct stream *stream;
} demuxer_t;
typedef struct {
int progid; //program id
int aid, vid, sid; //audio, video and subtitle id
} demux_program_t;
void demux_free(struct demuxer *demuxer);
void demux_cancel_and_free(struct demuxer *demuxer);

View File

@ -1,379 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <math.h>
#include <assert.h>
#include "common/common.h"
#include "common/msg.h"
#include "stream/stream.h"
#include "video/mp_image.h"
#include "demux.h"
#include "stheader.h"
#include "video/csputils.h"
struct priv {
struct demuxer *slave;
// streams[slave_stream_index] == our_stream
struct sh_stream **streams;
int num_streams;
// This contains each DVD sub stream, or NULL. Needed because DVD packets
// can come arbitrarily late in the MPEG stream, so the slave demuxer
// might add the streams only later.
struct sh_stream *dvd_subs[32];
// Used to rewrite the raw MPEG timestamps to playback time.
double base_time; // playback display start time of current segment
double base_dts; // packet DTS that maps to base_time
double last_dts; // DTS of previously demuxed packet
bool seek_reinit; // needs reinit after seek
bool is_dvd, is_cdda;
};
// If the timestamp difference between subsequent packets is this big, assume
// a reset. It should be big enough to account for 1. low video framerates and
// large audio frames, and 2. bad interleaving.
#define DTS_RESET_THRESHOLD 5.0
static void reselect_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
int num_slave = demux_get_num_stream(p->slave);
for (int n = 0; n < MPMIN(num_slave, p->num_streams); n++) {
if (p->streams[n]) {
demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
MP_NOPTS_VALUE, demux_stream_is_selected(p->streams[n]));
}
}
}
static void get_disc_lang(struct stream *stream, struct sh_stream *sh, bool dvd)
{
struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
if (dvd && sh->type == STREAM_SUB)
req.id = req.id & 0x1F; // mpeg ID to index
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
if (req.name[0])
sh->lang = talloc_strdup(sh, req.name);
}
static void add_dvd_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct stream *stream = demuxer->stream;
if (!p->is_dvd)
return;
struct stream_dvd_info_req info;
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
for (int n = 0; n < MPMIN(32, info.num_subs); n++) {
struct sh_stream *sh = demux_alloc_sh_stream(STREAM_SUB);
sh->demuxer_id = n + 0x20;
sh->codec->codec = "dvd_subtitle";
get_disc_lang(stream, sh, true);
// p->streams _must_ match with p->slave->streams, so we can't add
// it yet - it has to be done when the real stream appears, which
// could be right on start, or any time later.
p->dvd_subs[n] = sh;
// emulate the extradata
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
struct mp_cmat cmatrix;
mp_get_csp_matrix(&csp, &cmatrix);
char *s = talloc_strdup(sh, "");
s = talloc_asprintf_append(s, "palette: ");
for (int i = 0; i < 16; i++) {
int color = info.palette[i];
int y[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
int c[3];
mp_map_fixp_color(&cmatrix, 8, y, 8, c);
color = (c[2] << 16) | (c[1] << 8) | c[0];
if (i != 0)
s = talloc_asprintf_append(s, ", ");
s = talloc_asprintf_append(s, "%06x", color);
}
s = talloc_asprintf_append(s, "\n");
sh->codec->extradata = s;
sh->codec->extradata_size = strlen(s);
demux_add_sh_stream(demuxer, sh);
}
}
}
static void add_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
for (int n = p->num_streams; n < demux_get_num_stream(p->slave); n++) {
struct sh_stream *src = demux_get_stream(p->slave, n);
if (src->type == STREAM_SUB) {
struct sh_stream *sub = NULL;
if (src->demuxer_id >= 0x20 && src->demuxer_id <= 0x3F)
sub = p->dvd_subs[src->demuxer_id - 0x20];
if (sub) {
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sub);
continue;
}
}
struct sh_stream *sh = demux_alloc_sh_stream(src->type);
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
// Copy all stream fields that might be relevant
*sh->codec = *src->codec;
sh->demuxer_id = src->demuxer_id;
if (src->type == STREAM_VIDEO) {
double ar;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
== STREAM_OK)
{
struct mp_image_params f = {.w = src->codec->disp_w,
.h = src->codec->disp_h};
mp_image_params_set_dsize(&f, 1728 * ar, 1728);
sh->codec->par_w = f.p_w;
sh->codec->par_h = f.p_h;
}
}
get_disc_lang(demuxer->stream, sh, p->is_dvd);
demux_add_sh_stream(demuxer, sh);
}
reselect_streams(demuxer);
}
static void d_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
struct priv *p = demuxer->priv;
if (p->is_cdda) {
demux_seek(p->slave, seek_pts, flags);
return;
}
if (flags & SEEK_FACTOR) {
double tmp = 0;
stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
seek_pts *= tmp;
}
MP_VERBOSE(demuxer, "seek to: %f\n", seek_pts);
double seek_arg[] = {seek_pts, flags};
stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, seek_arg);
demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
p->seek_reinit = true;
}
static void reset_pts(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
double base;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_TIME, &base) < 1)
base = 0;
MP_VERBOSE(demuxer, "reset to time: %f\n", base);
p->base_dts = p->last_dts = MP_NOPTS_VALUE;
p->base_time = base;
p->seek_reinit = false;
}
static int d_fill_buffer(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct demux_packet *pkt = demux_read_any_packet(p->slave);
if (!pkt)
return 0;
demux_update(p->slave);
if (p->seek_reinit)
reset_pts(demuxer);
add_streams(demuxer);
if (pkt->stream >= p->num_streams) { // out of memory?
talloc_free(pkt);
return 0;
}
struct sh_stream *sh = p->streams[pkt->stream];
if (!demux_stream_is_selected(sh)) {
talloc_free(pkt);
return 1;
}
if (p->is_cdda) {
demux_add_packet(sh, pkt);
return 1;
}
MP_TRACE(demuxer, "ipts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
if (sh->type == STREAM_SUB) {
if (p->base_dts == MP_NOPTS_VALUE)
MP_WARN(demuxer, "subtitle packet along PTS reset\n");
} else if (pkt->dts != MP_NOPTS_VALUE) {
// Use the very first DTS to rebase the start time of the MPEG stream
// to the playback time.
if (p->base_dts == MP_NOPTS_VALUE)
p->base_dts = pkt->dts;
if (p->last_dts == MP_NOPTS_VALUE)
p->last_dts = pkt->dts;
if (fabs(p->last_dts - pkt->dts) >= DTS_RESET_THRESHOLD) {
MP_WARN(demuxer, "PTS discontinuity: %f->%f\n", p->last_dts, pkt->dts);
p->base_time += p->last_dts - p->base_dts;
p->base_dts = pkt->dts - pkt->duration;
}
p->last_dts = pkt->dts;
}
if (p->base_dts != MP_NOPTS_VALUE) {
double delta = -p->base_dts + p->base_time;
if (pkt->pts != MP_NOPTS_VALUE)
pkt->pts += delta;
if (pkt->dts != MP_NOPTS_VALUE)
pkt->dts += delta;
}
MP_TRACE(demuxer, "opts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
demux_add_packet(sh, pkt);
return 1;
}
static void add_stream_chapters(struct demuxer *demuxer)
{
int num = 0;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &num) < 1)
return;
for (int n = 0; n < num; n++) {
double p = n;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p) < 1)
continue;
demuxer_add_chapter(demuxer, "", p, 0);
}
}
static int d_open(demuxer_t *demuxer, enum demux_check check)
{
struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
if (check != DEMUX_CHECK_FORCE)
return -1;
struct demuxer_params params = {
.force_format = "+lavf",
.does_not_own_stream = true,
};
struct stream *cur = demuxer->stream;
const char *sname = "";
if (cur->info)
sname = cur->info->name;
p->is_cdda = strcmp(sname, "cdda") == 0;
p->is_dvd = strcmp(sname, "dvd") == 0 ||
strcmp(sname, "ifo") == 0 ||
strcmp(sname, "dvdnav") == 0 ||
strcmp(sname, "ifo_dvdnav") == 0;
if (p->is_cdda)
params.force_format = "+rawaudio";
char *t = NULL;
stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t);
if (t) {
mp_tags_set_str(demuxer->metadata, "TITLE", t);
talloc_free(t);
}
// Initialize the playback time. We need to read _some_ data to get the
// correct stream-layer time (at least with libdvdnav).
stream_peek(demuxer->stream, 1);
reset_pts(demuxer);
p->slave = demux_open(demuxer->stream, &params, demuxer->global);
if (!p->slave)
return -1;
// So that we don't miss initial packets of delayed subtitle streams.
demux_set_stream_autoselect(p->slave, true);
// With cache enabled, the stream can be seekable. This causes demux_lavf.c
// (actually libavformat/mpegts.c) to seek sometimes when reading a packet.
// It does this to seek back a bit in case the current file position points
// into the middle of a packet.
if (!p->is_cdda) {
demuxer->stream->seekable = false;
// Can be seekable even if the stream isn't.
demuxer->seekable = true;
}
add_dvd_streams(demuxer);
add_streams(demuxer);
add_stream_chapters(demuxer);
double len;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) >= 1)
demuxer->duration = len;
demuxer->extended_ctrls = true;
return 0;
}
static void d_close(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
demux_free(p->slave);
}
static int d_control(demuxer_t *demuxer, int cmd, void *arg)
{
struct priv *p = demuxer->priv;
switch (cmd) {
case DEMUXER_CTRL_RESYNC:
demux_flush(p->slave);
break; // relay to slave demuxer
case DEMUXER_CTRL_SWITCHED_TRACKS:
reselect_streams(demuxer);
return CONTROL_OK;
}
return demux_control(p->slave, cmd, arg);
}
const demuxer_desc_t demuxer_desc_disc = {
.name = "disc",
.desc = "CD/DVD/BD wrapper",
.fill_buffer = d_fill_buffer,
.open = d_open,
.close = d_close,
.seek = d_seek,
.control = d_control,
};

View File

@ -204,7 +204,6 @@ typedef struct lavf_priv {
AVIOContext *pb;
struct sh_stream **streams; // NULL for unknown streams
int num_streams;
int cur_program;
char *mime_type;
double seek_delay;
@ -1080,79 +1079,8 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
switch (cmd) {
case DEMUXER_CTRL_SWITCHED_TRACKS:
{
select_tracks(demuxer, 0);
return CONTROL_OK;
}
case DEMUXER_CTRL_IDENTIFY_PROGRAM:
{
demux_program_t *prog = arg;
AVProgram *program;
int p, i;
int start;
add_new_streams(demuxer);
prog->vid = prog->aid = prog->sid = -2;
if (priv->avfc->nb_programs < 1)
return CONTROL_FALSE;
if (prog->progid == -1) {
p = 0;
while (p < priv->avfc->nb_programs && priv->avfc->programs[p]->id != priv->cur_program)
p++;
p = (p + 1) % priv->avfc->nb_programs;
} else {
for (i = 0; i < priv->avfc->nb_programs; i++)
if (priv->avfc->programs[i]->id == prog->progid)
break;
if (i == priv->avfc->nb_programs)
return CONTROL_FALSE;
p = i;
}
start = p;
redo:
prog->vid = prog->aid = prog->sid = -2;
program = priv->avfc->programs[p];
for (i = 0; i < program->nb_stream_indexes; i++) {
struct sh_stream *stream = priv->streams[program->stream_index[i]];
if (stream) {
switch (stream->type) {
case STREAM_VIDEO:
if (prog->vid == -2)
prog->vid = stream->demuxer_id;
break;
case STREAM_AUDIO:
if (prog->aid == -2)
prog->aid = stream->demuxer_id;
break;
case STREAM_SUB:
if (prog->sid == -2)
prog->sid = stream->demuxer_id;
break;
}
}
}
if (prog->progid == -1 && prog->vid == -2 && prog->aid == -2) {
p = (p + 1) % priv->avfc->nb_programs;
if (p == start)
return CONTROL_FALSE;
goto redo;
}
priv->cur_program = prog->progid = program->id;
mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->programs[p]->metadata);
update_metadata(demuxer);
// Enforce metadata update even if no explicit METADATA_UPDATED since we switched program.
demux_metadata_changed(demuxer);
return CONTROL_OK;
}
case DEMUXER_CTRL_RESYNC:
stream_drop_buffers(priv->stream);
avio_flush(priv->avfc->pb);
avformat_flush(priv->avfc);
return CONTROL_OK;
case DEMUXER_CTRL_REPLACE_STREAM:
if (priv->own_stream)
free_stream(priv->stream);

View File

@ -1,65 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/common.h"
#include "common/playlist.h"
#include "stream/stream.h"
#include "stream/rar.h"
#include "demux.h"
static int open_file(struct demuxer *demuxer, enum demux_check check)
{
if (!demuxer->access_references)
return -1;
if (RarProbe(demuxer->stream))
return -1;
int count;
rar_file_t **files;
if (RarParse(demuxer->stream, &count, &files))
return -1;
void *tmp = talloc_new(NULL);
talloc_steal(tmp, files);
struct playlist *pl = talloc_zero(demuxer, struct playlist);
demuxer->playlist = pl;
// make it load rar://
pl->disable_safety = true;
char *prefix = mp_url_escape(tmp, demuxer->stream->url, "~|");
for (int n = 0; n < count; n++) {
// stream_rar.c does the real work
playlist_add_file(pl,
talloc_asprintf(tmp, "rar://%s|%s", prefix, files[n]->name));
RarFileDelete(files[n]);
}
demuxer->filetype = "rar";
demuxer->fully_read = true;
talloc_free(tmp);
return 0;
}
const struct demuxer_desc demuxer_desc_rar = {
.name = "rar",
.desc = "Rar archive file",
.open = open_file,
};

View File

@ -1,267 +0,0 @@
/*
* Copyright (C) 2001 Alex Beregszaszi
*
* Feb 19, 2002: Significant rewrites by Charles R. Henrich (henrich@msu.edu)
* to add support for audio, and bktr *BSD support.
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "common/common.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "options/options.h"
#include "demux.h"
#include "codec_tags.h"
#include "audio/format.h"
#include "osdep/endian.h"
#include "stream/stream.h"
#include "stream/tv.h"
static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
{
tvi_handle_t *tvh;
const tvi_functions_t *funcs;
if (check > DEMUX_CHECK_REQUEST)
return -1;
if (!demuxer->stream->info || strcmp(demuxer->stream->info->name, "tv") != 0)
return -1;
tv_param_t *params = mp_get_config_group(demuxer, demuxer->global,
&tv_params_conf);
bstr urlparams = bstr0(demuxer->stream->path);
bstr channel, input;
bstr_split_tok(urlparams, "/", &channel, &input);
if (channel.len) {
talloc_free(params->channel);
params->channel = bstrto0(NULL, channel);
}
if (input.len) {
bstr r;
params->input = bstrtoll(input, &r, 0);
if (r.len) {
MP_ERR(demuxer->stream, "invalid input: '%.*s'\n", BSTR_P(input));
return -1;
}
}
assert(demuxer->priv==NULL);
if(!(tvh=tv_begin(params, demuxer->log))) return -1;
if (!tvh->functions->init(tvh->priv)) return -1;
tvh->demuxer = demuxer;
if (!open_tv(tvh)){
tv_uninit(tvh);
return -1;
}
funcs = tvh->functions;
demuxer->priv=tvh;
struct sh_stream *sh_v = demux_alloc_sh_stream(STREAM_VIDEO);
/* get IMAGE FORMAT */
int fourcc;
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &fourcc);
if (fourcc == MP_FOURCC_MJPEG || fourcc == MP_FOURCC_JPEG) {
sh_v->codec->codec = "mjpeg";
} else {
sh_v->codec->codec = "rawvideo";
sh_v->codec->codec_tag = fourcc;
}
/* set FPS and FRAMETIME */
if(!sh_v->codec->fps)
{
float tmp;
if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &tmp) != TVI_CONTROL_TRUE)
sh_v->codec->fps = 25.0f; /* on PAL */
else sh_v->codec->fps = tmp;
}
if (tvh->tv_param->fps != -1.0f)
sh_v->codec->fps = tvh->tv_param->fps;
/* If playback only mode, go to immediate mode, fail silently */
if(tvh->tv_param->immediate == 1)
{
funcs->control(tvh->priv, TVI_CONTROL_IMMEDIATE, 0);
tvh->tv_param->audio = 0;
}
/* set width */
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_v->codec->disp_w);
/* set height */
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_v->codec->disp_h);
demux_add_sh_stream(demuxer, sh_v);
demuxer->seekable = 0;
/* here comes audio init */
if (tvh->tv_param->audio && funcs->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
{
int audio_format;
/* yeah, audio is present */
funcs->control(tvh->priv, TVI_CONTROL_AUD_SET_SAMPLERATE,
&tvh->tv_param->audiorate);
if (funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_FORMAT, &audio_format) != TVI_CONTROL_TRUE)
goto no_audio;
switch(audio_format)
{
// This is the only format any of the current inputs generate.
case AF_FORMAT_S16:
break;
default:
MP_ERR(tvh, "Audio type '%s' unsupported!\n",
af_fmt_to_str(audio_format));
goto no_audio;
}
struct sh_stream *sh_a = demux_alloc_sh_stream(STREAM_AUDIO);
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLERATE,
&sh_a->codec->samplerate);
int nchannels = sh_a->codec->channels.num;
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS,
&nchannels);
mp_chmap_from_channels(&sh_a->codec->channels, nchannels);
// s16ne
mp_set_pcm_codec(sh_a->codec, true, false, 16, BYTE_ORDER == BIG_ENDIAN);
demux_add_sh_stream(demuxer, sh_a);
MP_VERBOSE(tvh, " TV audio: %d channels, %d bits, %d Hz\n",
nchannels, 16, sh_a->codec->samplerate);
}
no_audio:
if(!(funcs->start(tvh->priv))){
// start failed :(
tv_uninit(tvh);
return -1;
}
/* set color eq */
tv_set_color_options(tvh, TV_COLOR_BRIGHTNESS, tvh->tv_param->brightness);
tv_set_color_options(tvh, TV_COLOR_HUE, tvh->tv_param->hue);
tv_set_color_options(tvh, TV_COLOR_SATURATION, tvh->tv_param->saturation);
tv_set_color_options(tvh, TV_COLOR_CONTRAST, tvh->tv_param->contrast);
if(tvh->tv_param->gain!=-1)
if(funcs->control(tvh->priv,TVI_CONTROL_VID_SET_GAIN,&tvh->tv_param->gain)!=TVI_CONTROL_TRUE)
MP_WARN(tvh, "Unable to set gain control!\n");
demuxer->extended_ctrls = true;
return 0;
}
static void demux_close_tv(demuxer_t *demuxer)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demuxer->priv);
if (!tvh) return;
tv_uninit(tvh);
free(tvh);
demuxer->priv=NULL;
}
static int demux_tv_fill_buffer(demuxer_t *demux)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demux->priv);
demux_packet_t* dp;
unsigned int len=0;
struct sh_stream *want_audio = NULL, *want_video = NULL;
int num_streams = demux_get_num_stream(demux);
for (int n = 0; n < num_streams; n++) {
struct sh_stream *sh = demux_get_stream(demux, n);
if (!demux_has_packet(sh) && demux_stream_is_selected(sh)) {
if (sh->type == STREAM_AUDIO)
want_audio = sh;
if (sh->type == STREAM_VIDEO)
want_video = sh;
}
}
/* ================== ADD AUDIO PACKET =================== */
if (want_audio && tvh->tv_param->audio &&
tvh->functions->control(tvh->priv,
TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
{
len = tvh->functions->get_audio_framesize(tvh->priv);
dp=new_demux_packet(len);
if (dp) {
dp->keyframe = true;
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
demux_add_packet(want_audio, dp);
}
}
/* ================== ADD VIDEO PACKET =================== */
if (want_video && tvh->functions->control(tvh->priv,
TVI_CONTROL_IS_VIDEO, 0) == TVI_CONTROL_TRUE)
{
len = tvh->functions->get_video_framesize(tvh->priv);
dp=new_demux_packet(len);
if (dp) {
dp->keyframe = true;
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
demux_add_packet(want_video, dp);
}
}
if (tvh->tv_param->scan) tv_scan(tvh);
return 1;
}
static int demux_tv_control(demuxer_t *demuxer, int cmd, void *arg)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demuxer->priv);
if (cmd != DEMUXER_CTRL_STREAM_CTRL)
return CONTROL_UNKNOWN;
struct demux_ctrl_stream_ctrl *ctrl = arg;
ctrl->res = tv_stream_control(tvh, ctrl->ctrl, ctrl->arg);
return CONTROL_OK;
}
const demuxer_desc_t demuxer_desc_tv = {
.name = "tv",
.desc = "TV card demuxer",
.fill_buffer = demux_tv_fill_buffer,
.control = demux_tv_control,
.open = demux_open_tv,
.close = demux_close_tv,
};

View File

@ -55,9 +55,6 @@ static void print_version(struct mp_log *log)
mp_print_version(log, true);
}
extern const struct m_sub_options tv_params_conf;
extern const struct m_sub_options stream_cdda_conf;
extern const struct m_sub_options stream_dvb_conf;
extern const struct m_sub_options stream_lavf_conf;
extern const struct m_sub_options sws_conf;
extern const struct m_sub_options drm_conf;
@ -278,22 +275,6 @@ const struct m_sub_options mp_osd_render_sub_opts = {
.change_flags = UPDATE_OSD,
};
#undef OPT_BASE_STRUCT
#define OPT_BASE_STRUCT struct dvd_opts
const struct m_sub_options dvd_conf = {
.opts = (const struct m_option[]){
OPT_STRING("dvd-device", device, M_OPT_FILE),
OPT_INT("dvd-speed", speed, 0),
OPT_INTRANGE("dvd-angle", angle, 0, 1, 99),
{0}
},
.size = sizeof(struct dvd_opts),
.defaults = &(const struct dvd_opts){
.angle = 1,
},
};
#undef OPT_BASE_STRUCT
#define OPT_BASE_STRUCT struct filter_opts
@ -386,16 +367,10 @@ const m_option_t mp_opts[] = {
// ------------------------- stream options --------------------
#if HAVE_DVDREAD || HAVE_DVDNAV
OPT_SUBSTRUCT("", dvd_opts, dvd_conf, 0),
#endif /* HAVE_DVDREAD */
OPT_INTPAIR("chapter", chapterrange, 0, .deprecation_message = "instead of "
"--chapter=A-B use --start=#A --end=#B+1"),
OPT_CHOICE_OR_INT("edition", edition_id, 0, 0, 8190,
({"auto", -1})),
#if HAVE_LIBBLURAY
OPT_STRING("bluray-device", bluray_device, M_OPT_FILE),
#endif /* HAVE_LIBBLURAY */
// ------------------------- demuxer options --------------------
@ -447,11 +422,6 @@ const m_option_t mp_opts[] = {
OPT_STRINGLIST("display-tags", display_tags, 0),
#if HAVE_CDDA
OPT_SUBSTRUCT("cdda", stream_cdda_opts, stream_cdda_conf, 0),
OPT_STRING("cdrom-device", cdrom_device, M_OPT_FILE),
#endif
// demuxer.c - select audio/sub file/demuxer
OPT_PATHLIST("audio-files", audio_files, 0),
OPT_CLI_ALIAS("audio-file", "audio-files-append"),
@ -467,12 +437,6 @@ const m_option_t mp_opts[] = {
OPT_DOUBLE("mf-fps", mf_fps, 0),
OPT_STRING("mf-type", mf_type, 0),
#if HAVE_TV
OPT_SUBSTRUCT("tv", tv_params, tv_params_conf, 0),
#endif /* HAVE_TV */
#if HAVE_DVBIN
OPT_SUBSTRUCT("dvbin", stream_dvb_opts, stream_dvb_conf, 0),
#endif
OPT_SUBSTRUCT("", stream_lavf_opts, stream_lavf_conf, 0),
// ------------------------- a-v sync options --------------------
@ -763,7 +727,6 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("cursor-autohide-delay", "cursor-autohide"),
OPT_REPLACED("delay", "audio-delay"),
OPT_REMOVED("dumpstream", "use --stream-dump=<filename>"),
OPT_REPLACED("dvdangle", "dvd-angle"),
OPT_REPLACED("endpos", "length"),
OPT_REPLACED("font", "osd-font"),
OPT_REPLACED("forcedsubsonly", "sub-forced-only"),

View File

@ -284,15 +284,8 @@ typedef struct MPOpts {
int w32_priority;
struct tv_params *tv_params;
struct pvr_params *stream_pvr_opts;
struct cdda_params *stream_cdda_opts;
struct dvb_params *stream_dvb_opts;
struct stream_lavf_params *stream_lavf_opts;
char *cdrom_device;
char *bluray_device;
double mf_fps;
char *mf_type;

View File

@ -220,41 +220,7 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
void *tmp = talloc_new(NULL);
bstr file = p.arg;
char *file0 = bstrdup0(tmp, p.arg);
#if HAVE_GPL
// expand DVD filename entries like dvd://1-3 into component titles
if (bstr_startswith0(file, "dvd://")) {
int offset = 6;
char *splitpos = strstr(file0 + offset, "-");
if (splitpos != NULL) {
char *endpos;
int start_title = strtol(file0 + offset, &endpos, 10);
int end_title;
//entries like dvd://-2 imply start at title 1
if (start_title < 0) {
end_title = abs(start_title);
start_title = 1;
} else
end_title = strtol(splitpos + 1, &endpos, 10);
#define dvd_range(a) (a >= 0 && a < 255)
if (dvd_range(start_title) && dvd_range(end_title)
&& (start_title < end_title)) {
for (int j = start_title; j <= end_title; j++) {
char *f = talloc_asprintf(tmp, "dvd://%d%s", j,
endpos);
playlist_add_file(files, f);
}
} else
MP_ERR(config, "Invalid play entry %s\n", file0);
} else // dvd:// or dvd://x entry
playlist_add_file(files, file0);
} else {
process_non_option(files, file0);
}
#else
process_non_option(files, file0);
#endif
talloc_free(tmp);
}
}

View File

@ -901,39 +901,6 @@ static int mp_property_playback_time(void *ctx, struct m_property *prop,
return property_time(action, arg, get_playback_time(mpctx));
}
/// Current BD/DVD title (RW)
static int mp_property_disc_title(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct demuxer *d = mpctx->demuxer;
if (!d || !d->extended_ctrls)
return M_PROPERTY_UNAVAILABLE;
unsigned int title = -1;
switch (action) {
case M_PROPERTY_GET:
if (demux_stream_control(d, STREAM_CTRL_GET_CURRENT_TITLE, &title) < 0)
return M_PROPERTY_UNAVAILABLE;
*(int*)arg = title;
return M_PROPERTY_OK;
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_INT,
.flags = M_OPT_MIN,
.min = -1,
};
return M_PROPERTY_OK;
case M_PROPERTY_SET:
title = *(int*)arg;
if (demux_stream_control(d, STREAM_CTRL_SET_CURRENT_TITLE, &title) < 0)
return M_PROPERTY_NOT_IMPLEMENTED;
if (!mpctx->stop_play)
mpctx->stop_play = PT_CURRENT_ENTRY;
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
/// Current chapter (RW)
static int mp_property_chapter(void *ctx, struct m_property *prop,
int action, void *arg)
@ -1216,53 +1183,6 @@ static int property_list_editions(void *ctx, struct m_property *prop,
get_edition_entry, mpctx);
}
/// Number of titles in BD/DVD
static int mp_property_disc_titles(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct demuxer *demuxer = mpctx->demuxer;
unsigned int num_titles;
if (!demuxer || !demuxer->extended_ctrls ||
demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
&num_titles) < 1)
return M_PROPERTY_UNAVAILABLE;
return m_property_int_ro(action, arg, num_titles);
}
static int get_disc_title_entry(int item, int action, void *arg, void *ctx)
{
struct MPContext *mpctx = ctx;
struct demuxer *demuxer = mpctx->demuxer;
double len = item;
if (demux_stream_control(demuxer, STREAM_CTRL_GET_TITLE_LENGTH, &len) < 1)
len = -1;
struct m_sub_property props[] = {
{"id", SUB_PROP_INT(item)},
{"length", {.type = CONF_TYPE_TIME}, {.time = len},
.unavailable = len < 0},
{0}
};
return m_property_read_sub(props, action, arg);
}
static int mp_property_list_disc_titles(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct demuxer *demuxer = mpctx->demuxer;
unsigned int num_titles;
if (!demuxer || !demuxer->extended_ctrls ||
demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
&num_titles) < 1)
return M_PROPERTY_UNAVAILABLE;
return m_property_read_list(action, arg, num_titles,
get_disc_title_entry, mpctx);
}
/// Number of chapters in file
static int mp_property_chapters(void *ctx, struct m_property *prop,
int action, void *arg)
@ -1286,67 +1206,6 @@ static int mp_property_editions(void *ctx, struct m_property *prop,
return m_property_int_ro(action, arg, demuxer->num_editions);
}
/// Current dvd angle (RW)
static int mp_property_angle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer || !demuxer->extended_ctrls)
return M_PROPERTY_UNAVAILABLE;
int ris, angles = -1, angle = 1;
ris = demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_ANGLES, &angles);
if (ris == STREAM_UNSUPPORTED)
return M_PROPERTY_UNAVAILABLE;
ris = demux_stream_control(demuxer, STREAM_CTRL_GET_ANGLE, &angle);
if (ris == STREAM_UNSUPPORTED)
return -1;
if (angle < 0 || angles <= 1)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_GET:
*(int *) arg = angle;
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
*(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
return M_PROPERTY_OK;
}
case M_PROPERTY_SET:
angle = *(int *)arg;
if (angle < 0 || angle > angles)
return M_PROPERTY_ERROR;
demux_flush(demuxer);
ris = demux_stream_control(demuxer, STREAM_CTRL_SET_ANGLE, &angle);
if (ris == STREAM_OK) {
demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL);
demux_flush(demuxer);
}
reset_audio_state(mpctx);
reset_video_state(mpctx);
mp_wakeup_core(mpctx);
return ris == STREAM_OK ? M_PROPERTY_OK : M_PROPERTY_ERROR;
case M_PROPERTY_GET_TYPE: {
struct m_option opt = {
.type = CONF_TYPE_INT,
.flags = CONF_RANGE,
.min = 1,
.max = angles,
};
*(struct m_option *)arg = opt;
return M_PROPERTY_OK;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int get_tag_entry(int item, int action, void *arg, void *ctx)
{
struct mp_tags *tags = ctx;
@ -2232,63 +2091,6 @@ static int mp_property_video(void *ctx, struct m_property *prop,
return property_switch_track(prop, action, arg, ctx, 0, STREAM_VIDEO);
}
static struct track *find_track_by_demuxer_id(MPContext *mpctx,
enum stream_type type,
int demuxer_id)
{
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->type == type && track->demuxer_id == demuxer_id)
return track;
}
return NULL;
}
static int mp_property_program(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
demux_program_t prog = {0};
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer || !mpctx->playback_initialized)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_SWITCH:
case M_PROPERTY_SET:
if (action == M_PROPERTY_SET && arg)
prog.progid = *((int *) arg);
else
prog.progid = -1;
if (demux_control(demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM, &prog) ==
CONTROL_UNKNOWN)
return M_PROPERTY_ERROR;
if (prog.aid < 0 && prog.vid < 0) {
MP_ERR(mpctx, "Selected program contains no audio or video streams!\n");
return M_PROPERTY_ERROR;
}
mp_switch_track(mpctx, STREAM_VIDEO,
find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.vid), 0);
mp_switch_track(mpctx, STREAM_AUDIO,
find_track_by_demuxer_id(mpctx, STREAM_AUDIO, prog.aid), 0);
mp_switch_track(mpctx, STREAM_SUB,
find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.sid), 0);
print_track_list(mpctx, "Program switched:");
return M_PROPERTY_OK;
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_INT,
.flags = CONF_RANGE,
.min = -1,
.max = (1 << 16) - 1,
};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_hwdec(void *ctx, struct m_property *prop,
int action, void *arg)
{
@ -2998,163 +2800,6 @@ static int mp_property_cursor_autohide(void *ctx, struct m_property *prop,
return r;
}
static int prop_stream_ctrl(struct MPContext *mpctx, int ctrl, void *arg)
{
if (!mpctx->demuxer || !mpctx->demuxer->extended_ctrls)
return M_PROPERTY_UNAVAILABLE;
int r = demux_stream_control(mpctx->demuxer, ctrl, arg);
switch (r) {
case STREAM_OK: return M_PROPERTY_OK;
case STREAM_UNSUPPORTED: return M_PROPERTY_UNAVAILABLE;
default: return M_PROPERTY_ERROR;
}
}
static int mp_property_tv_norm(void *ctx, struct m_property *prop,
int action, void *arg)
{
switch (action) {
case M_PROPERTY_SET:
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_SET_NORM, *(char **)arg);
case M_PROPERTY_SWITCH:
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_STEP_NORM, NULL);
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_tv_scan(void *ctx, struct m_property *prop,
int action, void *arg)
{
switch (action) {
case M_PROPERTY_SET:
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_SET_SCAN, arg);
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
/// TV color settings (RW)
static int mp_property_tv_color(void *ctx, struct m_property *prop,
int action, void *arg)
{
int req[2] = {(intptr_t)prop->priv};
switch (action) {
case M_PROPERTY_SET:
req[1] = *(int *)arg;
return prop_stream_ctrl(ctx, STREAM_CTRL_SET_TV_COLORS, req);
case M_PROPERTY_GET: {
int r = prop_stream_ctrl(ctx, STREAM_CTRL_GET_TV_COLORS, req);
if (r == M_PROPERTY_OK)
*(int *)arg = req[1];
return r;
}
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_INT,
.flags = M_OPT_RANGE,
.min = -100,
.max = 100,
};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_tv_freq(void *ctx, struct m_property *prop,
int action, void *arg)
{
switch (action) {
case M_PROPERTY_SET:
return prop_stream_ctrl(ctx, STREAM_CTRL_SET_TV_FREQ, arg);
case M_PROPERTY_GET:
return prop_stream_ctrl(ctx, STREAM_CTRL_GET_TV_FREQ, arg);
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLOAT};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_tv_channel(void *ctx, struct m_property *prop,
int action, void *arg)
{
switch (action) {
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
return M_PROPERTY_OK;
case M_PROPERTY_SET:
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_SET_CHAN, *(char **)arg);
case M_PROPERTY_GET:
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_GET_CHAN, arg);
case M_PROPERTY_SWITCH: {
struct m_property_switch_arg *sa = arg;
int dir = sa->inc >= 0 ? 1 : -1;
return prop_stream_ctrl(ctx, STREAM_CTRL_TV_STEP_CHAN, &dir);
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_dvb_channel(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
int r;
switch (action) {
case M_PROPERTY_SET:
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_SET_CHANNEL, arg);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_CURRENT_ENTRY;
return r;
case M_PROPERTY_SWITCH: {
struct m_property_switch_arg *sa = arg;
int dir = sa->inc >= 0 ? 1 : -1;
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_STEP_CHANNEL, &dir);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_CURRENT_ENTRY;
return r;
}
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = &m_option_type_intpair};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_dvb_channel_name(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
int r;
switch (action) {
case M_PROPERTY_SET:
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_SET_CHANNEL_NAME, arg);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_CURRENT_ENTRY;
return r;
case M_PROPERTY_SWITCH: {
struct m_property_switch_arg *sa = arg;
int dir = sa->inc >= 0 ? 1 : -1;
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_STEP_CHANNEL, &dir);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_CURRENT_ENTRY;
return r;
}
case M_PROPERTY_GET: {
return prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_GET_CHANNEL_NAME, arg);
}
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_playlist_pos_x(void *ctx, struct m_property *prop,
int action, void *arg, int base)
{
@ -3752,13 +3397,10 @@ static const struct m_property mp_properties_base[] = {
{"audio-pts", mp_property_audio_pts},
{"playtime-remaining", mp_property_playtime_remaining},
{"playback-time", mp_property_playback_time},
{"disc-title", mp_property_disc_title},
{"chapter", mp_property_chapter},
{"edition", mp_property_edition},
{"disc-titles", mp_property_disc_titles},
{"chapters", mp_property_chapters},
{"editions", mp_property_editions},
{"angle", mp_property_angle},
{"metadata", mp_property_metadata},
{"filtered-metadata", mp_property_filtered_metadata},
{"chapter-metadata", mp_property_chapter_metadata},
@ -3785,7 +3427,6 @@ static const struct m_property mp_properties_base[] = {
{"chapter-list", mp_property_list_chapters},
{"track-list", property_list_tracks},
{"edition-list", property_list_editions},
{"disc-title-list", mp_property_list_disc_titles},
{"playlist", mp_property_playlist},
{"playlist-pos", mp_property_playlist_pos},
@ -3832,7 +3473,6 @@ static const struct m_property mp_properties_base[] = {
{"estimated-vf-fps", mp_property_vf_fps},
{"video-aspect", mp_property_aspect},
{"vid", mp_property_video},
{"program", mp_property_program},
{"hwdec", mp_property_hwdec},
{"hwdec-current", mp_property_hwdec_current},
{"hwdec-interop", mp_property_hwdec_interop},
@ -3871,19 +3511,6 @@ static const struct m_property mp_properties_base[] = {
PROPERTY_BITRATE("audio-bitrate", false, STREAM_AUDIO),
PROPERTY_BITRATE("sub-bitrate", false, STREAM_SUB),
#define PROPERTY_TV_COLOR(name, type) \
{name, mp_property_tv_color, (void *)(intptr_t)type}
PROPERTY_TV_COLOR("tv-brightness", TV_COLOR_BRIGHTNESS),
PROPERTY_TV_COLOR("tv-contrast", TV_COLOR_CONTRAST),
PROPERTY_TV_COLOR("tv-saturation", TV_COLOR_SATURATION),
PROPERTY_TV_COLOR("tv-hue", TV_COLOR_HUE),
{"tv-freq", mp_property_tv_freq},
{"tv-norm", mp_property_tv_norm},
{"tv-scan", mp_property_tv_scan},
{"tv-channel", mp_property_tv_channel},
{"dvb-channel", mp_property_dvb_channel},
{"dvb-channel-name", mp_property_dvb_channel_name},
{"cursor-autohide", mp_property_cursor_autohide},
{"window-minimized", mp_property_win_minimized},
@ -5379,19 +5006,6 @@ static void cmd_show_progress(void *p)
mp_wakeup_core(mpctx);
}
static void cmd_tv_last_channel(void *p)
{
struct mp_cmd_ctx *cmd = p;
struct MPContext *mpctx = cmd->mpctx;
if (!mpctx->demuxer || !mpctx->demuxer->extended_ctrls) {
cmd->success = false;
return;
}
demux_stream_control(mpctx->demuxer, STREAM_CTRL_TV_LAST_CHAN, NULL);
}
static void cmd_track_add(void *p)
{
struct mp_cmd_ctx *cmd = p;
@ -6026,8 +5640,6 @@ const struct mp_cmd_def mp_cmds[] = {
.abort_on_playback_end = true,
},
{ "tv-last-channel", cmd_tv_last_channel, },
{ "screenshot", cmd_screenshot,
{
OPT_FLAGS("flags", v.i, 0,

View File

@ -174,11 +174,6 @@ static char *mp_get_playback_resume_config_filename(struct MPContext *mpctx,
realpath = mp_path_join(tmp, cwd, fname);
}
}
if (bstr_startswith0(bfname, "dvd://") && opts->dvd_opts && opts->dvd_opts->device)
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->dvd_opts->device);
if ((bstr_startswith0(bfname, "br://") || bstr_startswith0(bfname, "bd://") ||
bstr_startswith0(bfname, "bluray://")) && opts->bluray_device)
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->bluray_device);
uint8_t md5[16];
av_md5_sum(md5, realpath, strlen(realpath));
char *conf = talloc_strdup(tmp, "");

View File

@ -1,199 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "config.h"
#include <alsa/asoundlib.h>
#include "audio_in.h"
#include "common/msg.h"
int ai_alsa_setup(audio_in_t *ai)
{
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *swparams;
snd_pcm_uframes_t buffer_size, period_size;
int err;
int dir;
unsigned int rate;
snd_pcm_hw_params_alloca(&params);
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_hw_params_any(ai->alsa.handle, params);
if (err < 0) {
MP_ERR(ai, "Broken configuration for this PCM: no configurations available.\n");
return -1;
}
err = snd_pcm_hw_params_set_access(ai->alsa.handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
MP_ERR(ai, "Access type not available.\n");
return -1;
}
err = snd_pcm_hw_params_set_format(ai->alsa.handle, params, SND_PCM_FORMAT_S16);
if (err < 0) {
MP_ERR(ai, "Sample format not available.\n");
return -1;
}
err = snd_pcm_hw_params_set_channels(ai->alsa.handle, params, ai->req_channels);
if (err < 0) {
snd_pcm_hw_params_get_channels(params, &ai->channels);
MP_ERR(ai, "Channel count not available - reverting to default: %d\n",
ai->channels);
} else {
ai->channels = ai->req_channels;
}
dir = 0;
rate = ai->req_samplerate;
err = snd_pcm_hw_params_set_rate_near(ai->alsa.handle, params, &rate, &dir);
if (err < 0) {
MP_ERR(ai, "Cannot set samplerate.\n");
}
ai->samplerate = rate;
dir = 0;
ai->alsa.buffer_time = 1000000;
err = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params,
&ai->alsa.buffer_time, &dir);
if (err < 0) {
MP_ERR(ai, "Cannot set buffer time.\n");
}
dir = 0;
ai->alsa.period_time = ai->alsa.buffer_time / 4;
err = snd_pcm_hw_params_set_period_time_near(ai->alsa.handle, params,
&ai->alsa.period_time, &dir);
if (err < 0) {
MP_ERR(ai, "Cannot set period time.\n");
}
err = snd_pcm_hw_params(ai->alsa.handle, params);
if (err < 0) {
MP_ERR(ai, "Unable to install hardware parameters: %s", snd_strerror(err));
snd_pcm_hw_params_dump(params, ai->alsa.log);
return -1;
}
dir = -1;
snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
ai->alsa.chunk_size = period_size;
if (period_size == buffer_size) {
MP_ERR(ai, "Can't use period equal to buffer size (%u == %lu)\n", ai->alsa.chunk_size, (long)buffer_size);
return -1;
}
snd_pcm_sw_params_current(ai->alsa.handle, swparams);
err = snd_pcm_sw_params_set_avail_min(ai->alsa.handle, swparams, ai->alsa.chunk_size);
err = snd_pcm_sw_params_set_start_threshold(ai->alsa.handle, swparams, 0);
err = snd_pcm_sw_params_set_stop_threshold(ai->alsa.handle, swparams, buffer_size);
if (snd_pcm_sw_params(ai->alsa.handle, swparams) < 0) {
MP_ERR(ai, "Unable to install software parameters:\n");
snd_pcm_sw_params_dump(swparams, ai->alsa.log);
return -1;
}
if (mp_msg_test(ai->log, MSGL_V)) {
snd_pcm_dump(ai->alsa.handle, ai->alsa.log);
}
ai->alsa.bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16);
ai->alsa.bits_per_frame = ai->alsa.bits_per_sample * ai->channels;
ai->blocksize = ai->alsa.chunk_size * ai->alsa.bits_per_frame / 8;
ai->samplesize = ai->alsa.bits_per_sample;
ai->bytes_per_sample = ai->alsa.bits_per_sample/8;
return 0;
}
int ai_alsa_init(audio_in_t *ai)
{
int err;
const char *device = ai->alsa.device;
if (!device)
device = "default";
err = snd_pcm_open(&ai->alsa.handle, device, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) {
MP_ERR(ai, "Error opening audio: %s\n", snd_strerror(err));
return -1;
}
err = snd_output_stdio_attach(&ai->alsa.log, stderr, 0);
if (err < 0) {
return -1;
}
err = ai_alsa_setup(ai);
return err;
}
#ifndef timersub
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
int ai_alsa_xrun(audio_in_t *ai)
{
snd_pcm_status_t *status;
int res;
snd_pcm_status_alloca(&status);
if ((res = snd_pcm_status(ai->alsa.handle, status))<0) {
MP_ERR(ai, "ALSA status error: %s", snd_strerror(res));
return -1;
}
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp;
gettimeofday(&now, 0);
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
timersub(&now, &tstamp, &diff);
MP_ERR(ai, "ALSA xrun!!! (at least %.3f ms long)\n",
diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
if (mp_msg_test(ai->log, MSGL_V)) {
MP_ERR(ai, "ALSA Status:\n");
snd_pcm_status_dump(status, ai->alsa.log);
}
if ((res = snd_pcm_prepare(ai->alsa.handle))<0) {
MP_ERR(ai, "ALSA xrun: prepare error: %s", snd_strerror(res));
return -1;
}
return 0; /* ok, data should be accepted again */
}
MP_ERR(ai, "ALSA read/write error");
return -1;
}

View File

@ -1,153 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "osdep/io.h"
#include "audio_in.h"
#include "common/common.h"
#include "common/msg.h"
int ai_oss_set_samplerate(audio_in_t *ai)
{
int tmp = ai->req_samplerate;
if (ioctl(ai->oss.audio_fd, SNDCTL_DSP_SPEED, &tmp) == -1) return -1;
ai->samplerate = tmp;
return 0;
}
int ai_oss_set_channels(audio_in_t *ai)
{
int err;
int ioctl_param;
if (ai->req_channels > 2)
{
ioctl_param = ai->req_channels;
MP_VERBOSE(ai, "ioctl dsp channels: %d\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param));
if (err < 0) {
MP_ERR(ai, "Unable to set channel count: %d\n",
ai->req_channels);
return -1;
}
ai->channels = ioctl_param;
}
else
{
ioctl_param = (ai->req_channels == 2);
MP_VERBOSE(ai, "ioctl dsp stereo: %d (req: %d)\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_STEREO, &ioctl_param),
ioctl_param);
if (err < 0) {
MP_ERR(ai, "Unable to set stereo: %d\n",
ai->req_channels == 2);
return -1;
}
ai->channels = ioctl_param ? 2 : 1;
}
return 0;
}
int ai_oss_init(audio_in_t *ai)
{
int err;
int ioctl_param;
const char *device = ai->oss.device;
if (!device)
device = "/dev/dsp";
ai->oss.audio_fd = open(device, O_RDONLY | O_CLOEXEC);
if (ai->oss.audio_fd < 0)
{
MP_ERR(ai, "Unable to open '%s': %s\n", device, mp_strerror(errno));
return -1;
}
ioctl_param = 0 ;
MP_VERBOSE(ai, "ioctl dsp getfmt: %d\n",
ioctl(ai->oss.audio_fd, SNDCTL_DSP_GETFMTS, &ioctl_param));
MP_VERBOSE(ai, "Supported formats: %x\n", ioctl_param);
if (!(ioctl_param & AFMT_S16_NE))
MP_ERR(ai, "unsupported format\n");
ioctl_param = AFMT_S16_NE;
MP_VERBOSE(ai, "ioctl dsp setfmt: %d\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param));
if (err < 0) {
MP_ERR(ai, "Unable to set audio format.");
return -1;
}
if (ai_oss_set_channels(ai) < 0) return -1;
ioctl_param = ai->req_samplerate;
MP_VERBOSE(ai, "ioctl dsp speed: %d\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_SPEED, &ioctl_param));
if (err < 0) {
MP_ERR(ai, "Unable to set samplerate: %d\n",
ai->req_samplerate);
return -1;
}
ai->samplerate = ioctl_param;
MP_VERBOSE(ai, "ioctl dsp trigger: %d\n",
ioctl(ai->oss.audio_fd, SNDCTL_DSP_GETTRIGGER, &ioctl_param));
MP_VERBOSE(ai, "trigger: %x\n", ioctl_param);
ioctl_param = PCM_ENABLE_INPUT;
MP_VERBOSE(ai, "ioctl dsp trigger: %d\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_SETTRIGGER, &ioctl_param));
if (err < 0) {
MP_ERR(ai, "Unable to set trigger: %d\n",
PCM_ENABLE_INPUT);
}
ai->blocksize = 0;
MP_VERBOSE(ai, "ioctl dsp getblocksize: %d\n",
err = ioctl(ai->oss.audio_fd, SNDCTL_DSP_GETBLKSIZE, &ai->blocksize));
if (err < 0) {
MP_ERR(ai, "Unable to get block size!\n");
}
MP_VERBOSE(ai, "blocksize: %d\n", ai->blocksize);
// correct the blocksize to a reasonable value
if (ai->blocksize <= 0) {
ai->blocksize = 4096*ai->channels*2;
MP_ERR(ai, "Audio block size is zero, setting to %d!\n", ai->blocksize);
} else if (ai->blocksize < 4096*ai->channels*2) {
ai->blocksize *= 4096*ai->channels*2/ai->blocksize;
MP_ERR(ai, "Audio block size too low, setting to %d!\n", ai->blocksize);
}
ai->samplesize = 16;
ai->bytes_per_sample = 2;
return 0;
}

View File

@ -1,52 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include <sndio.h>
#include "audio_in.h"
#include "common/msg.h"
int ai_sndio_setup(audio_in_t *ai)
{
struct sio_par par;
sio_initpar(&par);
par.bits = 16;
par.sig = 1;
par.le = SIO_LE_NATIVE;
par.rchan = ai->req_channels;
par.rate = ai->req_samplerate;
par.appbufsz = ai->req_samplerate; /* 1 sec */
if (!sio_setpar(ai->sndio.hdl, &par) || !sio_getpar(ai->sndio.hdl, &par)) {
MP_ERR(ai, "could not configure sndio audio");
return -1;
}
ai->channels = par.rchan;
ai->samplerate = par.rate;
ai->samplesize = par.bits;
ai->bytes_per_sample = par.bps;
ai->blocksize = par.round * par.bps;
return 0;
}
int ai_sndio_init(audio_in_t *ai)
{
int err;
const char *device = ai->sndio.device;
if (!device)
device = "default";
if ((ai->sndio.hdl = sio_open(device, SIO_REC, 0)) == NULL) {
MP_ERR(ai, "could not open sndio audio");
return -1;
}
err = ai_sndio_setup(ai);
return err;
}

View File

@ -1,298 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#include "audio_in.h"
#include "common/common.h"
#include "common/msg.h"
#include <string.h>
#include <errno.h>
// sanitizes ai structure before calling other functions
int audio_in_init(audio_in_t *ai, struct mp_log *log, int type)
{
ai->type = type;
ai->setup = 0;
ai->log = log;
ai->channels = -1;
ai->samplerate = -1;
ai->blocksize = -1;
ai->bytes_per_sample = -1;
ai->samplesize = -1;
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
ai->alsa.handle = NULL;
ai->alsa.log = NULL;
ai->alsa.device = NULL;
return 0;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
ai->oss.audio_fd = -1;
ai->oss.device = NULL;
return 0;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
ai->sndio.hdl = NULL;
ai->sndio.device = NULL;
return 0;
#endif
default:
return -1;
}
}
int audio_in_setup(audio_in_t *ai)
{
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
if (ai_alsa_init(ai) < 0) return -1;
ai->setup = 1;
return 0;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
if (ai_oss_init(ai) < 0) return -1;
ai->setup = 1;
return 0;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
if (ai_sndio_init(ai) < 0) return -1;
ai->setup = 1;
return 0;
#endif
default:
return -1;
}
}
int audio_in_set_samplerate(audio_in_t *ai, int rate)
{
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
ai->req_samplerate = rate;
if (!ai->setup) return 0;
if (ai_alsa_setup(ai) < 0) return -1;
return ai->samplerate;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
ai->req_samplerate = rate;
if (!ai->setup) return 0;
if (ai_oss_set_samplerate(ai) < 0) return -1;
return ai->samplerate;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
ai->req_samplerate = rate;
if (!ai->setup) return 0;
if (ai_sndio_setup(ai) < 0) return -1;
return ai->samplerate;
#endif
default:
return -1;
}
}
int audio_in_set_channels(audio_in_t *ai, int channels)
{
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
ai->req_channels = channels;
if (!ai->setup) return 0;
if (ai_alsa_setup(ai) < 0) return -1;
return ai->channels;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
ai->req_channels = channels;
if (!ai->setup) return 0;
if (ai_oss_set_channels(ai) < 0) return -1;
return ai->channels;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
ai->req_channels = channels;
if (!ai->setup) return 0;
if (ai_sndio_setup(ai) < 0) return -1;
return ai->channels;
#endif
default:
return -1;
}
}
int audio_in_set_device(audio_in_t *ai, char *device)
{
#if HAVE_ALSA
int i;
#endif
if (ai->setup) return -1;
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
free(ai->alsa.device);
ai->alsa.device = strdup(device);
if (ai->alsa.device) {
/* mplayer could not handle colons in arguments */
for (i = 0; i < (int)strlen(ai->alsa.device); i++) {
if (ai->alsa.device[i] == '.') ai->alsa.device[i] = ':';
}
}
return 0;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
free(ai->oss.device);
ai->oss.device = strdup(device);
return 0;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
if (ai->sndio.device) free(ai->sndio.device);
ai->sndio.device = strdup(device);
return 0;
#endif
default:
return -1;
}
}
int audio_in_uninit(audio_in_t *ai)
{
if (ai->setup) {
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
if (ai->alsa.log)
snd_output_close(ai->alsa.log);
if (ai->alsa.handle) {
snd_pcm_close(ai->alsa.handle);
}
ai->setup = 0;
return 0;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
close(ai->oss.audio_fd);
ai->setup = 0;
return 0;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
if (ai->sndio.hdl)
sio_close(ai->sndio.hdl);
ai->setup = 0;
return 0;
#endif
}
}
return -1;
}
int audio_in_start_capture(audio_in_t *ai)
{
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
return snd_pcm_start(ai->alsa.handle);
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
return 0;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
if (!sio_start(ai->sndio.hdl))
return -1;
return 0;
#endif
default:
return -1;
}
}
int audio_in_read_chunk(audio_in_t *ai, unsigned char *buffer)
{
int ret;
switch (ai->type) {
#if HAVE_ALSA
case AUDIO_IN_ALSA:
ret = snd_pcm_readi(ai->alsa.handle, buffer, ai->alsa.chunk_size);
if (ret != ai->alsa.chunk_size) {
if (ret < 0) {
MP_ERR(ai, "\nError reading audio: %s\n", snd_strerror(ret));
if (ret == -EPIPE) {
if (ai_alsa_xrun(ai) == 0) {
MP_ERR(ai, "Recovered from cross-run, some frames may be left out!\n");
} else {
MP_ERR(ai, "Fatal error, cannot recover!\n");
}
}
} else {
MP_ERR(ai, "\nNot enough audio samples!\n");
}
return -1;
}
return ret;
#endif
#if HAVE_OSS_AUDIO
case AUDIO_IN_OSS:
ret = read(ai->oss.audio_fd, buffer, ai->blocksize);
if (ret != ai->blocksize) {
if (ret < 0) {
MP_ERR(ai, "\nError reading audio: %s\n", mp_strerror(errno));
} else {
MP_ERR(ai, "\nNot enough audio samples!\n");
}
return -1;
}
return ret;
#endif
#if HAVE_SNDIO
case AUDIO_IN_SNDIO:
ret = sio_read(ai->sndio.hdl, buffer, ai->blocksize);
if (ret != ai->blocksize) {
if (ret < 0) {
MP_ERR(ai, "\nError reading audio: %s\n", mp_strerror(errno));
} else {
MP_ERR(ai, "\nNot enough audio samples!\n");
}
return -1;
}
return ret;
#endif
default:
return -1;
}
}

View File

@ -1,118 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AUDIO_IN_H
#define MPLAYER_AUDIO_IN_H
#define AUDIO_IN_ALSA 1
#define AUDIO_IN_OSS 2
#define AUDIO_IN_SNDIO 3
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
struct mp_log;
#if HAVE_ALSA
#include <alsa/asoundlib.h>
typedef struct {
char *device;
snd_pcm_t *handle;
snd_output_t *log;
int buffer_time, period_time, chunk_size;
size_t bits_per_sample, bits_per_frame;
} ai_alsa_t;
#endif
#if HAVE_OSS_AUDIO
typedef struct {
char *device;
int audio_fd;
} ai_oss_t;
#endif
#if HAVE_SNDIO
#include <sndio.h>
typedef struct {
char *device;
struct sio_hdl *hdl;
} ai_sndio_t;
#endif
typedef struct
{
struct mp_log *log;
int type;
int setup;
/* requested values */
int req_channels;
int req_samplerate;
/* real values read-only */
int channels;
int samplerate;
int blocksize;
int bytes_per_sample;
int samplesize;
#if HAVE_ALSA
ai_alsa_t alsa;
#endif
#if HAVE_OSS_AUDIO
ai_oss_t oss;
#endif
#if HAVE_SNDIO
ai_sndio_t sndio;
#endif
} audio_in_t;
int audio_in_init(audio_in_t *ai, struct mp_log *log, int type);
int audio_in_setup(audio_in_t *ai);
int audio_in_set_device(audio_in_t *ai, char *device);
int audio_in_set_samplerate(audio_in_t *ai, int rate);
int audio_in_set_channels(audio_in_t *ai, int channels);
int audio_in_uninit(audio_in_t *ai);
int audio_in_start_capture(audio_in_t *ai);
int audio_in_read_chunk(audio_in_t *ai, unsigned char *buffer);
#if HAVE_ALSA
int ai_alsa_setup(audio_in_t *ai);
int ai_alsa_init(audio_in_t *ai);
int ai_alsa_xrun(audio_in_t *ai);
#endif
#if HAVE_OSS_AUDIO
int ai_oss_set_samplerate(audio_in_t *ai);
int ai_oss_set_channels(audio_in_t *ai);
int ai_oss_init(audio_in_t *ai);
#endif
#if HAVE_SNDIO
int ai_sndio_setup(audio_in_t *ai);
int ai_sndio_init(audio_in_t *ai);
#endif
#endif /* MPLAYER_AUDIO_IN_H */

View File

@ -1,809 +0,0 @@
/* dvbtune - tune.c
Copyright (C) Dave Chapman 2001,2002
Copyright (C) Rozhuk Ivan <rozhuk.im@gmail.com> 2016 - 2017
Modified for use with MPlayer, for details see the changelog at
http://svn.mplayerhq.hu/mplayer/trunk/
$Id$
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include "config.h"
#include "osdep/io.h"
#include "dvbin.h"
#include "dvb_tune.h"
#include "common/msg.h"
/* Keep in sync with enum fe_delivery_system. */
static const char *dvb_delsys_str[] = {
"UNDEFINED",
"DVB-C ANNEX A",
"DVB-C ANNEX B",
"DVB-T",
"DSS",
"DVB-S",
"DVB-S2",
"DVB-H",
"ISDBT",
"ISDBS",
"ISDBC",
"ATSC",
"ATSCMH",
"DTMB",
"CMMB",
"DAB",
"DVB-T2",
"TURBO",
"DVB-C ANNEX C",
NULL
};
const char *get_dvb_delsys(unsigned int delsys)
{
if (SYS_DVB__COUNT__ <= delsys)
return dvb_delsys_str[0];
return dvb_delsys_str[delsys];
}
unsigned int dvb_get_tuner_delsys_mask(int fe_fd, struct mp_log *log)
{
unsigned int ret_mask = 0, delsys;
struct dtv_property prop[1];
struct dtv_properties cmdseq = {.num = 1, .props = prop};
struct dvb_frontend_info fe_info;
#ifdef DVB_USE_S2API
/* S2API is the DVB API new since 2.6.28.
It allows to query frontends with multiple delivery systems. */
mp_verbose(log, "Querying tuner frontend type via DVBv5 API for frontend FD %d\n",
fe_fd);
prop[0].cmd = DTV_ENUM_DELSYS;
if (ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq) < 0) {
mp_err(log, "DVBv5: FE_GET_PROPERTY(DTV_ENUM_DELSYS) error: %d, FD: %d\n\n", errno, fe_fd);
goto old_api;
}
unsigned int i, delsys_count = prop[0].u.buffer.len;
mp_verbose(log, "DVBv5: Number of supported delivery systems: %d\n", delsys_count);
if (delsys_count == 0) {
mp_err(log, "DVBv5: Frontend FD %d returned no delivery systems!\n", fe_fd);
goto old_api;
}
for (i = 0; i < delsys_count; i++) {
delsys = (unsigned int)prop[0].u.buffer.data[i];
DELSYS_SET(ret_mask, delsys);
mp_verbose(log, "DVBv5: Tuner frontend type seems to be %s\n", get_dvb_delsys(delsys));
}
return ret_mask;
old_api:
#endif
mp_verbose(log, "Querying tuner frontend type via pre-DVBv5 API for frontend FD %d\n",
fe_fd);
memset(&fe_info, 0x00, sizeof(struct dvb_frontend_info));
if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) {
mp_err(log, "DVBv3: FE_GET_INFO error: %d, FD: %d\n\n", errno, fe_fd);
return ret_mask;
}
/* Try to get kernel DVB API version. */
prop[0].cmd = DTV_API_VERSION;
if (ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq) < 0) {
prop[0].u.data = 0x0300; /* Fail, assume 3.0 */
}
mp_verbose(log, "DVBv3: Queried tuner frontend type of device named '%s', FD: %d\n",
fe_info.name, fe_fd);
switch (fe_info.type) {
case FE_OFDM:
DELSYS_SET(ret_mask, SYS_DVBT);
if (prop[0].u.data < 0x0500)
break;
if (FE_CAN_2G_MODULATION & fe_info.caps) {
DELSYS_SET(ret_mask, SYS_DVBT2);
}
break;
case FE_QPSK:
DELSYS_SET(ret_mask, SYS_DVBS);
if (prop[0].u.data < 0x0500)
break;
if (FE_CAN_2G_MODULATION & fe_info.caps) {
DELSYS_SET(ret_mask, SYS_DVBS2);
}
#if 0 /* Not used now. */
if (FE_CAN_TURBO_FEC & fe_info.caps) {
DELSYS_SET(ret_mask, SYS_TURBO);
}
#endif
break;
case FE_QAM:
DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_A);
DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_C);
break;
#ifdef DVB_ATSC
case FE_ATSC:
if ((FE_CAN_8VSB | FE_CAN_16VSB) & fe_info.caps) {
DELSYS_SET(ret_mask, SYS_ATSC);
}
if ((FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO) & fe_info.caps) {
DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_B);
}
break;
#endif
default:
mp_err(log, "DVBv3: Unknown tuner frontend type: %d\n", fe_info.type);
return ret_mask;
}
for (delsys = 0; delsys < SYS_DVB__COUNT__; delsys ++) {
if (!DELSYS_IS_SET(ret_mask, delsys))
continue; /* Skip unsupported. */
mp_verbose(log, "DVBv3: Tuner frontend type seems to be %s\n", get_dvb_delsys(delsys));
}
return ret_mask;
}
int dvb_open_devices(dvb_priv_t *priv, unsigned int adapter,
unsigned int frontend, unsigned int demux_cnt)
{
unsigned int i;
char frontend_dev[PATH_MAX], dvr_dev[PATH_MAX], demux_dev[PATH_MAX];
dvb_state_t* state = priv->state;
snprintf(frontend_dev, sizeof(frontend_dev), "/dev/dvb/adapter%u/frontend%u", adapter, frontend);
snprintf(dvr_dev, sizeof(dvr_dev), "/dev/dvb/adapter%u/dvr0", adapter);
snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%u/demux0", adapter);
MP_VERBOSE(priv, "DVB_OPEN_DEVICES: frontend: %s\n", frontend_dev);
state->fe_fd = open(frontend_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (state->fe_fd < 0) {
MP_ERR(priv, "ERROR OPENING FRONTEND DEVICE %s: ERRNO %d\n",
frontend_dev, errno);
return 0;
}
state->demux_fds_cnt = 0;
MP_VERBOSE(priv, "DVB_OPEN_DEVICES(%d)\n", demux_cnt);
for (i = 0; i < demux_cnt; i++) {
state->demux_fds[i] = open(demux_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (state->demux_fds[i] < 0) {
MP_ERR(priv, "ERROR OPENING DEMUX 0: %d\n", errno);
return 0;
} else {
MP_VERBOSE(priv, "OPEN(%d), file %s: FD=%d, CNT=%d\n", i, demux_dev,
state->demux_fds[i], state->demux_fds_cnt);
state->demux_fds_cnt++;
}
}
state->dvr_fd = open(dvr_dev, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (state->dvr_fd < 0) {
MP_ERR(priv, "ERROR OPENING DVR DEVICE %s: %d\n", dvr_dev, errno);
return 0;
}
return 1;
}
int dvb_fix_demuxes(dvb_priv_t *priv, unsigned int cnt)
{
int i;
char demux_dev[PATH_MAX];
dvb_state_t* state = priv->state;
snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%d/demux0",
state->adapters[state->cur_adapter].devno);
MP_VERBOSE(priv, "FIX %d -> %d\n", state->demux_fds_cnt, cnt);
if (state->demux_fds_cnt >= cnt) {
for (i = state->demux_fds_cnt - 1; i >= (int)cnt; i--) {
MP_VERBOSE(priv, "FIX, CLOSE fd(%d): %d\n", i, state->demux_fds[i]);
close(state->demux_fds[i]);
}
state->demux_fds_cnt = cnt;
} else {
for (i = state->demux_fds_cnt; i < cnt; i++) {
state->demux_fds[i] = open(demux_dev,
O_RDWR | O_NONBLOCK | O_CLOEXEC);
MP_VERBOSE(priv, "FIX, OPEN fd(%d): %d\n", i, state->demux_fds[i]);
if (state->demux_fds[i] < 0) {
MP_ERR(priv, "ERROR OPENING DEMUX 0: %d\n", errno);
return 0;
} else
state->demux_fds_cnt++;
}
}
return 1;
}
int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid,
dmx_pes_type_t pestype)
{
int i;
struct dmx_pes_filter_params pesFilterParams;
pesFilterParams.pid = pid;
pesFilterParams.input = DMX_IN_FRONTEND;
pesFilterParams.output = DMX_OUT_TS_TAP;
pesFilterParams.pes_type = pestype;
pesFilterParams.flags = DMX_IMMEDIATE_START;
{
int buffersize = 256 * 1024;
if (ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) < 0)
MP_ERR(priv, "ERROR IN DMX_SET_BUFFER_SIZE %i for fd %d: ERRNO: %d\n",
pid, fd, errno);
}
errno = 0;
if ((i = ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams)) < 0) {
MP_ERR(priv, "ERROR IN SETTING DMX_FILTER %i for fd %d: ERRNO: %d\n",
pid, fd, errno);
return 0;
}
MP_VERBOSE(priv, "SET PES FILTER ON PID %d to fd %d, RESULT: %d, ERRNO: %d\n",
pid, fd, i, errno);
return 1;
}
int dvb_get_pmt_pid(dvb_priv_t *priv, int devno, int service_id)
{
/* We need special filters on the demux,
so open one locally, and close also here. */
char demux_dev[PATH_MAX];
snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%d/demux0", devno);
struct dmx_sct_filter_params fparams;
memset(&fparams, 0x00, sizeof(fparams));
fparams.pid = 0;
fparams.filter.filter[0] = 0x00;
fparams.filter.mask[0] = 0xff;
fparams.timeout = 0;
fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
int pat_fd;
if ((pat_fd = open(demux_dev, O_RDWR)) < 0) {
MP_ERR(priv, "Opening PAT DEMUX failed, error: %d", errno);
return -1;
}
if (ioctl(pat_fd, DMX_SET_FILTER, &fparams) < 0) {
MP_ERR(priv, "ioctl DMX_SET_FILTER failed, error: %d", errno);
close(pat_fd);
return -1;
}
int bytes_read;
int section_length;
unsigned char buft[4096];
unsigned char *bufptr = buft;
int pmt_pid = -1;
bool pat_read = false;
while (!pat_read) {
if (((bytes_read =
read(pat_fd, bufptr,
sizeof(buft))) < 0) && errno == EOVERFLOW)
bytes_read = read(pat_fd, bufptr, sizeof(buft));
if (bytes_read < 0) {
MP_ERR(priv, "PAT: read_sections: read error: %d", errno);
close(pat_fd);
return -1;
}
section_length = ((bufptr[1] & 0x0f) << 8) | bufptr[2];
if (bytes_read != section_length + 3)
continue;
bufptr += 8;
section_length -= 8;
/* assumes one section contains the whole pat */
pat_read = true;
while (section_length > 0) {
int this_service_id = (bufptr[0] << 8) | bufptr[1];
if (this_service_id == service_id) {
pmt_pid = ((bufptr[2] & 0x1f) << 8) | bufptr[3];
section_length = 0;
}
bufptr += 4;
section_length -= 4;
}
}
close(pat_fd);
return pmt_pid;
}
static void print_status(dvb_priv_t *priv, fe_status_t festatus)
{
MP_VERBOSE(priv, "FE_STATUS:");
if (festatus & FE_HAS_SIGNAL)
MP_VERBOSE(priv, " FE_HAS_SIGNAL");
if (festatus & FE_TIMEDOUT)
MP_VERBOSE(priv, " FE_TIMEDOUT");
if (festatus & FE_HAS_LOCK)
MP_VERBOSE(priv, " FE_HAS_LOCK");
if (festatus & FE_HAS_CARRIER)
MP_VERBOSE(priv, " FE_HAS_CARRIER");
if (festatus & FE_HAS_VITERBI)
MP_VERBOSE(priv, " FE_HAS_VITERBI");
if (festatus & FE_HAS_SYNC)
MP_VERBOSE(priv, " FE_HAS_SYNC");
MP_VERBOSE(priv, "\n");
}
static int check_status(dvb_priv_t *priv, int fd_frontend, int tmout)
{
int32_t strength;
fe_status_t festatus;
struct pollfd pfd[1];
int ok = 0, locks = 0;
time_t tm1, tm2;
pfd[0].fd = fd_frontend;
pfd[0].events = POLLPRI;
MP_VERBOSE(priv, "Getting frontend status\n");
tm1 = tm2 = time((time_t *) NULL);
while (!ok) {
festatus = 0;
if (poll(pfd, 1, tmout * 1000) > 0) {
if (pfd[0].revents & POLLPRI) {
if (ioctl(fd_frontend, FE_READ_STATUS, &festatus) >= 0) {
if (festatus & FE_HAS_LOCK)
locks++;
}
}
}
usleep(10000);
tm2 = time((time_t *) NULL);
if ((festatus & FE_TIMEDOUT) || (locks >= 2) || (tm2 - tm1 >= tmout))
ok = 1;
}
if (festatus & FE_HAS_LOCK) {
strength = 0;
if (ioctl(fd_frontend, FE_READ_BER, &strength) >= 0)
MP_VERBOSE(priv, "Bit error rate: %d\n", strength);
strength = 0;
if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &strength) >= 0)
MP_VERBOSE(priv, "Signal strength: %d\n", strength);
strength = 0;
if (ioctl(fd_frontend, FE_READ_SNR, &strength) >= 0)
MP_VERBOSE(priv, "SNR: %d\n", strength);
strength = 0;
if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &strength) >= 0)
MP_VERBOSE(priv, "UNC: %d\n", strength);
print_status(priv, festatus);
} else {
MP_ERR(priv, "Not able to lock to the signal on the given frequency, "
"timeout: %d\n", tmout);
return -1;
}
return 0;
}
struct diseqc_cmd {
struct dvb_diseqc_master_cmd cmd;
uint32_t wait;
};
static int diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd,
fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
{
if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) < 0)
return -1;
if (ioctl(fd, FE_SET_VOLTAGE, v) < 0)
return -1;
usleep(15 * 1000);
if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) < 0)
return -1;
usleep(cmd->wait * 1000);
usleep(15 * 1000);
if (ioctl(fd, FE_DISEQC_SEND_BURST, b) < 0)
return -1;
usleep(15 * 1000);
if (ioctl(fd, FE_SET_TONE, t) < 0)
return -1;
usleep(100000);
return 0;
}
/* digital satellite equipment control,
* specification is available from http://www.eutelsat.com/
*/
static int do_diseqc(int secfd, int sat_no, int polv, int hi_lo)
{
struct diseqc_cmd cmd = { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };
/* param: high nibble: reset bits, low nibble set bits,
* bits are: option, position, polarizaion, band
*/
cmd.cmd.msg[3] = 0xf0 | (((sat_no * 4) & 0x0f) | (hi_lo ? 1 : 0) | (polv ? 0 : 2));
return diseqc_send_msg(secfd, polv ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18,
&cmd, hi_lo ? SEC_TONE_ON : SEC_TONE_OFF,
((sat_no / 4) % 2) ? SEC_MINI_B : SEC_MINI_A);
}
#ifdef DVB_USE_S2API
static int dvbv5_tune(dvb_priv_t *priv, int fd_frontend,
unsigned int delsys, struct dtv_properties* cmdseq)
{
MP_VERBOSE(priv, "Tuning via S2API, channel is %s.\n",
get_dvb_delsys(delsys));
MP_VERBOSE(priv, "Dumping raw tuning commands and values:\n");
for (int i = 0; i < cmdseq->num; ++i) {
MP_VERBOSE(priv, "%02d: 0x%x(%d) => 0x%x(%d)\n",
i, cmdseq->props[i].cmd, cmdseq->props[i].cmd,
cmdseq->props[i].u.data, cmdseq->props[i].u.data);
}
if (ioctl(fd_frontend, FE_SET_PROPERTY, cmdseq) < 0) {
MP_ERR(priv, "ERROR tuning channel\n");
return -1;
}
return 0;
}
#endif
static int tune_it(dvb_priv_t *priv, int fd_frontend, unsigned int delsys,
unsigned int freq, unsigned int srate, char pol,
int stream_id,
fe_spectral_inversion_t specInv, unsigned int diseqc,
fe_modulation_t modulation,
fe_code_rate_t HP_CodeRate,
fe_transmit_mode_t TransmissionMode,
fe_guard_interval_t guardInterval,
fe_bandwidth_t bandwidth,
fe_code_rate_t LP_CodeRate, fe_hierarchy_t hier,
int timeout)
{
int hi_lo = 0, bandwidth_hz = 0;
dvb_state_t* state = priv->state;
struct dvb_frontend_parameters feparams;
MP_VERBOSE(priv, "TUNE_IT, fd_frontend %d, %s freq %lu, srate %lu, "
"pol %c, diseqc %u\n", fd_frontend,
get_dvb_delsys(delsys),
(long unsigned int)freq, (long unsigned int)srate,
(pol > ' ' ? pol : '-'), diseqc);
MP_VERBOSE(priv, "Using %s adapter %d\n",
get_dvb_delsys(delsys),
state->adapters[state->cur_adapter].devno);
{
/* discard stale QPSK events */
struct dvb_frontend_event ev;
while (true) {
if (ioctl(fd_frontend, FE_GET_EVENT, &ev) < 0)
break;
}
}
/* Prepare params, be verbose. */
switch (delsys) {
case SYS_DVBT2:
#ifndef DVB_USE_S2API
MP_ERR(priv, "ERROR: Can not tune to T2 channel, S2-API not "
"available, will tune to DVB-T!\n");
#endif
/* PASSTROUTH. */
case SYS_DVBT:
if (freq < 1000000)
freq *= 1000UL;
switch (bandwidth) {
case BANDWIDTH_5_MHZ:
bandwidth_hz = 5000000;
break;
case BANDWIDTH_6_MHZ:
bandwidth_hz = 6000000;
break;
case BANDWIDTH_7_MHZ:
bandwidth_hz = 7000000;
break;
case BANDWIDTH_8_MHZ:
bandwidth_hz = 8000000;
break;
case BANDWIDTH_10_MHZ:
bandwidth_hz = 10000000;
break;
case BANDWIDTH_AUTO:
if (freq < 474000000) {
bandwidth_hz = 7000000;
} else {
bandwidth_hz = 8000000;
}
break;
default:
bandwidth_hz = 0;
break;
}
MP_VERBOSE(priv, "tuning %s to %d Hz, bandwidth: %d\n",
get_dvb_delsys(delsys), freq, bandwidth_hz);
break;
case SYS_DVBS2:
#ifndef DVB_USE_S2API
MP_ERR(priv, "ERROR: Can not tune to S2 channel, S2-API not "
"available, will tune to DVB-S!\n");
#endif
/* PASSTROUTH. */
case SYS_DVBS:
if (freq > 2200000) {
// this must be an absolute frequency
if (freq < SLOF) {
freq -= LOF1;
hi_lo = 0;
} else {
freq -= LOF2;
hi_lo = 1;
}
}
MP_VERBOSE(priv, "tuning %s to Freq: %u, Pol: %c Srate: %d, "
"22kHz: %s, LNB: %d\n", get_dvb_delsys(delsys), freq,
pol, srate, hi_lo ? "on" : "off", diseqc);
if (do_diseqc(fd_frontend, diseqc, (pol == 'V' ? 1 : 0), hi_lo) == 0) {
MP_VERBOSE(priv, "DISEQC setting succeeded\n");
} else {
MP_ERR(priv, "DISEQC setting failed\n");
return -1;
}
break;
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
MP_VERBOSE(priv, "tuning %s to %d, srate=%d\n",
get_dvb_delsys(delsys), freq, srate);
break;
#ifdef DVB_ATSC
case SYS_ATSC:
case SYS_DVBC_ANNEX_B:
MP_VERBOSE(priv, "tuning %s to %d, modulation=%d\n",
get_dvb_delsys(delsys), freq, modulation);
break;
#endif
default:
MP_VERBOSE(priv, "Unknown FE type. Aborting\n");
return 0;
}
#ifdef DVB_USE_S2API
/* S2API is the DVB API new since 2.6.28.
* It is needed to tune to new delivery systems, e.g. DVB-S2.
* It takes a struct with a list of pairs of command + parameter.
*/
/* Reset before tune. */
struct dtv_property p_clear[] = {
{ .cmd = DTV_CLEAR },
};
struct dtv_properties cmdseq_clear = {
.num = 1,
.props = p_clear
};
if (ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq_clear) < 0) {
MP_ERR(priv, "FE_SET_PROPERTY DTV_CLEAR failed\n");
}
/* Tune. */
switch (delsys) {
case SYS_DVBS:
case SYS_DVBS2:
{
struct dtv_property p[] = {
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
{ .cmd = DTV_FREQUENCY, .u.data = freq },
{ .cmd = DTV_MODULATION, .u.data = modulation },
{ .cmd = DTV_SYMBOL_RATE, .u.data = srate },
{ .cmd = DTV_INNER_FEC, .u.data = HP_CodeRate },
{ .cmd = DTV_INVERSION, .u.data = specInv },
{ .cmd = DTV_ROLLOFF, .u.data = ROLLOFF_AUTO },
{ .cmd = DTV_PILOT, .u.data = PILOT_AUTO },
{ .cmd = DTV_TUNE },
};
struct dtv_properties cmdseq = {
.num = sizeof(p) / sizeof(p[0]),
.props = p
};
if (dvbv5_tune(priv, fd_frontend, delsys, &cmdseq) != 0) {
goto old_api;
}
}
break;
case SYS_DVBT:
case SYS_DVBT2:
{
struct dtv_property p[] = {
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
{ .cmd = DTV_FREQUENCY, .u.data = freq },
{ .cmd = DTV_MODULATION, .u.data = modulation },
{ .cmd = DTV_SYMBOL_RATE, .u.data = srate },
{ .cmd = DTV_CODE_RATE_HP, .u.data = HP_CodeRate },
{ .cmd = DTV_CODE_RATE_LP, .u.data = LP_CodeRate },
{ .cmd = DTV_INVERSION, .u.data = specInv },
{ .cmd = DTV_BANDWIDTH_HZ, .u.data = bandwidth_hz },
{ .cmd = DTV_TRANSMISSION_MODE, .u.data = TransmissionMode },
{ .cmd = DTV_GUARD_INTERVAL, .u.data = guardInterval },
{ .cmd = DTV_HIERARCHY, .u.data = hier },
{ .cmd = DTV_STREAM_ID, .u.data = stream_id },
{ .cmd = DTV_TUNE },
};
struct dtv_properties cmdseq = {
.num = sizeof(p) / sizeof(p[0]),
.props = p
};
if (dvbv5_tune(priv, fd_frontend, delsys, &cmdseq) != 0) {
goto old_api;
}
}
break;
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
{
struct dtv_property p[] = {
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
{ .cmd = DTV_FREQUENCY, .u.data = freq },
{ .cmd = DTV_MODULATION, .u.data = modulation },
{ .cmd = DTV_SYMBOL_RATE, .u.data = srate },
{ .cmd = DTV_INNER_FEC, .u.data = HP_CodeRate },
{ .cmd = DTV_INVERSION, .u.data = specInv },
{ .cmd = DTV_TUNE },
};
struct dtv_properties cmdseq = {
.num = sizeof(p) / sizeof(p[0]),
.props = p
};
if (dvbv5_tune(priv, fd_frontend, delsys, &cmdseq) != 0) {
goto old_api;
}
}
break;
#ifdef DVB_ATSC
case SYS_ATSC:
case SYS_DVBC_ANNEX_B:
{
struct dtv_property p[] = {
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
{ .cmd = DTV_FREQUENCY, .u.data = freq },
{ .cmd = DTV_INVERSION, .u.data = specInv },
{ .cmd = DTV_MODULATION, .u.data = modulation },
{ .cmd = DTV_TUNE },
};
struct dtv_properties cmdseq = {
.num = sizeof(p) / sizeof(p[0]),
.props = p
};
if (dvbv5_tune(priv, fd_frontend, delsys, &cmdseq) != 0) {
goto old_api;
}
}
break;
#endif
}
int tune_status = check_status(priv, fd_frontend, timeout);
if (tune_status != 0) {
MP_ERR(priv, "ERROR locking to channel when tuning with S2API, clearing and falling back to DVBv3-tuning.\n");
if (ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq_clear) < 0) {
MP_ERR(priv, "FE_SET_PROPERTY DTV_CLEAR failed\n");
}
goto old_api;
} else {
return tune_status;
}
old_api:
#endif
MP_VERBOSE(priv, "Tuning via DVB-API version 3.\n");
if (stream_id != NO_STREAM_ID_FILTER && stream_id != 0) {
MP_ERR(priv, "DVB-API version 3 does not support stream_id (PLP).\n");
return -1;
}
memset(&feparams, 0x00, sizeof(feparams));
feparams.frequency = freq;
feparams.inversion = specInv;
switch (delsys) {
case SYS_DVBT:
case SYS_DVBT2:
feparams.u.ofdm.bandwidth = bandwidth;
feparams.u.ofdm.code_rate_HP = HP_CodeRate;
feparams.u.ofdm.code_rate_LP = LP_CodeRate;
feparams.u.ofdm.constellation = modulation;
feparams.u.ofdm.transmission_mode = TransmissionMode;
feparams.u.ofdm.guard_interval = guardInterval;
feparams.u.ofdm.hierarchy_information = hier;
break;
case SYS_DVBS:
case SYS_DVBS2:
feparams.u.qpsk.symbol_rate = srate;
feparams.u.qpsk.fec_inner = HP_CodeRate;
break;
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
feparams.u.qam.symbol_rate = srate;
feparams.u.qam.fec_inner = HP_CodeRate;
feparams.u.qam.modulation = modulation;
break;
#ifdef DVB_ATSC
case SYS_ATSC:
case SYS_DVBC_ANNEX_B:
feparams.u.vsb.modulation = modulation;
break;
#endif
}
if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
MP_ERR(priv, "ERROR tuning channel\n");
return -1;
}
return check_status(priv, fd_frontend, timeout);
}
int dvb_tune(dvb_priv_t *priv, unsigned int delsys,
int freq, char pol, int srate, int diseqc,
int stream_id, fe_spectral_inversion_t specInv,
fe_modulation_t modulation, fe_guard_interval_t guardInterval,
fe_transmit_mode_t TransmissionMode, fe_bandwidth_t bandWidth,
fe_code_rate_t HP_CodeRate,
fe_code_rate_t LP_CodeRate, fe_hierarchy_t hier,
int timeout)
{
MP_INFO(priv, "dvb_tune %s Freq: %lu\n",
get_dvb_delsys(delsys), (long unsigned int) freq);
dvb_state_t* state = priv->state;
int ris = tune_it(priv, state->fe_fd, delsys, freq, srate, pol,
stream_id, specInv, diseqc, modulation,
HP_CodeRate, TransmissionMode, guardInterval,
bandWidth, LP_CodeRate, hier, timeout);
if (ris != 0)
MP_INFO(priv, "dvb_tune, TUNING FAILED\n");
return ris == 0;
}

View File

@ -1,42 +0,0 @@
/*
* 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.
*/
#ifndef MPLAYER_DVB_TUNE_H
#define MPLAYER_DVB_TUNE_H
#include "dvbin.h"
struct mp_log;
const char *get_dvb_delsys(unsigned int delsys);
unsigned int dvb_get_tuner_delsys_mask(int fe_fd, struct mp_log *log);
int dvb_open_devices(dvb_priv_t *priv, unsigned int adapter,
unsigned int frontend, unsigned int demux_cnt);
int dvb_fix_demuxes(dvb_priv_t *priv, unsigned int cnt);
int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid, dmx_pes_type_t pestype);
int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id);
int dvb_tune(dvb_priv_t *priv, unsigned int delsys,
int freq, char pol, int srate, int diseqc,
int stream_id, fe_spectral_inversion_t specInv,
fe_modulation_t modulation, fe_guard_interval_t guardInterval,
fe_transmit_mode_t TransmissionMode, fe_bandwidth_t bandWidth,
fe_code_rate_t HP_CodeRate, fe_code_rate_t LP_CodeRate,
fe_hierarchy_t hier, int timeout);
#endif /* MPLAYER_DVB_TUNE_H */

View File

@ -1,186 +0,0 @@
/* Imported from the dvbstream project
*
* Modified for use with MPlayer, for details see the changelog at
* http://svn.mplayerhq.hu/mplayer/trunk/
* $Id$
*/
#ifndef MPLAYER_DVBIN_H
#define MPLAYER_DVBIN_H
#include "config.h"
#include "stream.h"
#if !HAVE_GPL
#error GPL only
#endif
#define SLOF (11700 * 1000UL)
#define LOF1 (9750 * 1000UL)
#define LOF2 (10600 * 1000UL)
#include <inttypes.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
#include <linux/dvb/version.h>
#define MAX_ADAPTERS 16
#define MAX_FRONTENDS 8
#undef DVB_ATSC
#if defined(DVB_API_VERSION_MINOR)
/* kernel headers >=2.6.28 have version 5.
*
* Version 5 is also called S2API, it adds support for tuning to S2 channels
* and is extensible for future delivery systems. Old API is deprecated.
* StreamID-implementation only supported since API >=5.2.
* At least DTV_ENUM_DELSYS requires 5.5.
*/
#if (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 5)
#define DVB_USE_S2API 1
// This had a different name until API 5.8.
#ifndef DTV_STREAM_ID
#define DTV_STREAM_ID DTV_ISDBS_TS_ID
#endif
#endif
// This is only defined, for convenience, since API 5.8.
#ifndef NO_STREAM_ID_FILTER
#define NO_STREAM_ID_FILTER (~0U)
#endif
#if (DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR >= 1) || DVB_API_VERSION == 5
#define DVB_ATSC 1
#endif
#endif
#define DVB_CHANNEL_LOWER -1
#define DVB_CHANNEL_HIGHER 1
#ifndef DMX_FILTER_SIZE
#define DMX_FILTER_SIZE 32
#endif
typedef struct {
char *name;
unsigned int freq, srate, diseqc;
char pol;
unsigned int tpid, dpid1, dpid2, progid, ca, pids[DMX_FILTER_SIZE], pids_cnt;
bool is_dvb_x2; /* Used only in dvb_get_channels() and parse_vdr_par_string(), use delsys. */
unsigned int frontend;
unsigned int delsys;
unsigned int stream_id;
unsigned int service_id;
fe_spectral_inversion_t inv;
fe_modulation_t mod;
fe_transmit_mode_t trans;
fe_bandwidth_t bw;
fe_guard_interval_t gi;
fe_code_rate_t cr, cr_lp;
fe_hierarchy_t hier;
} dvb_channel_t;
typedef struct {
unsigned int NUM_CHANNELS;
unsigned int current;
dvb_channel_t *channels;
} dvb_channels_list_t;
typedef struct {
int devno;
unsigned int delsys_mask[MAX_FRONTENDS];
dvb_channels_list_t *list;
} dvb_adapter_config_t;
typedef struct {
unsigned int adapters_count;
dvb_adapter_config_t *adapters;
unsigned int cur_adapter;
unsigned int cur_frontend;
int fe_fd;
int dvr_fd;
int demux_fd[3], demux_fds[DMX_FILTER_SIZE], demux_fds_cnt;
int is_on;
int retry;
int timeout;
unsigned int last_freq;
bool switching_channel;
bool stream_used;
} dvb_state_t;
typedef struct {
struct mp_log *log;
dvb_state_t *state;
char *cfg_prog;
int cfg_devno;
int cfg_timeout;
char *cfg_file;
int cfg_full_transponder;
} dvb_priv_t;
/* Keep in sync with enum fe_delivery_system. */
#ifndef DVB_USE_S2API
# define SYS_DVBC_ANNEX_A 1
# define SYS_DVBC_ANNEX_B 1
# define SYS_DVBT 3
# define SYS_DVBS 5
# define SYS_DVBS2 6
# define SYS_ATSC 11
# define SYS_DVBT2 16
# define SYS_DVBC_ANNEX_C 18
#endif
#define SYS_DVB__COUNT__ (SYS_DVBC_ANNEX_C + 1)
#define DELSYS_BIT(__bit) (((unsigned int)1) << (__bit))
#define DELSYS_SET(__mask, __bit) \
(__mask) |= DELSYS_BIT((__bit))
#define DELSYS_IS_SET(__mask, __bit) \
(0 != ((__mask) & DELSYS_BIT((__bit))))
#ifdef DVB_ATSC
#define DELSYS_SUPP_MASK \
( \
DELSYS_BIT(SYS_DVBC_ANNEX_A) | \
DELSYS_BIT(SYS_DVBT) | \
DELSYS_BIT(SYS_DVBS) | \
DELSYS_BIT(SYS_DVBS2) | \
DELSYS_BIT(SYS_ATSC) | \
DELSYS_BIT(SYS_DVBC_ANNEX_B) | \
DELSYS_BIT(SYS_DVBT2) | \
DELSYS_BIT(SYS_DVBC_ANNEX_C) \
)
#else
#define DELSYS_SUPP_MASK \
( \
DELSYS_BIT(SYS_DVBC_ANNEX_A) | \
DELSYS_BIT(SYS_DVBT) | \
DELSYS_BIT(SYS_DVBS) | \
DELSYS_BIT(SYS_DVBS2) | \
DELSYS_BIT(SYS_DVBT2) | \
DELSYS_BIT(SYS_DVBC_ANNEX_C) \
)
#endif
int dvb_step_channel(stream_t *, int);
int dvb_set_channel(stream_t *, unsigned int, unsigned int);
dvb_state_t *dvb_get_state(stream_t *);
void dvb_free_state(dvb_state_t *);
#endif /* MPLAYER_DVBIN_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,133 +0,0 @@
/*
* Worldwide channel/frequency list
*
* Nathan Laredo (laredo@broked.net)
*
* Frequencies are given in kHz
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_FREQUENCIES_H
#define MPLAYER_FREQUENCIES_H
#define NTSC_AUDIO_CARRIER 4500
#define PAL_AUDIO_CARRIER_I 6000
#define PAL_AUDIO_CARRIER_BGHN 5500
#define PAL_AUDIO_CARRIER_MN 4500
#define PAL_AUDIO_CARRIER_D 6500
#define SEACAM_AUDIO_DKK1L 6500
#define SEACAM_AUDIO_BG 5500
/* NICAM 728 32-kHz, 14-bit digital stereo audio is transmitted in 1ms frames
containing 8 bits frame sync, 5 bits control, 11 bits additional data, and
704 bits audio data. The bit rate is reduced by transmitting only 10 bits
plus parity of each 14 bit sample, the largest sample in a frame determines
which 10 bits are transmitted. The parity bits for audio samples also
specify the scaling factor used for that channel during that frame. The
companeded audio data is interleaved to reduce the influence of dropouts
and the whole frame except for sync bits is scrambled for spectrum shaping.
Data is modulated using QPSK, at below following subcarrier freqs */
#define NICAM728_PAL_BGH 5850
#define NICAM728_PAL_I 6552
/* COMPREHENSIVE LIST OF FORMAT BY COUNTRY
(M) NTSC used in:
Antigua, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Burma,
Canada, Chile, Colombia, Costa Rica, Cuba, Curacao, Dominican Republic,
Ecuador, El Salvador, Guam Guatemala, Honduras, Jamaica, Japan,
South Korea, Mexico, Montserrat, Myanmar, Nicaragua, Panama, Peru,
Philippines, Puerto Rico, St Christopher and Nevis, Samoa, Suriname,
Taiwan, Trinidad/Tobago, United States, Venezuela, Virgin Islands
(B) PAL used in:
Albania, Algeria, Australia, Austria, Bahrain, Bangladesh, Belgium,
Bosnia-Herzegovinia, Brunei Darussalam, Cambodia, Cameroon, Croatia,
Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea, Finland, Germany,
Ghana, Gibraltar, Greenland, Iceland, India, Indonesia, Israel, Italy,
Jordan, Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysa, Maldives,
Malta, Nepal, Netherlands, New Zeland, Nigeria, Norway, Oman, Pakistan,
Papua New Guinea, Portugal, Qatar, Sao Tome and Principe, Saudi Arabia,
Seychelles, Sierra Leone, Singapore, Slovenia, Somali, Spain,
Sri Lanka, Sudan, Swaziland, Sweden, Switzeland, Syria, Thailand,
Tunisia, Turkey, Uganda, United Arab Emirates, Yemen
(N) PAL used in: (Combination N = 4.5MHz audio carrier, 3.58MHz burst)
Argentina (Combination N), Paraguay, Uruguay
(M) PAL (525/60, 3.57MHz burst) used in:
Brazil
(G) PAL used in:
Albania, Algeria, Austria, Bahrain, Bosnia/Herzegovinia, Cambodia,
Cameroon, Croatia, Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea,
Finland, Germany, Gibraltar, Greenland, Iceland, Israel, Italy, Jordan,
Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysia, Monaco,
Mozambique, Netherlands, New Zealand, Norway, Oman, Pakistan,
Papa New Guinea, Portugal, Qatar, Romania, Sierra Leone, Singapore,
Slovenia, Somalia, Spain, Sri Lanka, Sudan, Swaziland, Sweeden,
Switzerland, Syria, Thailand, Tunisia, Turkey, United Arab Emirates,
Yemen, Zambia, Zimbabwe
(D) PAL used in:
China, North Korea, Romania, Czech Republic
(H) PAL used in:
Belgium
(I) PAL used in:
Angola, Botswana, Gambia, Guinea-Bissau, Hong Kong, Ireland, Lesotho,
Malawi, Nambia, Nigeria, South Africa, Tanzania, United Kingdom,
Zanzibar
(B) SECAM used in:
Djibouti, Greece, Iran, Iraq, Lebanon, Mali, Mauritania, Mauritus,
Morocco
(D) SECAM used in:
Afghanistan, Armenia, Azerbaijan, Belarus, Bulgaria,
Estonia, Georgia, Hungary, Zazakhstan, Lithuania, Mongolia, Moldova,
Russia, Slovak Republic, Ukraine, Vietnam
(G) SECAM used in:
Greecem Iran, Iraq, Mali, Mauritus, Morocco, Saudi Arabia
(K) SECAM used in:
Armenia, Azerbaijan, Bulgaria, Estonia, Georgia,
Hungary, Kazakhstan, Lithuania, Madagascar, Moldova, Poland, Russia,
Slovak Republic, Ukraine, Vietnam
(K1) SECAM used in:
Benin, Burkina Faso, Burundi, Chad, Cape Verde, Central African
Republic, Comoros, Congo, Gabon, Madagascar, Niger, Rwanda, Senegal,
Togo, Zaire
(L) SECAM used in:
France
*/
/* --------------------------------------------------------------------- */
struct CHANLIST {
char name[8];
int freq;
};
struct CHANLISTS {
const char *name;
const struct CHANLIST *list;
int count;
};
#define CHAN_COUNT(x) (sizeof(x)/sizeof(struct CHANLIST))
/* --------------------------------------------------------------------- */
extern const struct CHANLISTS chanlists[];
extern const int chancount;
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
#endif /* MPLAYER_FREQUENCIES_H */

View File

@ -1,451 +0,0 @@
/*****************************************************************************
* rar.c: uncompressed RAR parser
*****************************************************************************
* Copyright (C) 2008-2010 Laurent Aimar
* $Id: f368245f4260f913f5c211e09b7dd511a96525e6 $
*
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <libavutil/intreadwrite.h>
#include "mpv_talloc.h"
#include "common/common.h"
#include "stream.h"
#include "rar.h"
static const uint8_t rar_marker[] = {
0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
};
static const int rar_marker_size = sizeof(rar_marker);
void RarFileDelete(rar_file_t *file)
{
for (int i = 0; i < file->chunk_count; i++) {
free(file->chunk[i]->mrl);
free(file->chunk[i]);
}
talloc_free(file->chunk);
free(file->name);
free_stream(file->s);
free(file);
}
typedef struct {
uint16_t crc;
uint8_t type;
uint16_t flags;
uint16_t size;
uint32_t add_size;
} rar_block_t;
enum {
RAR_BLOCK_MARKER = 0x72,
RAR_BLOCK_ARCHIVE = 0x73,
RAR_BLOCK_FILE = 0x74,
RAR_BLOCK_SUBBLOCK = 0x7a,
RAR_BLOCK_END = 0x7b,
};
enum {
RAR_BLOCK_END_HAS_NEXT = 0x0001,
};
enum {
RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
RAR_BLOCK_FILE_HAS_NEXT = 0x0002,
RAR_BLOCK_FILE_HAS_HIGH = 0x0100,
};
static int PeekBlock(struct stream *s, rar_block_t *hdr)
{
bstr data = stream_peek(s, 11);
const uint8_t *peek = (uint8_t *)data.start;
int peek_size = data.len;
if (peek_size < 7)
return -1;
hdr->crc = AV_RL16(&peek[0]);
hdr->type = peek[2];
hdr->flags = AV_RL16(&peek[3]);
hdr->size = AV_RL16(&peek[5]);
hdr->add_size = 0;
if ((hdr->flags & 0x8000) ||
hdr->type == RAR_BLOCK_FILE ||
hdr->type == RAR_BLOCK_SUBBLOCK) {
if (peek_size < 11)
return -1;
hdr->add_size = AV_RL32(&peek[7]);
}
if (hdr->size < 7)
return -1;
return 0;
}
static int SkipBlock(struct stream *s, const rar_block_t *hdr)
{
uint64_t size = (uint64_t)hdr->size + hdr->add_size;
while (size > 0) {
int skip = MPMIN(size, INT_MAX);
if (!stream_skip(s, skip))
return -1;
size -= skip;
}
return 0;
}
static int IgnoreBlock(struct stream *s, int block)
{
/* */
rar_block_t bk;
if (PeekBlock(s, &bk) || bk.type != block)
return -1;
return SkipBlock(s, &bk);
}
static int SkipEnd(struct stream *s, const rar_block_t *hdr)
{
if (!(hdr->flags & RAR_BLOCK_END_HAS_NEXT))
return -1;
if (SkipBlock(s, hdr))
return -1;
/* Now, we need to look for a marker block,
* It seems that there is garbage at EOF */
for (;;) {
bstr peek = stream_peek(s, rar_marker_size);
if (peek.len < rar_marker_size)
return -1;
if (!memcmp(peek.start, rar_marker, rar_marker_size))
break;
if (!stream_skip(s, 1))
return -1;
}
/* Skip marker and archive blocks */
if (IgnoreBlock(s, RAR_BLOCK_MARKER))
return -1;
if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
return -1;
return 0;
}
static int SkipFile(struct stream *s, int *count, rar_file_t ***file,
const rar_block_t *hdr, const char *volume_mrl)
{
int min_size = 7+21;
if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
min_size += 8;
if (hdr->size < (unsigned)min_size)
return -1;
bstr data = stream_peek(s, min_size);
if (data.len < min_size)
return -1;
const uint8_t *peek = (uint8_t *)data.start;
/* */
uint32_t file_size_low = AV_RL32(&peek[7+4]);
uint8_t method = peek[7+18];
uint16_t name_size = AV_RL16(&peek[7+19]);
uint32_t file_size_high = 0;
if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
file_size_high = AV_RL32(&peek[7+29]);
const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low;
char *name = calloc(1, name_size + 1);
if (!name)
return -1;
const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
if (name_offset + name_size <= hdr->size) {
const int max_size = name_offset + name_size;
bstr namedata = stream_peek(s, max_size);
if (namedata.len < max_size) {
free(name);
return -1;
}
memcpy(name, &namedata.start[name_offset], name_size);
}
rar_file_t *current = NULL;
if (method != 0x30) {
MP_WARN(s, "Ignoring compressed file %s (method=0x%2.2x)\n", name, method);
goto exit;
}
/* */
if( *count > 0 )
current = (*file)[*count - 1];
if (current &&
(current->is_complete ||
strcmp(current->name, name) ||
(hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0))
current = NULL;
if (!current) {
if (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS)
goto exit;
current = calloc(1, sizeof(*current));
if (!current)
goto exit;
MP_TARRAY_APPEND(NULL, *file, *count, current);
current->name = name;
current->size = file_size;
current->is_complete = false;
current->real_size = 0;
current->chunk_count = 0;
current->chunk = NULL;
name = NULL;
}
/* Append chunks */
rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
if (chunk) {
chunk->mrl = strdup(volume_mrl);
chunk->offset = stream_tell(s) + hdr->size;
chunk->size = hdr->add_size;
chunk->cummulated_size = 0;
if (current->chunk_count > 0) {
rar_file_chunk_t *previous = current->chunk[current->chunk_count-1];
chunk->cummulated_size += previous->cummulated_size +
previous->size;
}
MP_TARRAY_APPEND(NULL, current->chunk, current->chunk_count, chunk);
current->real_size += hdr->add_size;
}
if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0)
current->is_complete = true;
exit:
/* */
free(name);
/* We stop on the first non empty file if we cannot seek */
if (current) {
if (!s->seekable && current->size > 0)
return -1;
}
if (SkipBlock(s, hdr))
return -1;
return 0;
}
int RarProbe(struct stream *s)
{
bstr peek = stream_peek(s, rar_marker_size);
if (peek.len < rar_marker_size)
return -1;
if (memcmp(peek.start, rar_marker, rar_marker_size))
return -1;
return 0;
}
typedef struct {
const char *match;
const char *format;
int start;
int stop;
} rar_pattern_t;
static const rar_pattern_t *FindVolumePattern(const char *location)
{
static const rar_pattern_t patterns[] = {
{ ".part1.rar", "%s.part%.1d.rar", 2, 9 },
{ ".part01.rar", "%s.part%.2d.rar", 2, 99, },
{ ".part001.rar", "%s.part%.3d.rar", 2, 999 },
{ ".rar", "%s.%c%.2d", 0, 999 },
{ NULL, NULL, 0, 0 },
};
const size_t location_size = strlen(location);
for (int i = 0; patterns[i].match != NULL; i++) {
const size_t match_size = strlen(patterns[i].match);
if (location_size < match_size)
continue;
if (!strcmp(&location[location_size - match_size], patterns[i].match))
return &patterns[i];
}
return NULL;
}
int RarParse(struct stream *s, int *count, rar_file_t ***file)
{
*count = 0;
*file = NULL;
const rar_pattern_t *pattern = FindVolumePattern(s->url);
int volume_offset = 0;
char *volume_mrl;
if (asprintf(&volume_mrl, "%s", s->url) < 0)
return -1;
struct stream *vol = s;
for (;;) {
/* Skip marker & archive */
if (IgnoreBlock(vol, RAR_BLOCK_MARKER) ||
IgnoreBlock(vol, RAR_BLOCK_ARCHIVE)) {
if (vol != s)
free_stream(vol);
free(volume_mrl);
return -1;
}
/* */
int has_next = -1;
for (;;) {
rar_block_t bk;
int ret;
if (PeekBlock(vol, &bk))
break;
switch(bk.type) {
case RAR_BLOCK_END:
ret = SkipEnd(vol, &bk);
has_next = ret && (bk.flags & RAR_BLOCK_END_HAS_NEXT);
break;
case RAR_BLOCK_FILE:
ret = SkipFile(vol, count, file, &bk, volume_mrl);
break;
default:
ret = SkipBlock(vol, &bk);
break;
}
if (ret)
break;
}
if (has_next < 0 && *count > 0 && !(*file)[*count -1]->is_complete)
has_next = 1;
if (vol != s)
free_stream(vol);
if (!has_next || !pattern)
goto done;
/* Open next volume */
const int volume_index = pattern->start + volume_offset++;
if (volume_index > pattern->stop)
goto done;
char *volume_base;
if (asprintf(&volume_base, "%.*s",
(int)(strlen(s->url) - strlen(pattern->match)), s->url) < 0) {
goto done;
}
free(volume_mrl);
if (pattern->start) {
if (asprintf(&volume_mrl, pattern->format, volume_base, volume_index) < 0)
volume_mrl = NULL;
} else {
if (asprintf(&volume_mrl, pattern->format, volume_base,
'r' + volume_index / 100, volume_index % 100) < 0)
volume_mrl = NULL;
}
free(volume_base);
if (!volume_mrl)
goto done;
vol = stream_create(volume_mrl, STREAM_READ, s->cancel, s->global);
if (!vol)
goto done;
}
done:
free(volume_mrl);
if (*count == 0) {
talloc_free(*file);
return -1;
}
return 0;
}
int RarSeek(rar_file_t *file, uint64_t position)
{
if (position > file->real_size)
position = file->real_size;
/* Search the chunk */
const rar_file_chunk_t *old_chunk = file->current_chunk;
for (int i = 0; i < file->chunk_count; i++) {
file->current_chunk = file->chunk[i];
if (position < file->current_chunk->cummulated_size + file->current_chunk->size)
break;
}
file->i_pos = position;
const uint64_t offset = file->current_chunk->offset +
(position - file->current_chunk->cummulated_size);
if (strcmp(old_chunk->mrl, file->current_chunk->mrl)) {
if (file->s)
free_stream(file->s);
file->s = stream_create(file->current_chunk->mrl, STREAM_READ,
file->cancel, file->global);
}
return file->s ? stream_seek(file->s, offset) : 0;
}
ssize_t RarRead(rar_file_t *file, void *data, size_t size)
{
size_t total = 0;
while (total < size) {
const uint64_t chunk_end = file->current_chunk->cummulated_size + file->current_chunk->size;
int max = MPMIN(MPMIN((int64_t)(size - total), (int64_t)(chunk_end - file->i_pos)), INT_MAX);
if (max <= 0)
break;
int r = stream_read(file->s, data, max);
if (r <= 0)
break;
total += r;
data = (char *)data + r;
file->i_pos += r;
if (file->i_pos >= chunk_end &&
RarSeek(file, file->i_pos))
break;
}
return total;
}

View File

@ -1,61 +0,0 @@
/*****************************************************************************
* rar.h: uncompressed RAR parser
*****************************************************************************
* Copyright (C) 2008-2010 Laurent Aimar
* $Id: 4dea45925c2d8f319d692475bc0307fdd9f6cfe7 $
*
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef MP_RAR_H
#define MP_RAR_H
#include <inttypes.h>
#include <sys/types.h>
typedef struct {
char *mrl;
uint64_t offset;
uint64_t size;
uint64_t cummulated_size;
} rar_file_chunk_t;
typedef struct {
char *name;
uint64_t size;
bool is_complete;
int chunk_count;
rar_file_chunk_t **chunk;
uint64_t real_size; /* Gathered size */
// When actually reading the data
struct mpv_global *global;
struct mp_cancel *cancel;
uint64_t i_pos;
stream_t *s;
rar_file_chunk_t *current_chunk;
} rar_file_t;
int RarProbe(struct stream *);
void RarFileDelete(rar_file_t *);
int RarParse(struct stream *, int *, rar_file_t ***);
int RarSeek(rar_file_t *file, uint64_t position);
ssize_t RarRead(rar_file_t *file, void *data, size_t size);
#endif

View File

@ -45,10 +45,6 @@
// Includes additional padding in case sizes get rounded up by sector size.
#define TOTAL_BUFFER_SIZE (STREAM_MAX_BUFFER_SIZE + STREAM_MAX_SECTOR_SIZE)
extern const stream_info_t stream_info_cdda;
extern const stream_info_t stream_info_dvb;
extern const stream_info_t stream_info_tv;
extern const stream_info_t stream_info_smb;
extern const stream_info_t stream_info_null;
extern const stream_info_t stream_info_memory;
extern const stream_info_t stream_info_mf;
@ -56,56 +52,21 @@ extern const stream_info_t stream_info_ffmpeg;
extern const stream_info_t stream_info_ffmpeg_unsafe;
extern const stream_info_t stream_info_avdevice;
extern const stream_info_t stream_info_file;
extern const stream_info_t stream_info_ifo;
extern const stream_info_t stream_info_ifo_dvdnav;
extern const stream_info_t stream_info_dvd;
extern const stream_info_t stream_info_dvdnav;
extern const stream_info_t stream_info_bdmv_dir;
extern const stream_info_t stream_info_bluray;
extern const stream_info_t stream_info_bdnav;
extern const stream_info_t stream_info_rar;
extern const stream_info_t stream_info_edl;
extern const stream_info_t stream_info_libarchive;
extern const stream_info_t stream_info_cb;
static const stream_info_t *const stream_list[] = {
#if HAVE_CDDA
&stream_info_cdda,
#endif
&stream_info_ffmpeg,
&stream_info_ffmpeg_unsafe,
&stream_info_avdevice,
#if HAVE_DVBIN
&stream_info_dvb,
#endif
#if HAVE_TV
&stream_info_tv,
#endif
#if HAVE_LIBSMBCLIENT
&stream_info_smb,
#endif
#if HAVE_DVDREAD || HAVE_DVDNAV
&stream_info_ifo,
&stream_info_dvd,
#endif
#if HAVE_DVDNAV
&stream_info_ifo_dvdnav,
&stream_info_dvdnav,
#endif
#if HAVE_LIBBLURAY
&stream_info_bdmv_dir,
&stream_info_bluray,
&stream_info_bdnav,
#endif
#if HAVE_LIBARCHIVE
&stream_info_libarchive,
#endif
&stream_info_memory,
&stream_info_null,
&stream_info_mf,
&stream_info_edl,
&stream_info_rar,
&stream_info_file,
&stream_info_cb,
NULL

View File

@ -55,48 +55,13 @@ enum stream_ctrl {
// stream_memory.c
STREAM_CTRL_SET_CONTENTS,
// stream_rar.c
// stream_libarchive.c
STREAM_CTRL_GET_BASE_FILENAME,
// Certain network protocols
STREAM_CTRL_AVSEEK,
STREAM_CTRL_HAS_AVSEEK,
STREAM_CTRL_GET_METADATA,
// TV
STREAM_CTRL_TV_SET_SCAN,
STREAM_CTRL_SET_TV_FREQ,
STREAM_CTRL_GET_TV_FREQ,
STREAM_CTRL_SET_TV_COLORS,
STREAM_CTRL_GET_TV_COLORS,
STREAM_CTRL_TV_SET_NORM,
STREAM_CTRL_TV_STEP_NORM,
STREAM_CTRL_TV_SET_CHAN,
STREAM_CTRL_TV_GET_CHAN,
STREAM_CTRL_TV_STEP_CHAN,
STREAM_CTRL_TV_LAST_CHAN,
STREAM_CTRL_DVB_SET_CHANNEL,
STREAM_CTRL_DVB_SET_CHANNEL_NAME,
STREAM_CTRL_DVB_GET_CHANNEL_NAME,
STREAM_CTRL_DVB_STEP_CHANNEL,
// Optical discs
STREAM_CTRL_GET_TIME_LENGTH,
STREAM_CTRL_GET_DVD_INFO,
STREAM_CTRL_GET_DISC_NAME,
STREAM_CTRL_GET_NUM_CHAPTERS,
STREAM_CTRL_GET_CURRENT_TIME,
STREAM_CTRL_GET_CHAPTER_TIME,
STREAM_CTRL_SEEK_TO_TIME,
STREAM_CTRL_GET_ASPECT_RATIO,
STREAM_CTRL_GET_NUM_ANGLES,
STREAM_CTRL_GET_ANGLE,
STREAM_CTRL_SET_ANGLE,
STREAM_CTRL_GET_NUM_TITLES,
STREAM_CTRL_GET_TITLE_LENGTH, // double* (in: title number, out: len)
STREAM_CTRL_GET_LANG,
STREAM_CTRL_GET_CURRENT_TITLE,
STREAM_CTRL_SET_CURRENT_TITLE,
};
struct stream_lang_req {
@ -110,12 +75,6 @@ struct stream_dvd_info_req {
int num_subs;
};
// for STREAM_CTRL_SET_TV_COLORS
#define TV_COLOR_BRIGHTNESS 1
#define TV_COLOR_HUE 2
#define TV_COLOR_SATURATION 3
#define TV_COLOR_CONTRAST 4
// for STREAM_CTRL_AVSEEK
struct stream_avseek {
int stream_index;

View File

@ -1,612 +0,0 @@
/*
* Copyright (C) 2010 Benjamin Zores <ben@geexbox.org>
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Blu-ray parser/reader using libbluray
* Use 'git clone git://git.videolan.org/libbluray' to get it.
*
* TODO:
* - Add descrambled keys database support (KEYDB.cfg)
*
*/
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <libbluray/bluray.h>
#include <libbluray/meta_data.h>
#include <libbluray/overlay.h>
#include <libbluray/keys.h>
#include <libbluray/bluray-version.h>
#include <libbluray/log_control.h>
#include <libavutil/common.h>
#include "config.h"
#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#include "options/m_config.h"
#include "options/path.h"
#include "stream.h"
#include "osdep/timer.h"
#include "sub/osd.h"
#include "sub/img_convert.h"
#include "video/mp_image.h"
#define BLURAY_SECTOR_SIZE 6144
#define BLURAY_DEFAULT_ANGLE 0
#define BLURAY_DEFAULT_CHAPTER 0
#define BLURAY_PLAYLIST_TITLE -3
#define BLURAY_DEFAULT_TITLE -2
#define BLURAY_MENU_TITLE -1
// 90khz ticks
#define BD_TIMEBASE (90000)
#define BD_TIME_TO_MP(x) ((x) / (double)(BD_TIMEBASE))
#define BD_TIME_FROM_MP(x) ((uint64_t)(x * BD_TIMEBASE))
// copied from aacs.h in libaacs
#define AACS_ERROR_CORRUPTED_DISC -1 /* opening or reading of AACS files failed */
#define AACS_ERROR_NO_CONFIG -2 /* missing config file */
#define AACS_ERROR_NO_PK -3 /* no matching processing key */
#define AACS_ERROR_NO_CERT -4 /* no valid certificate */
#define AACS_ERROR_CERT_REVOKED -5 /* certificate has been revoked */
#define AACS_ERROR_MMC_OPEN -6 /* MMC open failed (no MMC drive ?) */
#define AACS_ERROR_MMC_FAILURE -7 /* MMC failed */
#define AACS_ERROR_NO_DK -8 /* no matching device key */
struct bluray_priv_s {
BLURAY *bd;
BLURAY_TITLE_INFO *title_info;
int num_titles;
int current_angle;
int current_title;
int current_playlist;
int cfg_title;
int cfg_playlist;
char *cfg_device;
bool use_nav;
};
static void destruct(struct bluray_priv_s *priv)
{
if (priv->title_info)
bd_free_title_info(priv->title_info);
bd_close(priv->bd);
}
inline static int play_playlist(struct bluray_priv_s *priv, int playlist)
{
return bd_select_playlist(priv->bd, playlist);
}
inline static int play_title(struct bluray_priv_s *priv, int title)
{
return bd_select_title(priv->bd, title);
}
static void bluray_stream_close(stream_t *s)
{
destruct(s->priv);
}
static void handle_event(stream_t *s, const BD_EVENT *ev)
{
struct bluray_priv_s *b = s->priv;
switch (ev->event) {
case BD_EVENT_MENU:
break;
case BD_EVENT_STILL:
break;
case BD_EVENT_STILL_TIME:
bd_read_skip_still(b->bd);
break;
case BD_EVENT_END_OF_TITLE:
break;
case BD_EVENT_PLAYLIST:
b->current_playlist = ev->param;
b->current_title = bd_get_current_title(b->bd);
if (b->title_info)
bd_free_title_info(b->title_info);
b->title_info = bd_get_playlist_info(b->bd, b->current_playlist,
b->current_angle);
break;
case BD_EVENT_TITLE:
if (ev->param == BLURAY_TITLE_FIRST_PLAY) {
b->current_title = bd_get_current_title(b->bd);
} else
b->current_title = ev->param;
if (b->title_info) {
bd_free_title_info(b->title_info);
b->title_info = NULL;
}
break;
case BD_EVENT_ANGLE:
b->current_angle = ev->param;
if (b->title_info) {
bd_free_title_info(b->title_info);
b->title_info = bd_get_playlist_info(b->bd, b->current_playlist,
b->current_angle);
}
break;
case BD_EVENT_POPUP:
break;
#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 5, 0)
case BD_EVENT_DISCONTINUITY:
break;
#endif
default:
MP_TRACE(s, "Unhandled event: %d %d\n", ev->event, ev->param);
break;
}
}
static int bluray_stream_fill_buffer(stream_t *s, char *buf, int len)
{
struct bluray_priv_s *b = s->priv;
BD_EVENT event;
while (bd_get_event(b->bd, &event))
handle_event(s, &event);
return bd_read(b->bd, buf, len);
}
static int bluray_stream_control(stream_t *s, int cmd, void *arg)
{
struct bluray_priv_s *b = s->priv;
switch (cmd) {
case STREAM_CTRL_GET_NUM_CHAPTERS: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
*((unsigned int *) arg) = ti->chapter_count;
return STREAM_OK;
}
case STREAM_CTRL_GET_CHAPTER_TIME: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
int chapter = *(double *)arg;
double time = MP_NOPTS_VALUE;
if (chapter >= 0 || chapter < ti->chapter_count)
time = BD_TIME_TO_MP(ti->chapters[chapter].start);
if (time == MP_NOPTS_VALUE)
return STREAM_ERROR;
*(double *)arg = time;
return STREAM_OK;
}
case STREAM_CTRL_SET_CURRENT_TITLE: {
const uint32_t title = *((unsigned int*)arg);
if (title >= b->num_titles || !play_title(b, title))
return STREAM_UNSUPPORTED;
b->current_title = title;
return STREAM_OK;
}
case STREAM_CTRL_GET_CURRENT_TITLE: {
*((unsigned int *) arg) = b->current_title;
return STREAM_OK;
}
case STREAM_CTRL_GET_NUM_TITLES: {
*((unsigned int *)arg) = b->num_titles;
return STREAM_OK;
}
case STREAM_CTRL_GET_TIME_LENGTH: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
*((double *) arg) = BD_TIME_TO_MP(ti->duration);
return STREAM_OK;
}
case STREAM_CTRL_GET_CURRENT_TIME: {
*((double *) arg) = BD_TIME_TO_MP(bd_tell_time(b->bd));
return STREAM_OK;
}
case STREAM_CTRL_SEEK_TO_TIME: {
double pts = *((double *) arg);
bd_seek_time(b->bd, BD_TIME_FROM_MP(pts));
stream_drop_buffers(s);
// API makes it hard to determine seeking success
return STREAM_OK;
}
case STREAM_CTRL_GET_NUM_ANGLES: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
*((int *) arg) = ti->angle_count;
return STREAM_OK;
}
case STREAM_CTRL_GET_ANGLE: {
*((int *) arg) = b->current_angle;
return STREAM_OK;
}
case STREAM_CTRL_SET_ANGLE: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (!ti)
return STREAM_UNSUPPORTED;
int angle = *((int *) arg);
if (angle < 0 || angle > ti->angle_count)
return STREAM_UNSUPPORTED;
b->current_angle = angle;
bd_seamless_angle_change(b->bd, angle);
return STREAM_OK;
}
case STREAM_CTRL_GET_LANG: {
const BLURAY_TITLE_INFO *ti = b->title_info;
if (ti && ti->clip_count) {
struct stream_lang_req *req = arg;
BLURAY_STREAM_INFO *si = NULL;
int count = 0;
switch (req->type) {
case STREAM_AUDIO:
count = ti->clips[0].audio_stream_count;
si = ti->clips[0].audio_streams;
break;
case STREAM_SUB:
count = ti->clips[0].pg_stream_count;
si = ti->clips[0].pg_streams;
break;
}
for (int n = 0; n < count; n++) {
BLURAY_STREAM_INFO *i = &si[n];
if (i->pid == req->id) {
snprintf(req->name, sizeof(req->name), "%.4s", i->lang);
return STREAM_OK;
}
}
}
return STREAM_ERROR;
}
case STREAM_CTRL_GET_DISC_NAME: {
const struct meta_dl *meta = bd_get_meta(b->bd);
if (!meta || !meta->di_name || !meta->di_name[0])
break;
*(char**)arg = talloc_strdup(NULL, meta->di_name);
return STREAM_OK;
}
case STREAM_CTRL_GET_SIZE:
*(int64_t *)arg = bd_get_title_size(b->bd);
return STREAM_OK;
default:
break;
}
return STREAM_UNSUPPORTED;
}
static const char *aacs_strerr(int err)
{
switch (err) {
case AACS_ERROR_CORRUPTED_DISC: return "opening or reading of AACS files failed";
case AACS_ERROR_NO_CONFIG: return "missing config file";
case AACS_ERROR_NO_PK: return "no matching processing key";
case AACS_ERROR_NO_CERT: return "no valid certificate";
case AACS_ERROR_CERT_REVOKED: return "certificate has been revoked";
case AACS_ERROR_MMC_OPEN: return "MMC open failed (maybe no MMC drive?)";
case AACS_ERROR_MMC_FAILURE: return "MMC failed";
case AACS_ERROR_NO_DK: return "no matching device key";
default: return "unknown error";
}
}
static bool check_disc_info(stream_t *s)
{
struct bluray_priv_s *b = s->priv;
const BLURAY_DISC_INFO *info = bd_get_disc_info(b->bd);
// check Blu-ray
if (!info->bluray_detected) {
MP_ERR(s, "Given stream is not a Blu-ray.\n");
return false;
}
// check AACS
if (info->aacs_detected) {
if (!info->libaacs_detected) {
MP_ERR(s, "AACS encryption detected but cannot find libaacs.\n");
return false;
}
if (!info->aacs_handled) {
MP_ERR(s, "AACS error: %s\n", aacs_strerr(info->aacs_error_code));
return false;
}
}
// check BD+
if (info->bdplus_detected) {
if (!info->libbdplus_detected) {
MP_ERR(s, "BD+ encryption detected but cannot find libbdplus.\n");
return false;
}
if (!info->bdplus_handled) {
MP_ERR(s, "Cannot decrypt BD+ encryption.\n");
return false;
}
}
return true;
}
static void select_initial_title(stream_t *s, int title_guess) {
struct bluray_priv_s *b = s->priv;
if (b->cfg_title == BLURAY_PLAYLIST_TITLE) {
if (!play_playlist(b, b->cfg_playlist))
MP_WARN(s, "Couldn't start playlist '%05d'.\n", b->cfg_playlist);
b->current_title = bd_get_current_title(b->bd);
} else {
int title = -1;
if (b->cfg_title != BLURAY_DEFAULT_TITLE )
title = b->cfg_title;
else
title = title_guess;
if (title < 0)
return;
if (play_title(b, title))
b->current_title = title;
else {
MP_WARN(s, "Couldn't start title '%d'.\n", title);
b->current_title = bd_get_current_title(b->bd);
}
}
}
static int bluray_stream_open_internal(stream_t *s)
{
struct bluray_priv_s *b = s->priv;
char *device = NULL;
/* find the requested device */
if (b->cfg_device && b->cfg_device[0]) {
device = b->cfg_device;
} else {
mp_read_option_raw(s->global, "bluray-device", &m_option_type_string,
&device);
}
if (!device || !device[0]) {
MP_ERR(s, "No Blu-ray device/location was specified ...\n");
return STREAM_UNSUPPORTED;
}
if (!mp_msg_test(s->log, MSGL_DEBUG))
bd_set_debug_mask(0);
/* open device */
BLURAY *bd = bd_open(device, NULL);
if (!bd) {
MP_ERR(s, "Couldn't open Blu-ray device: %s\n", device);
return STREAM_UNSUPPORTED;
}
b->bd = bd;
if (!check_disc_info(s)) {
destruct(b);
return STREAM_UNSUPPORTED;
}
int title_guess = BLURAY_DEFAULT_TITLE;
if (b->use_nav) {
MP_FATAL(s, "BluRay menu support has been removed.\n");
return STREAM_ERROR;
} else {
/* check for available titles on disc */
b->num_titles = bd_get_titles(bd, TITLES_RELEVANT, 0);
if (!b->num_titles) {
MP_ERR(s, "Can't find any Blu-ray-compatible title here.\n");
destruct(b);
return STREAM_UNSUPPORTED;
}
MP_INFO(s, "List of available titles:\n");
/* parse titles information */
uint64_t max_duration = 0;
for (int i = 0; i < b->num_titles; i++) {
BLURAY_TITLE_INFO *ti = bd_get_title_info(bd, i, 0);
if (!ti)
continue;
char *time = mp_format_time(ti->duration / 90000, false);
MP_INFO(s, "idx: %3d duration: %s (playlist: %05d.mpls)\n",
i, time, ti->playlist);
talloc_free(time);
/* try to guess which title may contain the main movie */
if (ti->duration > max_duration) {
max_duration = ti->duration;
title_guess = i;
}
bd_free_title_info(ti);
}
}
// these should be set before any callback
b->current_angle = -1;
b->current_title = -1;
// initialize libbluray event queue
bd_get_event(bd, NULL);
select_initial_title(s, title_guess);
s->fill_buffer = bluray_stream_fill_buffer;
s->close = bluray_stream_close;
s->control = bluray_stream_control;
s->sector_size = BLURAY_SECTOR_SIZE;
s->priv = b;
s->demuxer = "+disc";
MP_VERBOSE(s, "Blu-ray successfully opened.\n");
return STREAM_OK;
}
const stream_info_t stream_info_bdnav;
static int bluray_stream_open(stream_t *s)
{
struct bluray_priv_s *b = talloc_zero(s, struct bluray_priv_s);
s->priv = b;
b->use_nav = s->info == &stream_info_bdnav;
bstr title, bdevice, rest = { .len = 0 };
bstr_split_tok(bstr0(s->path), "/", &title, &bdevice);
b->cfg_title = BLURAY_DEFAULT_TITLE;
if (bstr_equals0(title, "longest") || bstr_equals0(title, "first")) {
b->cfg_title = BLURAY_DEFAULT_TITLE;
} else if (bstr_equals0(title, "menu")) {
b->cfg_title = BLURAY_MENU_TITLE;
} else if (bstr_equals0(title, "mpls")) {
bstr_split_tok(bdevice, "/", &title, &bdevice);
long long pl = bstrtoll(title, &rest, 10);
if (rest.len) {
MP_ERR(s, "number expected: '%.*s'\n", BSTR_P(rest));
return STREAM_ERROR;
} else if (pl < 0 || 99999 < pl) {
MP_ERR(s, "invalid playlist: '%.*s', must be in the range 0-99999\n",
BSTR_P(title));
return STREAM_ERROR;
}
b->cfg_playlist = pl;
b->cfg_title = BLURAY_PLAYLIST_TITLE;
} else if (title.len) {
long long t = bstrtoll(title, &rest, 10);
if (rest.len) {
MP_ERR(s, "number expected: '%.*s'\n", BSTR_P(rest));
return STREAM_ERROR;
} else if (t < 0 || 99999 < t) {
MP_ERR(s, "invalid title: '%.*s', must be in the range 0-99999\n",
BSTR_P(title));
return STREAM_ERROR;
}
b->cfg_title = t;
}
b->cfg_device = bstrto0(b, bdevice);
return bluray_stream_open_internal(s);
}
const stream_info_t stream_info_bluray = {
.name = "bd",
.open = bluray_stream_open,
.protocols = (const char*const[]){ "bd", "br", "bluray", NULL },
};
const stream_info_t stream_info_bdnav = {
.name = "bdnav",
.open = bluray_stream_open,
.protocols = (const char*const[]){ "bdnav", "brnav", "bluraynav", NULL },
};
static bool check_bdmv(const char *path)
{
if (strcasecmp(mp_basename(path), "MovieObject.bdmv"))
return false;
FILE *temp = fopen(path, "rb");
if (!temp)
return false;
char data[50] = {0};
fread(data, 50, 1, temp);
fclose(temp);
bstr bdata = {data, 50};
return bstr_startswith0(bdata, "MOBJ0100") || // AVCHD
bstr_startswith0(bdata, "MOBJ0200") || // Blu-ray
bstr_startswith0(bdata, "MOBJ0300"); // UHD BD
}
// Destructively remove the current trailing path component.
static void remove_prefix(char *path)
{
size_t len = strlen(path);
#if HAVE_DOS_PATHS
const char *seps = "/\\";
#else
const char *seps = "/";
#endif
while (len > 0 && !strchr(seps, path[len - 1]))
len--;
while (len > 0 && strchr(seps, path[len - 1]))
len--;
path[len] = '\0';
}
static int bdmv_dir_stream_open(stream_t *stream)
{
struct bluray_priv_s *priv = talloc_ptrtype(stream, priv);
stream->priv = priv;
*priv = (struct bluray_priv_s){
.cfg_title = BLURAY_DEFAULT_TITLE,
};
if (!stream->access_references)
goto unsupported;
char *path = mp_file_get_path(priv, bstr0(stream->url));
if (!path)
goto unsupported;
// We allow the path to point to a directory containing BDMV/, a
// directory containing MovieObject.bdmv, or that file itself.
if (!check_bdmv(path)) {
// On UNIX, just assume the filename has always this case.
char *npath = mp_path_join(priv, path, "MovieObject.bdmv");
if (!check_bdmv(npath)) {
npath = mp_path_join(priv, path, "BDMV/MovieObject.bdmv");
if (!check_bdmv(npath))
goto unsupported;
}
path = npath;
}
// Go up by 2 levels.
remove_prefix(path);
remove_prefix(path);
priv->cfg_device = path;
if (strlen(priv->cfg_device) <= 1)
goto unsupported;
MP_INFO(stream, "BDMV detected. Redirecting to bluray://\n");
return bluray_stream_open_internal(stream);
unsupported:
talloc_free(priv);
stream->priv = NULL;
return STREAM_UNSUPPORTED;
}
const stream_info_t stream_info_bdmv_dir = {
.name = "bdmv/bluray",
.open = bdmv_dir_stream_open,
.protocols = (const char*const[]){ "file", "", NULL },
};

View File

@ -1,402 +0,0 @@
/*
* Original author: Albeu
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <cdio/cdio.h>
#if CDIO_API_VERSION < 6
#define OLD_API
#endif
#ifdef OLD_API
#include <cdio/cdda.h>
#include <cdio/paranoia.h>
#else
#include <cdio/paranoia/cdda.h>
#include <cdio/paranoia/paranoia.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "mpv_talloc.h"
#include "stream.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "options/options.h"
#include "common/msg.h"
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
typedef struct cdda_params {
cdrom_drive_t *cd;
cdrom_paranoia_t *cdp;
int sector;
int start_sector;
int end_sector;
// options
int speed;
int paranoia_mode;
int sector_size;
int search_overlap;
int toc_bias;
int toc_offset;
int skip;
char *device;
int span[2];
int cdtext;
} cdda_priv;
#define OPT_BASE_STRUCT struct cdda_params
const struct m_sub_options stream_cdda_conf = {
.opts = (const m_option_t[]) {
OPT_INTRANGE("speed", speed, 0, 1, 100),
OPT_INTRANGE("paranoia", paranoia_mode, 0, 0, 2),
OPT_INTRANGE("sector-size", sector_size, 0, 1, 100),
OPT_INTRANGE("overlap", search_overlap, 0, 0, 75),
OPT_INT("toc-bias", toc_bias, 0),
OPT_INT("toc-offset", toc_offset, 0),
OPT_FLAG("skip", skip, 0),
OPT_INTPAIR("span", span, 0),
OPT_FLAG("cdtext", cdtext, 0),
{0}
},
.size = sizeof(struct cdda_params),
.defaults = &(const struct cdda_params){
.search_overlap = -1,
.skip = 1,
},
};
static const char *const cdtext_name[] = {
#ifdef OLD_API
[CDTEXT_ARRANGER] = "Arranger",
[CDTEXT_COMPOSER] = "Composer",
[CDTEXT_MESSAGE] = "Message",
[CDTEXT_ISRC] = "ISRC",
[CDTEXT_PERFORMER] = "Performer",
[CDTEXT_SONGWRITER] = "Songwriter",
[CDTEXT_TITLE] = "Title",
[CDTEXT_UPC_EAN] = "UPC_EAN",
#else
[CDTEXT_FIELD_ARRANGER] = "Arranger",
[CDTEXT_FIELD_COMPOSER] = "Composer",
[CDTEXT_FIELD_MESSAGE] = "Message",
[CDTEXT_FIELD_ISRC] = "ISRC",
[CDTEXT_FIELD_PERFORMER] = "Performer",
[CDTEXT_FIELD_SONGWRITER] = "Songwriter",
[CDTEXT_FIELD_TITLE] = "Title",
[CDTEXT_FIELD_UPC_EAN] = "UPC_EAN",
#endif
};
static void print_cdtext(stream_t *s, int track)
{
cdda_priv* p = (cdda_priv*)s->priv;
if (!p->cdtext)
return;
#ifdef OLD_API
cdtext_t *text = cdio_get_cdtext(p->cd->p_cdio, track);
#else
cdtext_t *text = cdio_get_cdtext(p->cd->p_cdio);
#endif
int header = 0;
if (text) {
for (int i = 0; i < sizeof(cdtext_name) / sizeof(cdtext_name[0]); i++) {
const char *name = cdtext_name[i];
#ifdef OLD_API
const char *value = cdtext_get_const(i, text);
#else
const char *value = cdtext_get_const(text, i, track);
#endif
if (name && value) {
if (!header)
MP_INFO(s, "CD-Text (%s):\n", track ? "track" : "CD");
header = 1;
MP_INFO(s, " %s: '%s'\n", name, value);
}
}
}
}
static void print_track_info(stream_t *s, int track)
{
MP_INFO(s, "Switched to track %d\n", track);
print_cdtext(s, track);
}
static void cdparanoia_callback(long int inpos, paranoia_cb_mode_t function)
{
}
static int fill_buffer(stream_t *s, char *buffer, int max_len)
{
cdda_priv *p = (cdda_priv *)s->priv;
int16_t *buf;
int i;
if (max_len < CDIO_CD_FRAMESIZE_RAW)
return -1;
if ((p->sector < p->start_sector) || (p->sector > p->end_sector)) {
return 0;
}
buf = paranoia_read(p->cdp, cdparanoia_callback);
if (!buf)
return 0;
p->sector++;
memcpy(buffer, buf, CDIO_CD_FRAMESIZE_RAW);
for (i = 0; i < p->cd->tracks; i++) {
if (p->cd->disc_toc[i].dwStartSector == p->sector - 1) {
print_track_info(s, i + 1);
break;
}
}
return CDIO_CD_FRAMESIZE_RAW;
}
static int seek(stream_t *s, int64_t newpos)
{
cdda_priv *p = (cdda_priv *)s->priv;
int sec;
int current_track = 0, seeked_track = 0;
int seek_to_track = 0;
int i;
newpos += p->start_sector * CDIO_CD_FRAMESIZE_RAW;
sec = newpos / CDIO_CD_FRAMESIZE_RAW;
if (newpos < 0 || sec > p->end_sector) {
p->sector = p->end_sector + 1;
return 0;
}
for (i = 0; i < p->cd->tracks; i++) {
if (p->sector >= p->cd->disc_toc[i].dwStartSector
&& p->sector < p->cd->disc_toc[i + 1].dwStartSector)
current_track = i;
if (sec >= p->cd->disc_toc[i].dwStartSector
&& sec < p->cd->disc_toc[i + 1].dwStartSector)
{
seeked_track = i;
seek_to_track = sec == p->cd->disc_toc[i].dwStartSector;
}
}
if (current_track != seeked_track && !seek_to_track)
print_track_info(s, seeked_track + 1);
p->sector = sec;
paranoia_seek(p->cdp, sec, SEEK_SET);
return 1;
}
static void close_cdda(stream_t *s)
{
cdda_priv *p = (cdda_priv *)s->priv;
paranoia_free(p->cdp);
cdda_close(p->cd);
}
static int get_track_by_sector(cdda_priv *p, unsigned int sector)
{
int i;
for (i = p->cd->tracks; i >= 0; --i)
if (p->cd->disc_toc[i].dwStartSector <= sector)
break;
return i;
}
static int control(stream_t *stream, int cmd, void *arg)
{
cdda_priv *p = stream->priv;
switch (cmd) {
case STREAM_CTRL_GET_NUM_CHAPTERS:
{
int start_track = get_track_by_sector(p, p->start_sector);
int end_track = get_track_by_sector(p, p->end_sector);
if (start_track == -1 || end_track == -1)
return STREAM_ERROR;
*(unsigned int *)arg = end_track + 1 - start_track;
return STREAM_OK;
}
case STREAM_CTRL_GET_CHAPTER_TIME:
{
int track = *(double *)arg;
int start_track = get_track_by_sector(p, p->start_sector);
int end_track = get_track_by_sector(p, p->end_sector);
track += start_track;
if (track > end_track)
return STREAM_ERROR;
int64_t sector = p->cd->disc_toc[track].dwStartSector;
int64_t pos = sector * CDIO_CD_FRAMESIZE_RAW;
// Assume standard audio CD: 44.1khz, 2 channels, s16 samples
*(double *)arg = pos / (44100.0 * 2 * 2);
return STREAM_OK;
}
case STREAM_CTRL_GET_SIZE:
*(int64_t *)arg =
(p->end_sector + 1 - p->start_sector) * CDIO_CD_FRAMESIZE_RAW;
return STREAM_OK;
}
return STREAM_UNSUPPORTED;
}
static int open_cdda(stream_t *st)
{
st->priv = mp_get_config_group(st, st->global, &stream_cdda_conf);
cdda_priv *priv = st->priv;
cdda_priv *p = priv;
int mode = p->paranoia_mode;
int offset = p->toc_offset;
cdrom_drive_t *cdd = NULL;
int last_track;
char *global_device;
mp_read_option_raw(st->global, "cdrom-device", &m_option_type_string,
&global_device);
talloc_steal(st, global_device);
if (st->path[0]) {
p->device = st->path;
} else if (global_device && global_device[0]) {
p->device = global_device;
} else {
p->device = DEFAULT_CDROM_DEVICE;
}
#if defined(__NetBSD__)
cdd = cdda_identify_scsi(p->device, p->device, 0, NULL);
#else
cdd = cdda_identify(p->device, 0, NULL);
#endif
if (!cdd) {
MP_ERR(st, "Can't open CDDA device.\n");
return STREAM_ERROR;
}
cdda_verbose_set(cdd, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
if (p->sector_size)
cdd->nsectors = p->sector_size;
if (cdda_open(cdd) != 0) {
MP_ERR(st, "Can't open disc.\n");
cdda_close(cdd);
return STREAM_ERROR;
}
priv->cd = cdd;
if (p->toc_bias)
offset -= cdda_track_firstsector(cdd, 1);
if (offset) {
for (int n = 0; n < cdd->tracks + 1; n++)
cdd->disc_toc[n].dwStartSector += offset;
}
if (p->speed > 0)
cdda_speed_set(cdd, p->speed);
last_track = cdda_tracks(cdd);
if (p->span[0] > last_track)
p->span[0] = last_track;
if (p->span[1] < p->span[0])
p->span[1] = p->span[0];
if (p->span[1] > last_track)
p->span[1] = last_track;
if (p->span[0])
priv->start_sector = cdda_track_firstsector(cdd, p->span[0]);
else
priv->start_sector = cdda_disc_firstsector(cdd);
if (p->span[1])
priv->end_sector = cdda_track_lastsector(cdd, p->span[1]);
else
priv->end_sector = cdda_disc_lastsector(cdd);
priv->cdp = paranoia_init(cdd);
if (priv->cdp == NULL) {
cdda_close(cdd);
free(priv);
return STREAM_ERROR;
}
if (mode == 0)
mode = PARANOIA_MODE_DISABLE;
else if (mode == 1)
mode = PARANOIA_MODE_OVERLAP;
else
mode = PARANOIA_MODE_FULL;
if (p->skip)
mode &= ~PARANOIA_MODE_NEVERSKIP;
else
mode |= PARANOIA_MODE_NEVERSKIP;
if (p->search_overlap > 0)
mode |= PARANOIA_MODE_OVERLAP;
else if (p->search_overlap == 0)
mode &= ~PARANOIA_MODE_OVERLAP;
paranoia_modeset(priv->cdp, mode);
if (p->search_overlap > 0)
paranoia_overlapset(priv->cdp, p->search_overlap);
paranoia_seek(priv->cdp, priv->start_sector, SEEK_SET);
priv->sector = priv->start_sector;
st->priv = priv;
st->sector_size = CDIO_CD_FRAMESIZE_RAW;
st->fill_buffer = fill_buffer;
st->seek = seek;
st->seekable = true;
st->control = control;
st->close = close_cdda;
st->streaming = true;
st->demuxer = "+disc";
print_cdtext(st, 0);
return STREAM_OK;
}
const stream_info_t stream_info_cdda = {
.name = "cdda",
.open = open_cdda,
.protocols = (const char*const[]){"cdda", NULL },
};

File diff suppressed because it is too large Load Diff

View File

@ -1,993 +0,0 @@
/*
* Original author: Benjamin Zores
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <libgen.h>
#include <errno.h>
#include <stdint.h>
#include <dvdread/dvd_reader.h>
#include <dvdread/ifo_types.h>
#include <dvdread/ifo_read.h>
#include <dvdread/nav_read.h>
#include "osdep/io.h"
#include "config.h"
#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#define FIRST_AC3_AID 128
#define FIRST_DTS_AID 136
#define FIRST_MPG_AID 0
#define FIRST_PCM_AID 160
#include "stream.h"
#include "options/m_config.h"
#include "options/options.h"
#include "options/path.h"
#include "stream_dvd_common.h"
#define LIBDVDREAD_VERSION(maj,min,micro) ((maj)*10000 + (min)*100 + (micro))
/*
* Try to autodetect the libdvd-0.9.0 library
* (0.9.0 removed the <dvdread/dvd_udf.h> header, and moved the two defines
* DVD_VIDEO_LB_LEN and MAX_UDF_FILE_NAME_LEN from it to
* <dvdread/dvd_reader.h>)
*/
#ifndef DVDREAD_VERSION
#if defined(DVD_VIDEO_LB_LEN) && defined(MAX_UDF_FILE_NAME_LEN)
#define DVDREAD_VERSION LIBDVDREAD_VERSION(0,9,0)
#else
#define DVDREAD_VERSION LIBDVDREAD_VERSION(0,8,0)
#endif
#endif
typedef struct {
int id; // 0 - 31 mpeg; 128 - 159 ac3; 160 - 191 pcm
int language;
int type;
int channels;
} stream_language_t;
typedef struct {
dvd_reader_t *dvd;
dvd_file_t *title;
ifo_handle_t *vmg_file;
tt_srpt_t *tt_srpt;
ifo_handle_t *vts_file;
vts_ptt_srpt_t *vts_ptt_srpt;
pgc_t *cur_pgc;
//
int cur_title;
int cur_cell;
int last_cell;
int cur_pack;
int cell_last_pack;
int cur_pgc_idx;
// Navi:
int packs_left;
dsi_t dsi_pack;
int angle_seek;
unsigned int *cell_times_table;
// audio datas
int nr_of_channels;
stream_language_t audio_streams[32];
// subtitles
int nr_of_subtitles;
stream_language_t subtitles[32];
int dvd_angle;
char *dvd_device_current;
int dvd_speed;
int dvd_title;
int cfg_title;
char *cfg_device;
} dvd_priv_t;
static int dvd_lang_from_aid(stream_t *stream, int id) {
dvd_priv_t *d;
int i;
if (!stream) return 0;
d = stream->priv;
if (!d) return 0;
for(i=0;i<d->nr_of_channels;i++) {
if(d->audio_streams[i].id==id)
return d->audio_streams[i].language;
}
return 0;
}
static int dvd_number_of_subs(stream_t *stream) {
int i;
int maxid = -1;
dvd_priv_t *d;
if (!stream) return -1;
d = stream->priv;
if (!d) return -1;
for (i = 0; i < d->nr_of_subtitles; i++)
if (d->subtitles[i].id > maxid) maxid = d->subtitles[i].id;
return maxid + 1;
}
static int dvd_lang_from_sid(stream_t *stream, int id) {
int i;
dvd_priv_t *d;
if (!stream) return 0;
d = stream->priv;
if (!d) return 0;
for (i = 0; i < d->nr_of_subtitles; i++)
if (d->subtitles[i].id == id && d->subtitles[i].language) return d->subtitles[i].language;
return 0;
}
static int dvd_next_cell(stream_t *stream, dvd_priv_t *d) {
int next_cell=d->cur_cell;
MP_DBG(stream, "dvd_next_cell: next1=0x%X \n",next_cell);
if( d->cur_pgc->cell_playback[ next_cell ].block_type == BLOCK_TYPE_ANGLE_BLOCK ) {
while(next_cell<d->last_cell) {
if( d->cur_pgc->cell_playback[next_cell].block_mode == BLOCK_MODE_LAST_CELL )
break;
++next_cell;
}
}
MP_DBG(stream, "dvd_next_cell: next2=0x%X \n",next_cell);
++next_cell;
if(next_cell>=d->last_cell)
return -1; // EOF
if(d->cur_pgc->cell_playback[next_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK ) {
next_cell+=d->dvd_angle-1;
if(next_cell>=d->last_cell)
return -1; // EOF
}
MP_DBG(stream, "dvd_next_cell: next3=0x%X \n",next_cell);
return next_cell;
}
static int dvd_read_sector(stream_t *stream, dvd_priv_t *d, unsigned char *data)
{
int len;
if(d->packs_left==0) {
/**
* If we're not at the end of this cell, we can determine the next
* VOBU to display using the VOBU_SRI information section of the
* DSI. Using this value correctly follows the current angle,
* avoiding the doubled scenes in The Matrix, and makes our life
* really happy.
*
* Otherwise, we set our next address past the end of this cell to
* force the code above to go to the next cell in the program.
*/
if(d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn + ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
MP_DBG(stream, "Navi new pos=0x%X \n",d->cur_pack);
} else {
// end of cell! find next cell!
MP_VERBOSE(stream, "--- END OF CELL !!! ---\n");
d->cur_pack=d->cell_last_pack+1;
}
}
read_next:
if(d->cur_pack>d->cell_last_pack) {
// end of cell!
int next=dvd_next_cell(stream, d);
if(next>=0) {
d->cur_cell=next;
// if( d->cur_pgc->cell_playback[d->cur_cell].block_type
// == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle-1;
d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
MP_VERBOSE(stream, "DVD next cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack);
} else
return -1; // EOF
}
len = DVDReadBlocks(d->title, d->cur_pack, 1, data);
// only == 0 should indicate an error, but some dvdread version are buggy when used with dvdcss
if(len <= 0) return -1; //error
if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF &&
data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF) {
// found a Navi packet!!!
#if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0)
navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]));
#else
navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t));
#endif
if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ) {
MP_VERBOSE(stream, "Invalid NAVI packet! lba=0x%X navi=0x%X \n",
d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn);
} else {
// process!
d->packs_left = d->dsi_pack.dsi_gi.vobu_ea;
MP_DBG(stream, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left);
//navPrint_DSI(&d->dsi_pack);
MP_TRACE(stream, "\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d->cur_cell,
d->dsi_pack.dsi_gi.vobu_c_idn,d->dsi_pack.dsi_gi.vobu_vob_idn,
d->cur_pgc->cell_position[d->cur_cell].cell_nr,
d->cur_pgc->cell_position[d->cur_cell].vob_id_nr);
if(d->angle_seek) {
int i,skip=0;
for(i=0;i<9;i++) // check if all values zero:
if((skip=d->dsi_pack.sml_agli.data[i].address)!=0) break;
if(skip && skip!=0x7fffffff) {
// sml_agli table has valid data (at least one non-zero):
d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+
d->dsi_pack.sml_agli.data[d->dvd_angle-1].address;
d->angle_seek=0;
d->cur_pack--;
MP_VERBOSE(stream, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d->cur_pack);
} else {
// check if we're in the right cell, jump otherwise:
if( (d->dsi_pack.dsi_gi.vobu_c_idn==d->cur_pgc->cell_position[d->cur_cell].cell_nr) &&
(d->dsi_pack.dsi_gi.vobu_vob_idn==d->cur_pgc->cell_position[d->cur_cell].vob_id_nr) ){
d->angle_seek=0;
MP_VERBOSE(stream, "Angle-seek synced by cell/vob IDN search! \n");
} else {
// wrong angle, skip this vobu:
d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+
d->dsi_pack.dsi_gi.vobu_ea;
d->angle_seek=2; // DEBUG
}
}
}
}
++d->cur_pack;
goto read_next;
}
++d->cur_pack;
if(d->packs_left>=0) --d->packs_left;
if(d->angle_seek) {
if(d->angle_seek==2) MP_VERBOSE(stream, "!!! warning! reading packet while angle_seek !!!\n");
goto read_next; // searching for Navi packet
}
return d->cur_pack-1;
}
static int fill_buffer(stream_t *s, char *buf, int len)
{
int64_t pos;
if (len < 2048)
return -1;
pos = dvd_read_sector(s, s->priv, buf);
if (pos < 0)
return -1;
return 2048; // full sector
}
static void stream_dvd_close(stream_t *s) {
dvd_priv_t *d = s->priv;
ifoClose(d->vts_file);
ifoClose(d->vmg_file);
DVDCloseFile(d->title);
DVDClose(d->dvd);
if (d->dvd_speed)
dvd_set_speed(s,d->dvd_device_current, -1); /* -1 => restore default */
}
static int mp_get_titleset_length(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
{
int vts_ttn; ///< title number within video title set
int pgc_no; ///< program chain number
int msec; ///< time length in milliseconds
msec=0;
if(!vts_file || !tt_srpt)
return 0;
if(vts_file->vtsi_mat && vts_file->vts_pgcit)
{
vts_ttn = tt_srpt->title[title_no].vts_ttn - 1;
pgc_no = vts_file->vts_ptt_srpt->title[vts_ttn].ptt[0].pgcn - 1;
msec = mp_dvdtimetomsec(&vts_file->vts_pgcit->pgci_srp[pgc_no].pgc->playback_time);
}
return msec;
}
static int get_num_chapter(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
{
if(!vts_file || !tt_srpt)
return 0;
if(title_no < 0 || title_no >= tt_srpt->nr_of_srpts)
return 0;
// map global title to vts title
title_no = tt_srpt->title[title_no].vts_ttn - 1;
if(title_no < 0 || title_no >= vts_file->vts_ptt_srpt->nr_of_srpts)
return 0;
return vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts;
}
// p: in=chapter number, out=PTS
static int get_chapter_time(ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no, double *p)
{
unsigned int i, cell, last_cell;
unsigned int t=0;
ptt_info_t *ptt;
pgc_t *pgc;
title_no = tt_srpt->title[title_no].vts_ttn - 1;
if(vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts < 2)
return 0;
ptt = vts_file->vts_ptt_srpt->title[title_no].ptt;
int cur = 0;
for(i=0; i<vts_file->vts_ptt_srpt->title[title_no].nr_of_ptts; i++)
{
pgc = vts_file->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
cell = pgc->program_map[ptt[i].pgn-1]; //here the cell is 1-based
if(ptt[i].pgn<pgc->nr_of_programs)
last_cell = pgc->program_map[ptt[i].pgn];
else
last_cell = 0;
while (cell < last_cell) {
if(!(pgc->cell_playback[cell-1].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
pgc->cell_playback[cell-1].block_mode != BLOCK_MODE_FIRST_CELL)
) {
if (cur == *p) {
*p = t / 1000.0;
return 1;
}
t += mp_dvdtimetomsec(&pgc->cell_playback[cell-1].playback_time);
cur++;
}
cell++;
}
}
return 0;
}
static void list_chapters(stream_t *stream, ifo_handle_t *vts_file, tt_srpt_t *tt_srpt, int title_no)
{
MP_INFO(stream, "CHAPTERS: ");
for (int n = 0; ; n++) {
double p = n;
int r;
r = get_chapter_time(vts_file, tt_srpt, title_no, &p);
if (!r)
break;
int t = p * 1000;
MP_INFO(stream, "%02d:%02d:%02d.%03d,", t/3600000, (t/60000)%60, (t/1000)%60, t%1000);
}
MP_INFO(stream, "\n");
}
static double dvd_get_current_time(stream_t *stream, int cell)
{
int i, tm;
dvd_priv_t *d = stream->priv;
tm=0;
if(cell < 0) cell=d->cur_cell;
for(i=0; i<cell; i++) {
if(d->cur_pgc->cell_playback[i].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
d->cur_pgc->cell_playback[i].block_mode != BLOCK_MODE_FIRST_CELL
)
continue;
tm += d->cell_times_table[i];
}
tm += mp_dvdtimetomsec(&d->dsi_pack.dsi_gi.c_eltm);
return (double)tm/1000.0;
}
static void dvd_seek(stream_t *stream, dvd_priv_t *d, int pos)
{
d->packs_left=-1;
d->cur_pack=pos;
// check if we stay in current cell (speedup things, and avoid angle skip)
if(d->cur_pack>d->cell_last_pack ||
d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector) {
// ok, cell change, find the right cell!
cell_playback_t *cell;
for(d->cur_cell=0; d->cur_cell < d->cur_pgc->nr_of_cells; d->cur_cell++) {
cell = &(d->cur_pgc->cell_playback[d->cur_cell]);
if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
continue;
d->cell_last_pack=cell->last_sector;
if(d->cur_pack<cell->first_sector) {
d->cur_pack=cell->first_sector;
break;
}
if(d->cur_pack<=d->cell_last_pack) break; // ok, we find it! :)
}
}
MP_VERBOSE(stream, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n",
d->cur_pack,d->cur_cell,d->cur_pgc->cell_playback[ d->cur_cell ].first_sector,d->cell_last_pack);
// if we're in interleaved multi-angle cell, find the right angle chain!
// (read Navi block, and use the seamless angle jump table)
d->angle_seek=1;
}
static int do_seek(stream_t *s, int64_t newpos) {
stream_drop_buffers(s);
dvd_seek(s, s->priv,newpos/2048);
return 1;
}
static int dvd_seek_to_time(stream_t *stream, ifo_handle_t *vts_file, double sec)
{
unsigned int i, j, k, timeunit, ac_time, tmap_sector=0, cell_sector=0, vobu_sector=0;
int t=0;
double tm, duration;
int64_t pos = -1;
dvd_priv_t *d = stream->priv;
vts_tmapt_t *vts_tmapt = vts_file->vts_tmapt;
if(!vts_file->vts_tmapt || sec < 0)
return 0;
duration = (double) mp_get_titleset_length(d->vts_file, d->tt_srpt, d->cur_title) / 1000.0f;
if(sec > duration)
return 0;
i=d->cur_pgc_idx;
timeunit = vts_tmapt->tmap[i].tmu;
for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
ac_time = timeunit * (j + 1);
if(ac_time >= sec)
break;
tmap_sector = vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff;
}
//search enclosing cell
for(i=0; i<d->cur_pgc->nr_of_cells; i++) {
if(tmap_sector >= d->cur_pgc->cell_playback[i].first_sector && tmap_sector <= d->cur_pgc->cell_playback[i].last_sector) {
cell_sector = d->cur_pgc->cell_playback[i].first_sector;
break;
}
}
pos = ((int64_t)cell_sector)<<11;
do_seek(stream, pos);
do {
char buf[2048];
if (dvd_read_sector(stream, stream->priv, buf) < 0) // skip
break;
t = mp_dvdtimetomsec(&d->dsi_pack.dsi_gi.c_eltm);
} while(!t);
tm = dvd_get_current_time(stream, -1);
pos = ((int64_t)tmap_sector)<<11;
do_seek(stream, pos);
//now get current time in terms of the cell+cell time offset
memset(&d->dsi_pack.dsi_gi.c_eltm, 0, sizeof(dvd_time_t));
while(tm <= sec) {
char buf[2048];
if (dvd_read_sector(stream, stream->priv, buf) < 0) // skip
break;
pos += 2048;
tm = dvd_get_current_time(stream, -1);
};
tmap_sector = pos >> 11;
//search closest VOBU sector
k=(vts_file->vts_vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4; //entries in the vobu admap
for(i=1; i<k; i++) {
if(vts_file->vts_vobu_admap->vobu_start_sectors[i] > tmap_sector)
break;
}
vobu_sector = vts_file->vts_vobu_admap->vobu_start_sectors[i-1];
pos = ((int64_t)vobu_sector) << 11;
do_seek(stream, pos);
return 1;
}
static int control(stream_t *stream,int cmd,void* arg)
{
dvd_priv_t *d = stream->priv;
switch(cmd)
{
case STREAM_CTRL_GET_TIME_LENGTH:
{
*((double *)arg) = (double) mp_get_titleset_length(d->vts_file, d->tt_srpt, d->cur_title)/1000.0;
return 1;
}
case STREAM_CTRL_GET_NUM_TITLES:
{
*((unsigned int *)arg) = d->vmg_file->tt_srpt->nr_of_srpts;
return 1;
}
case STREAM_CTRL_GET_TITLE_LENGTH:
{
int t = *(double *)arg;
if (t < 0 || t >= d->vmg_file->tt_srpt->nr_of_srpts)
break;
if (d->tt_srpt->title[t].title_set_nr !=
d->tt_srpt->title[d->dvd_title].title_set_nr)
break;
*(double *)arg =
mp_get_titleset_length(d->vts_file, d->tt_srpt, t) / 1000.0;
return 1;
}
case STREAM_CTRL_GET_NUM_CHAPTERS:
{
int r;
r = get_num_chapter(d->vts_file, d->tt_srpt, d->cur_title);
if(! r) return STREAM_UNSUPPORTED;
*((unsigned int *)arg) = r;
return 1;
}
case STREAM_CTRL_GET_CHAPTER_TIME:
{
int r;
r = get_chapter_time(d->vts_file, d->tt_srpt, d->cur_title, (double *)arg);
if(! r) return STREAM_UNSUPPORTED;
return 1;
}
case STREAM_CTRL_GET_CURRENT_TITLE:
{
*((unsigned int *)arg) = d->cur_title;
return 1;
}
case STREAM_CTRL_GET_CURRENT_TIME:
{
double tm;
tm = dvd_get_current_time(stream, -1);
if(tm != -1) {
*((double *)arg) = tm;
return 1;
}
break;
}
case STREAM_CTRL_SEEK_TO_TIME:
{
if(dvd_seek_to_time(stream, d->vts_file, *((double*)arg)))
return 1;
break;
}
case STREAM_CTRL_GET_ASPECT_RATIO:
{
*((double *)arg) = !d->vts_file->vtsi_mat->vts_video_attr.display_aspect_ratio ? 4.0/3.0 : 16.0/9.0;
return 1;
}
case STREAM_CTRL_GET_NUM_ANGLES:
{
*((int *)arg) = d->vmg_file->tt_srpt->title[d->dvd_title].nr_of_angles;
return 1;
}
case STREAM_CTRL_GET_ANGLE:
{
*((int *)arg) = d->dvd_angle;
return 1;
}
case STREAM_CTRL_SET_ANGLE:
{
int ang = *((int *)arg);
if(ang>d->vmg_file->tt_srpt->title[d->dvd_title].nr_of_angles || ang<=0)
break;
d->dvd_angle = ang;
d->angle_seek = 1;
return 1;
}
case STREAM_CTRL_GET_LANG:
{
struct stream_lang_req *req = arg;
int lang = 0;
switch(req->type) {
case STREAM_AUDIO:
lang = dvd_lang_from_aid(stream, req->id);
break;
case STREAM_SUB:
lang = dvd_lang_from_sid(stream, req->id);
break;
}
if (!lang)
break;
snprintf(req->name, sizeof(req->name), "%c%c", lang >> 8, lang);
return STREAM_OK;
}
case STREAM_CTRL_GET_DVD_INFO:
{
struct stream_dvd_info_req *req = arg;
memset(req, 0, sizeof(*req));
req->num_subs = dvd_number_of_subs(stream);
memcpy(req->palette, d->cur_pgc->palette, sizeof(req->palette));
return STREAM_OK;
}
case STREAM_CTRL_GET_DISC_NAME:
{
char buffer[128];
if (DVDUDFVolumeInfo(d->dvd, buffer, sizeof(buffer), NULL, 0) < 0 &&
DVDISOVolumeInfo(d->dvd, buffer, sizeof(buffer), NULL, 0) < 0)
break;
if (!buffer[0])
break;
*(char**)arg = talloc_strdup(NULL, buffer);
return STREAM_OK;
}
case STREAM_CTRL_GET_SIZE:
*(int64_t *)arg =
(d->cur_pgc->cell_playback[d->last_cell-1].last_sector)*2048LL;
return STREAM_OK;
}
return STREAM_UNSUPPORTED;
}
static int open_s_internal(stream_t *stream)
{
int k;
dvd_priv_t *d = stream->priv;
struct dvd_opts *opts =
mp_get_config_group(stream, stream->global, &dvd_conf);
d->dvd_angle = opts->angle;
MP_VERBOSE(stream, "URL: %s\n", stream->url);
d->dvd_title = d->cfg_title + 1;
if(1){
//int ret,ret2;
int ttn,pgc_id,pgn;
dvd_reader_t *dvd;
dvd_file_t *title;
ifo_handle_t *vmg_file;
tt_srpt_t *tt_srpt;
ifo_handle_t *vts_file;
pgc_t *pgc;
/**
* Open the disc.
*/
if(d->cfg_device && d->cfg_device[0])
d->dvd_device_current = d->cfg_device;
else if(opts->device && opts->device[0])
d->dvd_device_current = talloc_strdup(stream, opts->device);
else
d->dvd_device_current = DEFAULT_DVD_DEVICE;
d->dvd_speed = opts->speed;
dvd_set_speed(stream,d->dvd_device_current, d->dvd_speed);
#if defined(__APPLE__) || defined(__DARWIN__)
/* Dynamic DVD drive selection on Darwin */
if(!strcmp(d->dvd_device_current, "/dev/rdiskN")) {
int i;
size_t len = strlen(d->dvd_device_current)+1;
char *temp_device = malloc(len);
for (i = 1; i < 10; i++) {
snprintf(temp_device, len, "/dev/rdisk%d", i);
dvd = DVDOpen(temp_device);
if(!dvd) {
MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",temp_device,
mp_strerror(errno));
} else {
#if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4)
dvd_file_t *dvdfile = DVDOpenFile(dvd,d->dvd_title,DVD_READ_INFO_FILE);
if(!dvdfile) {
MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",temp_device,
mp_strerror(errno));
DVDClose(dvd);
continue;
}
DVDCloseFile(dvdfile);
#endif
break;
}
}
free(temp_device);
if(!dvd) {
return STREAM_UNSUPPORTED;
}
} else
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
{
dvd = DVDOpen(d->dvd_device_current);
if(!dvd) {
MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",
d->dvd_device_current, mp_strerror(errno));
return STREAM_UNSUPPORTED;
}
}
MP_VERBOSE(stream, "Reading disc structure, please wait...\n");
/**
* Load the video manager to find out the information about the titles on
* this disc.
*/
vmg_file = ifoOpen(dvd, 0);
if(!vmg_file) {
MP_ERR(stream, "Can't open VMG info!\n");
DVDClose( dvd );
return STREAM_UNSUPPORTED;
}
tt_srpt = vmg_file->tt_srpt;
/**
* Make sure our title number is valid.
*/
MP_INFO(stream, "There are %d titles on this DVD.\n", tt_srpt->nr_of_srpts );
if(d->dvd_title < 1 || d->dvd_title > tt_srpt->nr_of_srpts) {
MP_ERR(stream, "Invalid DVD title number: %d\n", d->dvd_title);
ifoClose( vmg_file );
DVDClose( dvd );
return STREAM_UNSUPPORTED;
}
--(d->dvd_title); // remap 1.. -> 0..
/**
* Make sure the angle number is valid for this title.
*/
MP_INFO(stream, "There are %d angles in this DVD title.\n", tt_srpt->title[d->dvd_title].nr_of_angles);
if(d->dvd_angle<1 || d->dvd_angle>tt_srpt->title[d->dvd_title].nr_of_angles) {
MP_ERR(stream, "Invalid DVD angle number: %d\n", d->dvd_angle);
goto fail;
}
ttn = tt_srpt->title[d->dvd_title].vts_ttn - 1;
/**
* Load the VTS information for the title set our title is in.
*/
vts_file = ifoOpen( dvd, tt_srpt->title[d->dvd_title].title_set_nr );
if(!vts_file) {
MP_ERR(stream, "Cannot open the IFO file for DVD title %d.\n", tt_srpt->title[d->dvd_title].title_set_nr );
goto fail;
}
/**
* We've got enough info, time to open the title set data.
*/
title = DVDOpenFile(dvd, tt_srpt->title[d->dvd_title].title_set_nr, DVD_READ_TITLE_VOBS);
if(!title) {
MP_ERR(stream, "Cannot open title VOBS (VTS_%02d_1.VOB).\n", tt_srpt->title[d->dvd_title].title_set_nr);
ifoClose( vts_file );
goto fail;
}
MP_VERBOSE(stream, "DVD successfully opened.\n");
// store data
d->dvd=dvd;
d->title=title;
d->vmg_file=vmg_file;
d->tt_srpt=tt_srpt;
d->vts_file=vts_file;
d->cur_title = d->dvd_title;
pgc_id = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgcn; // local
pgn = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgn; // local
pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc : NULL;
/**
* Check number of audio channels and types
*/
{
d->nr_of_channels=0;
if(vts_file->vts_pgcit) {
int i;
for(i=0;i<8;i++)
if(pgc->audio_control[i] & 0x8000) {
audio_attr_t * audio = &vts_file->vtsi_mat->vts_audio_attr[i];
int language = 0;
char tmp[] = "unknown";
stream_language_t *audio_stream = &d->audio_streams[d->nr_of_channels];
if(audio->lang_type == 1) {
language=audio->lang_code;
tmp[0]=language>>8;
tmp[1]=language&0xff;
tmp[2]=0;
}
audio_stream->language=language;
audio_stream->id=pgc->audio_control[i] >> 8 & 7;
switch(audio->audio_format) {
case 0: // ac3
audio_stream->id+=FIRST_AC3_AID;
break;
case 6: // dts
audio_stream->id+=FIRST_DTS_AID;
break;
case 2: // mpeg layer 1/2/3
case 3: // mpeg2 ext
audio_stream->id+=FIRST_MPG_AID;
break;
case 4: // lpcm
audio_stream->id+=FIRST_PCM_AID;
break;
}
audio_stream->type=audio->audio_format;
// Pontscho: to my mind, tha channels:
// 1 - stereo
// 5 - 5.1
audio_stream->channels=audio->channels;
MP_INFO(stream, "audio stream: %d format: %s (%s) language: %s aid: %d.\n",
d->nr_of_channels,
dvd_audio_stream_types[ audio->audio_format ],
dvd_audio_stream_channels[ audio->channels ],
tmp,
audio_stream->id
);
d->nr_of_channels++;
}
}
MP_INFO(stream, "number of audio channels on disk: %d.\n",d->nr_of_channels );
}
/**
* Check number of subtitles and language
*/
{
int i;
d->nr_of_subtitles=0;
for(i=0;i<32;i++)
if(pgc->subp_control[i] & 0x80000000) {
subp_attr_t * subtitle = &vts_file->vtsi_mat->vts_subp_attr[i];
video_attr_t *video = &vts_file->vtsi_mat->vts_video_attr;
int language = 0;
char tmp[] = "unknown";
stream_language_t *sub_stream = &d->subtitles[d->nr_of_subtitles];
if(subtitle->type == 1) {
language=subtitle->lang_code;
tmp[0]=language>>8;
tmp[1]=language&0xff;
tmp[2]=0;
}
sub_stream->language=language;
sub_stream->id=d->nr_of_subtitles;
if(video->display_aspect_ratio == 0) /* 4:3 */
sub_stream->id = pgc->subp_control[i] >> 24 & 31;
else if(video->display_aspect_ratio == 3) /* 16:9 */
sub_stream->id = pgc->subp_control[i] >> 8 & 31;
MP_INFO(stream, "subtitle ( sid ): %d language: %s\n", sub_stream->id, tmp);
d->nr_of_subtitles++;
}
MP_INFO(stream, "number of subtitles on disk: %d\n",d->nr_of_subtitles);
}
/**
* Determine which program chain we want to watch. This is based on the
* chapter number.
*/
d->cur_pgc_idx = pgc_id-1;
d->cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
d->cur_cell = d->cur_pgc->program_map[pgn-1] - 1; // start playback here
d->packs_left=-1; // for Navi stuff
d->angle_seek=0;
d->last_cell=d->cur_pgc->nr_of_cells;
if(d->cur_pgc->cell_playback[d->cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK )
d->cur_cell+=d->dvd_angle-1;
d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
MP_VERBOSE(stream, "DVD start cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack);
//assign cell_times_table
d->cell_times_table = malloc(sizeof(unsigned int) * d->cur_pgc->nr_of_cells);
if(d->cell_times_table == NULL)
return STREAM_UNSUPPORTED;
for(k=0; k<d->cur_pgc->nr_of_cells; k++)
d->cell_times_table[k] = mp_dvdtimetomsec(&d->cur_pgc->cell_playback[k].playback_time);
list_chapters(stream, vts_file,tt_srpt,d->dvd_title);
// ... (unimplemented)
// return NULL;
stream->demuxer = "+disc";
stream->lavf_type = "mpeg";
stream->sector_size = 2048;
stream->fill_buffer = fill_buffer;
stream->control = control;
stream->close = stream_dvd_close;
MP_VERBOSE(stream, "DVD start=%d end=%d \n",d->cur_pack,d->cur_pgc->cell_playback[d->last_cell-1].last_sector);
stream->priv = (void*)d;
return STREAM_OK;
fail:
ifoClose(vmg_file);
DVDClose(dvd);
return STREAM_UNSUPPORTED;
}
MP_ERR(stream, "mpv was compiled without DVD support, exiting.\n");
return STREAM_UNSUPPORTED;
}
static int open_s(stream_t *stream)
{
dvd_priv_t *d = talloc_zero(stream, dvd_priv_t);
stream->priv = d;
bstr title, bdevice;
bstr_split_tok(bstr0(stream->path), "/", &title, &bdevice);
if (title.len) {
bstr rest;
d->cfg_title = bstrtoll(title, &rest, 10);
if (rest.len) {
MP_ERR(stream, "number expected: '%.*s'\n", BSTR_P(rest));
return STREAM_ERROR;
}
}
d->cfg_device = bstrto0(d, bdevice);
return open_s_internal(stream);
}
static int ifo_stream_open(stream_t *stream)
{
dvd_priv_t *priv = talloc_zero(stream, dvd_priv_t);
stream->priv = priv;
if (!stream->access_references)
goto unsupported;
char *path = mp_file_get_path(priv, bstr0(stream->url));
if (!path)
goto unsupported;
if (!dvd_probe(path, ".ifo", "DVDVIDEO-VTS"))
goto unsupported;
char *base = mp_basename(path);
// Only accept individual titles - use dvdnav for video_ts.ifo
if (strncasecmp(base, "vts_", 4))
goto unsupported;
if (sscanf(base + 3, "_%02d_", &priv->cfg_title) != 1)
goto unsupported;
priv->cfg_device = bstrto0(priv, mp_dirname(path));
MP_INFO(stream, ".IFO detected. Redirecting to dvdread://\n");
return open_s_internal(stream);
unsupported:
talloc_free(priv);
stream->priv = NULL;
return STREAM_UNSUPPORTED;
}
const stream_info_t stream_info_dvd = {
.name = "dvd",
.open = open_s,
.protocols = (const char*const[]){ "dvdread", NULL },
};
const stream_info_t stream_info_ifo = {
.name = "ifo",
.open = ifo_stream_open,
.protocols = (const char*const[]){ "file", "", NULL },
};

View File

@ -1,164 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <libavutil/intreadwrite.h>
#include "config.h"
#include <dvdread/ifo_types.h>
#ifdef __FreeBSD__
#include <sys/cdrio.h>
#endif
#ifdef __linux__
#include <linux/cdrom.h>
#include <scsi/sg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#endif
#include "osdep/io.h"
#include "common/msg.h"
#include "misc/bstr.h"
#include "stream_dvd_common.h"
const char * const dvd_audio_stream_types[8] = { "ac3","unknown","mpeg1","mpeg2ext","lpcm","unknown","dts" };
const char * const dvd_audio_stream_channels[6] = { "mono", "stereo", "unknown", "unknown", "5.1/6.1", "5.1" };
void dvd_set_speed(stream_t *stream, char *device, unsigned speed)
{
#if defined(__linux__) && defined(SG_IO) && defined(GPCMD_SET_STREAMING)
int fd;
unsigned char buffer[28];
unsigned char cmd[12];
struct sg_io_hdr sghdr;
struct stat st;
memset(&st, 0, sizeof(st));
if (stat(device, &st) == -1) return;
if (!S_ISBLK(st.st_mode)) return; /* not a block device */
switch (speed) {
case 0: /* don't touch speed setting */
return;
case -1: /* restore default value */
MP_INFO(stream, "Restoring DVD speed... ");
break;
default: /* limit to <speed> KB/s */
// speed < 100 is multiple of DVD single speed (1350KB/s)
if (speed < 100)
speed *= 1350;
MP_INFO(stream, "Limiting DVD speed to %dKB/s... ", speed);
break;
}
memset(&sghdr, 0, sizeof(sghdr));
sghdr.interface_id = 'S';
sghdr.timeout = 5000;
sghdr.dxfer_direction = SG_DXFER_TO_DEV;
sghdr.dxfer_len = sizeof(buffer);
sghdr.dxferp = buffer;
sghdr.cmd_len = sizeof(cmd);
sghdr.cmdp = cmd;
memset(cmd, 0, sizeof(cmd));
cmd[0] = GPCMD_SET_STREAMING;
cmd[10] = sizeof(buffer);
memset(buffer, 0, sizeof(buffer));
/* first sector 0, last sector 0xffffffff */
AV_WB32(buffer + 8, 0xffffffff);
if (speed == -1)
buffer[0] = 4; /* restore default */
else {
/* <speed> kilobyte */
AV_WB32(buffer + 12, speed);
AV_WB32(buffer + 20, speed);
}
/* 1 second */
AV_WB16(buffer + 18, 1000);
AV_WB16(buffer + 26, 1000);
fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd == -1) {
MP_INFO(stream, "Couldn't open DVD device for writing, changing DVD speed needs write access.\n");
return;
}
if (ioctl(fd, SG_IO, &sghdr) < 0)
MP_INFO(stream, "failed\n");
else
MP_INFO(stream, "successful\n");
close(fd);
#endif
}
/**
\brief Converts DVD time structure to milliseconds.
\param *dev the DVD time structure to convert
\return returns the time in milliseconds
*/
int mp_dvdtimetomsec(dvd_time_t *dt)
{
int framerates[4] = {0, 2500, 0, 2997};
int framerate = framerates[(dt->frame_u & 0xc0) >> 6];
int msec = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000;
msec += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000;
msec += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000;
if(framerate > 0)
msec += (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 100000 / framerate;
return msec;
}
// Check if this is likely to be an .ifo or similar file.
int dvd_probe(const char *path, const char *ext, const char *sig)
{
if (!bstr_case_endswith(bstr0(path), bstr0(ext)))
return false;
FILE *temp = fopen(path, "rb");
if (!temp)
return false;
bool r = false;
char data[50];
assert(strlen(sig) <= sizeof(data));
if (fread(data, 50, 1, temp) == 1) {
if (memcmp(data, sig, strlen(sig)) == 0)
r = true;
}
fclose(temp);
return r;
}

View File

@ -1,38 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_STREAM_DVD_COMMON_H
#define MPLAYER_STREAM_DVD_COMMON_H
#include <inttypes.h>
#include <stdbool.h>
#include "stream.h"
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
extern const char * const dvd_audio_stream_channels[6];
extern const char * const dvd_audio_stream_types[8];
void dvd_set_speed(stream_t *stream, char *device, unsigned speed);
int mp_dvdtimetomsec(dvd_time_t *dt);
int dvd_probe(const char *path, const char *ext, const char *sig);
#endif /* MPLAYER_STREAM_DVD_COMMON_H */

View File

@ -1,611 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libavutil/common.h>
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <dvdnav/dvdnav.h>
#include "osdep/io.h"
#include "options/options.h"
#include "common/msg.h"
#include "input/input.h"
#include "options/m_config.h"
#include "options/path.h"
#include "osdep/timer.h"
#include "stream.h"
#include "demux/demux.h"
#include "video/out/vo.h"
#include "stream_dvd_common.h"
#define TITLE_MENU -1
#define TITLE_LONGEST -2
struct priv {
dvdnav_t *dvdnav; // handle to libdvdnav stuff
char *filename; // path
unsigned int duration; // in milliseconds
int mousex, mousey;
int title;
uint32_t spu_clut[16];
bool spu_clut_valid;
bool had_initial_vts;
int dvd_speed;
int track;
char *device;
struct dvd_opts *opts;
};
#define DNE(e) [e] = # e
static const char *const mp_dvdnav_events[] = {
DNE(DVDNAV_BLOCK_OK),
DNE(DVDNAV_NOP),
DNE(DVDNAV_STILL_FRAME),
DNE(DVDNAV_SPU_STREAM_CHANGE),
DNE(DVDNAV_AUDIO_STREAM_CHANGE),
DNE(DVDNAV_VTS_CHANGE),
DNE(DVDNAV_CELL_CHANGE),
DNE(DVDNAV_NAV_PACKET),
DNE(DVDNAV_STOP),
DNE(DVDNAV_HIGHLIGHT),
DNE(DVDNAV_SPU_CLUT_CHANGE),
DNE(DVDNAV_HOP_CHANNEL),
DNE(DVDNAV_WAIT),
};
#define LOOKUP_NAME(array, i) \
(((i) >= 0 && (i) < MP_ARRAY_SIZE(array)) ? array[(i)] : "?")
/**
* \brief mp_dvdnav_lang_from_aid() returns the language corresponding to audio id 'aid'
* \param stream: - stream pointer
* \param sid: physical subtitle id
* \return 0 on error, otherwise language id
*/
static int mp_dvdnav_lang_from_aid(stream_t *stream, int aid)
{
uint8_t lg;
uint16_t lang;
struct priv *priv = stream->priv;
if (aid < 0)
return 0;
lg = dvdnav_get_audio_logical_stream(priv->dvdnav, aid & 0x7);
if (lg == 0xff)
return 0;
lang = dvdnav_audio_stream_to_lang(priv->dvdnav, lg);
if (lang == 0xffff)
return 0;
return lang;
}
/**
* \brief mp_dvdnav_lang_from_sid() returns the language corresponding to subtitle id 'sid'
* \param stream: - stream pointer
* \param sid: physical subtitle id
* \return 0 on error, otherwise language id
*/
static int mp_dvdnav_lang_from_sid(stream_t *stream, int sid)
{
uint8_t k;
uint16_t lang;
struct priv *priv = stream->priv;
if (sid < 0)
return 0;
for (k = 0; k < 32; k++)
if (dvdnav_get_spu_logical_stream(priv->dvdnav, k) == sid)
break;
if (k == 32)
return 0;
lang = dvdnav_spu_stream_to_lang(priv->dvdnav, k);
if (lang == 0xffff)
return 0;
return lang;
}
/**
* \brief mp_dvdnav_number_of_subs() returns the count of available subtitles
* \param stream: - stream pointer
* \return 0 on error, something meaningful otherwise
*/
static int mp_dvdnav_number_of_subs(stream_t *stream)
{
struct priv *priv = stream->priv;
uint8_t lg, k, n = 0;
for (k = 0; k < 32; k++) {
lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
if (lg == 0xff)
continue;
if (lg >= n)
n = lg + 1;
}
return n;
}
static int fill_buffer(stream_t *s, char *buf, int max_len)
{
struct priv *priv = s->priv;
dvdnav_t *dvdnav = priv->dvdnav;
if (max_len < 2048)
return -1;
while (1) {
int len = -1;
int event = DVDNAV_NOP;
if (dvdnav_get_next_block(dvdnav, buf, &event, &len) != DVDNAV_STATUS_OK)
{
MP_ERR(s, "Error getting next block from DVD %d (%s)\n",
event, dvdnav_err_to_string(dvdnav));
return 0;
}
if (event != DVDNAV_BLOCK_OK) {
const char *name = LOOKUP_NAME(mp_dvdnav_events, event);
MP_VERBOSE(s, "DVDNAV: event %s (%d).\n", name, event);
}
switch (event) {
case DVDNAV_BLOCK_OK:
return len;
case DVDNAV_STOP:
return 0;
case DVDNAV_NAV_PACKET: {
pci_t *pnavpci = dvdnav_get_current_nav_pci(dvdnav);
uint32_t start_pts = pnavpci->pci_gi.vobu_s_ptm;
MP_TRACE(s, "start pts = %"PRIu32"\n", start_pts);
break;
}
case DVDNAV_STILL_FRAME:
dvdnav_still_skip(dvdnav);
return 0;
case DVDNAV_WAIT:
dvdnav_wait_skip(dvdnav);
return 0;
case DVDNAV_HIGHLIGHT:
break;
case DVDNAV_VTS_CHANGE: {
int tit = 0, part = 0;
dvdnav_vts_change_event_t *vts_event =
(dvdnav_vts_change_event_t *)s->buffer;
MP_INFO(s, "DVDNAV, switched to title: %d\n",
vts_event->new_vtsN);
if (!priv->had_initial_vts) {
// dvdnav sends an initial VTS change before any data; don't
// cause a blocking wait for the player, because the player in
// turn can't initialize the demuxer without data.
priv->had_initial_vts = true;
break;
}
if (dvdnav_current_title_info(dvdnav, &tit, &part) == DVDNAV_STATUS_OK)
{
MP_VERBOSE(s, "DVDNAV, NEW TITLE %d\n", tit);
if (priv->title > 0 && tit != priv->title)
MP_WARN(s, "Requested title not found\n");
}
break;
}
case DVDNAV_CELL_CHANGE: {
dvdnav_cell_change_event_t *ev = (dvdnav_cell_change_event_t *)buf;
if (ev->pgc_length)
priv->duration = ev->pgc_length / 90;
break;
}
case DVDNAV_SPU_CLUT_CHANGE: {
memcpy(priv->spu_clut, buf, 16 * sizeof(uint32_t));
priv->spu_clut_valid = true;
break;
}
}
}
return 0;
}
static int control(stream_t *stream, int cmd, void *arg)
{
struct priv *priv = stream->priv;
dvdnav_t *dvdnav = priv->dvdnav;
int tit, part;
switch (cmd) {
case STREAM_CTRL_GET_NUM_CHAPTERS: {
if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
break;
if (dvdnav_get_number_of_parts(dvdnav, tit, &part) != DVDNAV_STATUS_OK)
break;
if (!part)
break;
*(unsigned int *)arg = part;
return 1;
}
case STREAM_CTRL_GET_CHAPTER_TIME: {
double *ch = arg;
int chapter = *ch;
if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
break;
uint64_t *parts = NULL, duration = 0;
int n = dvdnav_describe_title_chapters(dvdnav, tit, &parts, &duration);
if (!parts)
break;
if (chapter < 0 || chapter + 1 > n)
break;
*ch = chapter > 0 ? parts[chapter - 1] / 90000.0 : 0;
free(parts);
return 1;
}
case STREAM_CTRL_GET_TIME_LENGTH: {
if (priv->duration) {
*(double *)arg = (double)priv->duration / 1000.0;
return 1;
}
break;
}
case STREAM_CTRL_GET_ASPECT_RATIO: {
uint8_t ar = dvdnav_get_video_aspect(dvdnav);
*(double *)arg = !ar ? 4.0 / 3.0 : 16.0 / 9.0;
return 1;
}
case STREAM_CTRL_GET_CURRENT_TIME: {
double tm;
tm = dvdnav_get_current_time(dvdnav) / 90000.0f;
if (tm != -1) {
*(double *)arg = tm;
return 1;
}
break;
}
case STREAM_CTRL_GET_NUM_TITLES: {
int32_t num_titles = 0;
if (dvdnav_get_number_of_titles(dvdnav, &num_titles) != DVDNAV_STATUS_OK)
break;
*((unsigned int*)arg)= num_titles;
return STREAM_OK;
}
case STREAM_CTRL_GET_TITLE_LENGTH: {
int t = *(double *)arg;
int32_t num_titles = 0;
if (dvdnav_get_number_of_titles(dvdnav, &num_titles) != DVDNAV_STATUS_OK)
break;
if (t < 0 || t >= num_titles)
break;
uint64_t duration = 0;
uint64_t *parts = NULL;
dvdnav_describe_title_chapters(dvdnav, t + 1, &parts, &duration);
if (!parts)
break;
free(parts);
*(double *)arg = duration / 90000.0;
return STREAM_OK;
}
case STREAM_CTRL_GET_CURRENT_TITLE: {
if (dvdnav_current_title_info(dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
break;
*((unsigned int *) arg) = tit - 1;
return STREAM_OK;
}
case STREAM_CTRL_SET_CURRENT_TITLE: {
int title = *((unsigned int *) arg);
if (dvdnav_title_play(priv->dvdnav, title + 1) != DVDNAV_STATUS_OK)
break;
stream_drop_buffers(stream);
return STREAM_OK;
}
case STREAM_CTRL_SEEK_TO_TIME: {
double *args = arg;
double d = args[0]; // absolute target timestamp
int flags = args[1]; // from SEEK_* flags (demux.h)
if (flags & SEEK_HR)
d -= 10; // fudge offset; it's a hack, because fuck libdvd*
int64_t tm = (int64_t)(d * 90000);
if (tm < 0)
tm = 0;
if (priv->duration && tm >= (priv->duration * 90))
tm = priv->duration * 90 - 1;
uint32_t pos, len;
if (dvdnav_get_position(dvdnav, &pos, &len) != DVDNAV_STATUS_OK)
break;
MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm);
if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK)
break;
stream_drop_buffers(stream);
d = dvdnav_get_current_time(dvdnav) / 90000.0f;
MP_VERBOSE(stream, "landed at: %f\n", d);
if (dvdnav_get_position(dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
MP_VERBOSE(stream, "block: %lu\n", (unsigned long)pos);
return STREAM_OK;
}
case STREAM_CTRL_GET_NUM_ANGLES: {
uint32_t curr, angles;
if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
break;
*(int *)arg = angles;
return 1;
}
case STREAM_CTRL_GET_ANGLE: {
uint32_t curr, angles;
if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
break;
*(int *)arg = curr;
return 1;
}
case STREAM_CTRL_SET_ANGLE: {
uint32_t curr, angles;
int new_angle = *(int *)arg;
if (dvdnav_get_angle_info(dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
break;
if (new_angle > angles || new_angle < 1)
break;
if (dvdnav_angle_change(dvdnav, new_angle) != DVDNAV_STATUS_OK)
return 1;
}
case STREAM_CTRL_GET_LANG: {
struct stream_lang_req *req = arg;
int lang = 0;
switch (req->type) {
case STREAM_AUDIO:
lang = mp_dvdnav_lang_from_aid(stream, req->id);
break;
case STREAM_SUB:
lang = mp_dvdnav_lang_from_sid(stream, req->id);
break;
}
if (!lang)
break;
snprintf(req->name, sizeof(req->name), "%c%c", lang >> 8, lang);
return STREAM_OK;
}
case STREAM_CTRL_GET_DVD_INFO: {
struct stream_dvd_info_req *req = arg;
memset(req, 0, sizeof(*req));
req->num_subs = mp_dvdnav_number_of_subs(stream);
assert(sizeof(uint32_t) == sizeof(unsigned int));
memcpy(req->palette, priv->spu_clut, sizeof(req->palette));
return STREAM_OK;
}
case STREAM_CTRL_GET_DISC_NAME: {
const char *volume = NULL;
if (dvdnav_get_title_string(dvdnav, &volume) != DVDNAV_STATUS_OK)
break;
if (!volume || !volume[0])
break;
*(char**)arg = talloc_strdup(NULL, volume);
return STREAM_OK;
}
}
return STREAM_UNSUPPORTED;
}
static void stream_dvdnav_close(stream_t *s)
{
struct priv *priv = s->priv;
dvdnav_close(priv->dvdnav);
priv->dvdnav = NULL;
if (priv->dvd_speed)
dvd_set_speed(s, priv->filename, -1);
if (priv->filename)
free(priv->filename);
}
static struct priv *new_dvdnav_stream(stream_t *stream, char *filename)
{
struct priv *priv = stream->priv;
const char *title_str;
if (!filename)
return NULL;
if (!(priv->filename = strdup(filename)))
return NULL;
priv->dvd_speed = priv->opts->speed;
dvd_set_speed(stream, priv->filename, priv->dvd_speed);
if (dvdnav_open(&(priv->dvdnav), priv->filename) != DVDNAV_STATUS_OK) {
free(priv->filename);
priv->filename = NULL;
return NULL;
}
if (!priv->dvdnav)
return NULL;
dvdnav_set_readahead_flag(priv->dvdnav, 1);
if (dvdnav_set_PGC_positioning_flag(priv->dvdnav, 1) != DVDNAV_STATUS_OK)
MP_ERR(stream, "stream_dvdnav, failed to set PGC positioning\n");
/* report the title?! */
dvdnav_get_title_string(priv->dvdnav, &title_str);
return priv;
}
static int open_s_internal(stream_t *stream)
{
struct priv *priv, *p;
priv = p = stream->priv;
char *filename;
p->opts = mp_get_config_group(stream, stream->global, &dvd_conf);
if (p->device && p->device[0])
filename = p->device;
else if (p->opts->device && p->opts->device[0])
filename = p->opts->device;
else
filename = DEFAULT_DVD_DEVICE;
if (!new_dvdnav_stream(stream, filename)) {
MP_ERR(stream, "Couldn't open DVD device: %s\n",
filename);
return STREAM_UNSUPPORTED;
}
if (p->track == TITLE_LONGEST) { // longest
dvdnav_t *dvdnav = priv->dvdnav;
uint64_t best_length = 0;
int best_title = -1;
int32_t num_titles;
if (dvdnav_get_number_of_titles(dvdnav, &num_titles) == DVDNAV_STATUS_OK) {
MP_VERBOSE(stream, "List of available titles:\n");
for (int n = 1; n <= num_titles; n++) {
uint64_t *parts = NULL, duration = 0;
dvdnav_describe_title_chapters(dvdnav, n, &parts, &duration);
if (parts) {
if (duration > best_length) {
best_length = duration;
best_title = n;
}
if (duration > 90000) { // arbitrarily ignore <1s titles
char *time = mp_format_time(duration / 90000, false);
MP_VERBOSE(stream, "title: %3d duration: %s\n",
n - 1, time);
talloc_free(time);
}
free(parts);
}
}
}
p->track = best_title - 1;
MP_INFO(stream, "Selecting title %d.\n", p->track);
}
if (p->track >= 0) {
priv->title = p->track;
if (dvdnav_title_play(priv->dvdnav, p->track + 1) != DVDNAV_STATUS_OK) {
MP_FATAL(stream, "dvdnav_stream, couldn't select title %d, error '%s'\n",
p->track, dvdnav_err_to_string(priv->dvdnav));
return STREAM_UNSUPPORTED;
}
} else {
MP_FATAL(stream, "DVD menu support has been removed.\n");
return STREAM_ERROR;
}
if (p->opts->angle > 1)
dvdnav_angle_change(priv->dvdnav, p->opts->angle);
stream->sector_size = 2048;
stream->fill_buffer = fill_buffer;
stream->control = control;
stream->close = stream_dvdnav_close;
stream->demuxer = "+disc";
stream->lavf_type = "mpeg";
return STREAM_OK;
}
static int open_s(stream_t *stream)
{
struct priv *priv = talloc_zero(stream, struct priv);
stream->priv = priv;
bstr title, bdevice;
bstr_split_tok(bstr0(stream->path), "/", &title, &bdevice);
priv->track = TITLE_LONGEST;
if (bstr_equals0(title, "longest") || bstr_equals0(title, "first")) {
priv->track = TITLE_LONGEST;
} else if (bstr_equals0(title, "menu")) {
priv->track = TITLE_MENU;
} else if (title.len) {
bstr rest;
priv->track = bstrtoll(title, &rest, 10);
if (rest.len) {
MP_ERR(stream, "number expected: '%.*s'\n", BSTR_P(rest));
return STREAM_ERROR;
}
}
priv->device = bstrto0(priv, bdevice);
return open_s_internal(stream);
}
const stream_info_t stream_info_dvdnav = {
.name = "dvdnav",
.open = open_s,
.protocols = (const char*const[]){ "dvd", "dvdnav", NULL },
};
static bool check_ifo(const char *path)
{
if (strcasecmp(mp_basename(path), "video_ts.ifo"))
return false;
return dvd_probe(path, ".ifo", "DVDVIDEO-VMG");
}
static int ifo_dvdnav_stream_open(stream_t *stream)
{
struct priv *priv = talloc_zero(stream, struct priv);
stream->priv = priv;
if (!stream->access_references)
goto unsupported;
priv->track = TITLE_LONGEST;
char *path = mp_file_get_path(priv, bstr0(stream->url));
if (!path)
goto unsupported;
// We allow the path to point to a directory containing VIDEO_TS/, a
// directory containing VIDEO_TS.IFO, or that file itself.
if (!check_ifo(path)) {
// On UNIX, just assume the filename is always uppercase.
char *npath = mp_path_join(priv, path, "VIDEO_TS.IFO");
if (!check_ifo(npath)) {
npath = mp_path_join(priv, path, "VIDEO_TS/VIDEO_TS.IFO");
if (!check_ifo(npath))
goto unsupported;
}
path = npath;
}
priv->device = bstrto0(priv, mp_dirname(path));
MP_INFO(stream, ".IFO detected. Redirecting to dvd://\n");
return open_s_internal(stream);
unsupported:
talloc_free(priv);
stream->priv = NULL;
return STREAM_UNSUPPORTED;
}
const stream_info_t stream_info_ifo_dvdnav = {
.name = "ifo_dvdnav",
.open = ifo_dvdnav_stream_open,
.protocols = (const char*const[]){ "file", "", NULL },
};

View File

@ -1,147 +0,0 @@
// Major parts based on:
/*****************************************************************************
* access.c: uncompressed RAR access
*****************************************************************************
* Copyright (C) 2008-2010 Laurent Aimar
* $Id: dcd973529e0029abe326d31f8d58cd13bbcc276c $
*
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "osdep/io.h"
#include "common/msg.h"
#include "stream.h"
#include "options/m_option.h"
#include "rar.h"
/*
This works as follows:
- stream_open() with file01.rar
- is opened as normal file (stream_file.c or others) first
- demux_rar.c detects it
- if multi-part, opens file02.rar, file03.rar, etc. as actual streams
- it returns a playlist with entries like this to the player:
rar://bla01.rar|subfile.mkv
(one such entry for each file contained in the rar)
- stream_open() with the playlist entry, e.g. rar://bla01.rar|subfile.mkv
- leads to rar_entry_open()
- opens bla01.rar etc. again as actual streams
- read accesses go into subfile.mkv contained in the rar file(s)
*/
static int rar_entry_fill_buffer(stream_t *s, char *buffer, int max_len)
{
rar_file_t *rar_file = s->priv;
return RarRead(rar_file, buffer, max_len);
}
static int rar_entry_seek(stream_t *s, int64_t newpos)
{
rar_file_t *rar_file = s->priv;
return RarSeek(rar_file, newpos);
}
static void rar_entry_close(stream_t *s)
{
rar_file_t *rar_file = s->priv;
RarFileDelete(rar_file);
}
static int rar_entry_control(stream_t *s, int cmd, void *arg)
{
rar_file_t *rar_file = s->priv;
switch (cmd) {
case STREAM_CTRL_GET_BASE_FILENAME:
*(char **)arg = talloc_strdup(NULL, rar_file->s->url);
return STREAM_OK;
case STREAM_CTRL_GET_SIZE:
*(int64_t *)arg = rar_file->size;
return STREAM_OK;
}
return STREAM_UNSUPPORTED;
}
static int rar_entry_open(stream_t *stream)
{
if (!strchr(stream->path, '|'))
return STREAM_ERROR;
char *base = talloc_strdup(stream, stream->path);
char *name = strchr(base, '|');
*name++ = '\0';
mp_url_unescape_inplace(base);
struct stream *rar = stream_create(base, STREAM_READ | STREAM_SAFE_ONLY,
stream->cancel, stream->global);
if (!rar)
return STREAM_ERROR;
int count;
rar_file_t **files;
if (RarProbe(rar) || RarParse(rar, &count, &files)) {
free_stream(rar);
return STREAM_ERROR;
}
rar_file_t *file = NULL;
for (int i = 0; i < count; i++) {
if (!file && strcmp(files[i]->name, name) == 0)
file = files[i];
else
RarFileDelete(files[i]);
}
talloc_free(files);
if (!file) {
free_stream(rar);
return STREAM_ERROR;
}
rar_file_chunk_t dummy = {
.mrl = base,
};
file->current_chunk = &dummy;
file->s = rar; // transfer ownership
file->cancel = stream->cancel;
file->global = stream->global;
RarSeek(file, 0);
stream->priv = file;
stream->fill_buffer = rar_entry_fill_buffer;
stream->seek = rar_entry_seek;
stream->seekable = true;
stream->close = rar_entry_close;
stream->control = rar_entry_control;
return STREAM_OK;
}
const stream_info_t stream_info_rar = {
.name = "rar",
.open = rar_entry_open,
.protocols = (const char*const[]){ "rar", NULL },
};

View File

@ -1,150 +0,0 @@
/*
* Original author: M. Tourne
*
* This file is part of mpv.
*
* mpv 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 3 of the License, or
* (at your option) any later version.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <libsmbclient.h>
#include <unistd.h>
#include "common/msg.h"
#include "stream.h"
#include "options/m_option.h"
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
struct priv {
int fd;
};
static void smb_auth_fn(const char *server, const char *share,
char *workgroup, int wgmaxlen, char *username, int unmaxlen,
char *password, int pwmaxlen)
{
strncpy(workgroup, "LAN", wgmaxlen - 1);
}
static int control(stream_t *s, int cmd, void *arg) {
struct priv *p = s->priv;
switch(cmd) {
case STREAM_CTRL_GET_SIZE: {
off_t size = smbc_lseek(p->fd,0,SEEK_END);
smbc_lseek(p->fd,s->pos,SEEK_SET);
if(size != (off_t)-1) {
*(int64_t *)arg = size;
return 1;
}
}
break;
}
return STREAM_UNSUPPORTED;
}
static int seek(stream_t *s,int64_t newpos) {
struct priv *p = s->priv;
if(smbc_lseek(p->fd,newpos,SEEK_SET)<0) {
return 0;
}
return 1;
}
static int fill_buffer(stream_t *s, char* buffer, int max_len){
struct priv *p = s->priv;
int r = smbc_read(p->fd,buffer,max_len);
return (r <= 0) ? -1 : r;
}
static int write_buffer(stream_t *s, char* buffer, int len) {
struct priv *p = s->priv;
int r;
int wr = 0;
while (wr < len) {
r = smbc_write(p->fd,buffer,len);
if (r <= 0)
return -1;
wr += r;
buffer += r;
}
return len;
}
static void close_f(stream_t *s){
struct priv *p = s->priv;
smbc_close(p->fd);
}
static int open_f (stream_t *stream)
{
char *filename;
int64_t len;
int fd, err;
struct priv *priv = talloc_zero(stream, struct priv);
stream->priv = priv;
filename = stream->url;
bool write = stream->mode == STREAM_WRITE;
mode_t m = write ? O_RDWR|O_CREAT|O_TRUNC : O_RDONLY;
if(!filename) {
MP_ERR(stream, "[smb] Bad url\n");
return STREAM_ERROR;
}
err = smbc_init(smb_auth_fn, 1);
if (err < 0) {
MP_ERR(stream, "Cannot init the libsmbclient library: %d\n",err);
return STREAM_ERROR;
}
fd = smbc_open(filename, m,0644);
if (fd < 0) {
MP_ERR(stream, "Could not open from LAN: '%s'\n", filename);
return STREAM_ERROR;
}
len = 0;
if(!write) {
len = smbc_lseek(fd,0,SEEK_END);
smbc_lseek (fd, 0, SEEK_SET);
}
if(len > 0 || write) {
stream->seekable = true;
stream->seek = seek;
}
priv->fd = fd;
stream->fill_buffer = fill_buffer;
stream->write_buffer = write_buffer;
stream->close = close_f;
stream->control = control;
stream->read_chunk = 128 * 1024;
stream->streaming = true;
return STREAM_OK;
}
const stream_info_t stream_info_smb = {
.name = "smb",
.open = open_f,
.protocols = (const char*const[]){"smb", NULL},
.can_write = true, //who's gonna do that?
};

View File

@ -1,52 +0,0 @@
/*
* stream layer for TV Input, based on previous work from Albeu
*
* Copyright (C) 2006 Benjamin Zores
* Original author: Albeu
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "stream.h"
#include "options/m_option.h"
#include "tv.h"
#include <stdio.h>
static void
tv_stream_close (stream_t *stream)
{
}
static int
tv_stream_open (stream_t *stream)
{
stream->close=tv_stream_close;
stream->demuxer = "tv";
return STREAM_OK;
}
const stream_info_t stream_info_tv = {
.name = "tv",
.open = tv_stream_open,
.protocols = (const char*const[]){ "tv", NULL },
};

View File

@ -1,986 +0,0 @@
/*
* TV Interface for MPlayer
*
* API idea based on libvo2
*
* Copyright (C) 2001 Alex Beregszaszi
*
* Feb 19, 2002: Significant rewrites by Charles R. Henrich (henrich@msu.edu)
* to add support for audio, and bktr *BSD support.
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <assert.h>
#include <libavutil/avstring.h>
#include "config.h"
#include "common/msg.h"
#include "misc/ctype.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "options/options.h"
#include "stream.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "tv.h"
#include "frequencies.h"
/* enumerating drivers (like in stream.c) */
extern const tvi_info_t tvi_info_dummy;
extern const tvi_info_t tvi_info_v4l2;
/** List of drivers in autodetection order */
static const tvi_info_t *const tvi_driver_list[]={
#if HAVE_TV_V4L2
&tvi_info_v4l2,
#endif
&tvi_info_dummy,
NULL
};
#define OPT_BASE_STRUCT tv_param_t
const struct m_sub_options tv_params_conf = {
.opts = (const m_option_t[]) {
OPT_FLAG("immediatemode", immediate, 0),
OPT_FLAG("audio", audio, 0),
OPT_INT("audiorate", audiorate, 0),
OPT_STRING("driver", driver, 0),
OPT_STRING("device", device, 0),
OPT_FLOAT("freq", freq, 0),
OPT_STRING("channel", channel, 0),
OPT_STRING("chanlist", chanlist, 0),
OPT_STRING("norm", norm, 0),
OPT_INTRANGE("automute", automute, 0, 0, 255),
#if HAVE_TV_V4L2
OPT_INT("normid", normid, 0),
#endif
OPT_INTRANGE("width", width, 0, 0, 4096),
OPT_INTRANGE("height", height, 0, 0, 4096),
OPT_INT("input", input, 0),
OPT_GENERAL(int, "outfmt", outfmt, 0, .type = &m_option_type_fourcc),
OPT_FLOAT("fps", fps, 0),
OPT_STRINGLIST("channels", channels, 0),
OPT_INTRANGE("brightness", brightness, 0, -100, 100),
OPT_INTRANGE("contrast", contrast, 0, -100, 100),
OPT_INTRANGE("hue", hue, 0, -100, 100),
OPT_INTRANGE("saturation", saturation, 0, -100, 100),
OPT_INTRANGE("gain", gain, 0, -1, 100),
#if HAVE_TV_V4L2
OPT_INTRANGE("amode", amode, 0, 0, 3),
OPT_INTRANGE("volume", volume, 0, 0, 65535),
OPT_INTRANGE("bass", bass, 0, 0, 65535),
OPT_INTRANGE("treble", treble, 0, 0, 65535),
OPT_INTRANGE("balance", balance, 0, 0, 65535),
OPT_INTRANGE("forcechan", forcechan, 0, 1, 2),
OPT_FLAG("forceaudio", force_audio, 0),
OPT_INTRANGE("buffersize", buffer_size, 0, 16, 1024),
OPT_FLAG("mjpeg", mjpeg, 0),
OPT_INTRANGE("decimation", decimation, 0, 1, 4),
OPT_INTRANGE("quality", quality, 0, 0, 100),
#if HAVE_ALSA
OPT_FLAG("alsa", alsa, 0),
#endif /* HAVE_ALSA */
#endif /* HAVE_TV_V4L2 */
OPT_STRING("adevice", adevice, 0),
OPT_INTRANGE("audioid", audio_id, 0, 0, 9),
OPT_FLAG("scan-autostart", scan, 0),
OPT_INTRANGE("scan-threshold", scan_threshold, 0, 1, 100),
OPT_FLOATRANGE("scan-period", scan_period, 0, 0.1, 2.0),
{0}
},
.size = sizeof(tv_param_t),
.defaults = &(const tv_param_t){
.chanlist = "europe-east",
.norm = "pal",
.normid = -1,
.width = -1,
.height = -1,
.outfmt = -1,
.fps = -1.0,
.audio = 1,
.immediate = 1,
.audiorate = 44100,
.amode = -1,
.volume = -1,
.bass = -1,
.treble = -1,
.balance = -1,
.forcechan = -1,
.buffer_size = -1,
.decimation = 2,
.quality = 90,
.gain = -1,
.scan_threshold = 50,
.scan_period = 0.5,
},
};
tvi_handle_t *tv_new_handle(int size, struct mp_log *log, const tvi_functions_t *functions)
{
tvi_handle_t *h = calloc(1, sizeof(*h));
if (!h)
return NULL;
h->priv = calloc(1, size);
if (!h->priv) {
free(h);
return NULL;
}
h->log = log;
h->functions = functions;
h->chanlist = -1;
h->norm = -1;
h->channel = -1;
return h;
}
void tv_free_handle(tvi_handle_t *h)
{
if (!h)
return;
free(h->priv);
free(h->scan);
free(h);
}
void tv_start_scan(tvi_handle_t *tvh, int start)
{
MP_INFO(tvh, "start scan\n");
tvh->tv_param->scan=start?1:0;
}
static int tv_set_freq_float(tvi_handle_t *tvh, float freq)
{
return tv_set_freq(tvh, freq/1000.0*16);
}
void tv_scan(tvi_handle_t *tvh)
{
unsigned int now;
struct CHANLIST cl;
tv_channels_t *tv_channel_tmp=NULL;
tv_channels_t *tv_channel_add=NULL;
tv_scan_t* scan;
int found=0, index=1;
//Channel scanner without tuner is useless and causes crash due to uninitialized chanlist_s
if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) != TVI_CONTROL_TRUE)
{
MP_WARN(tvh, "Channel scanner is not available without tuner\n");
tvh->tv_param->scan=0;
return;
}
scan = tvh->scan;
now=(unsigned int)mp_time_us();
if (!scan) {
scan=calloc(1,sizeof(tv_scan_t));
tvh->scan=scan;
cl = tvh->chanlist_s[scan->channel_num];
tv_set_freq_float(tvh, cl.freq);
scan->scan_timer=now+1e6*tvh->tv_param->scan_period;
}
if(scan->scan_timer>now)
return;
if (tv_get_signal(tvh)>tvh->tv_param->scan_threshold) {
cl = tvh->chanlist_s[scan->channel_num];
tv_channel_tmp=tvh->tv_channel_list;
while (tv_channel_tmp) {
index++;
if (cl.freq==tv_channel_tmp->freq){
found=1;
break;
}
tv_channel_add=tv_channel_tmp;
tv_channel_tmp=tv_channel_tmp->next;
}
if (!found) {
MP_INFO(tvh, "Found new channel: %s (#%d). \n",cl.name,index);
scan->new_channels++;
tv_channel_tmp = malloc(sizeof(tv_channels_t));
tv_channel_tmp->index=index;
tv_channel_tmp->next=NULL;
tv_channel_tmp->prev=tv_channel_add;
tv_channel_tmp->freq=cl.freq;
snprintf(tv_channel_tmp->name,sizeof(tv_channel_tmp->name),"ch%d",index);
strncpy(tv_channel_tmp->number, cl.name, 5);
tv_channel_tmp->number[4]='\0';
if (!tvh->tv_channel_list)
tvh->tv_channel_list=tv_channel_tmp;
else {
tv_channel_add->next=tv_channel_tmp;
tvh->tv_channel_list->prev=tv_channel_tmp;
}
}else
MP_INFO(tvh, "Found existing channel: %s-%s.\n",
tv_channel_tmp->number,tv_channel_tmp->name);
}
scan->channel_num++;
scan->scan_timer=now+1e6*tvh->tv_param->scan_period;
if (scan->channel_num>=chanlists[tvh->chanlist].count) {
tvh->tv_param->scan=0;
MP_INFO(tvh, "TV scan end. Found %d new channels.\n", scan->new_channels);
tv_channel_tmp=tvh->tv_channel_list;
if(tv_channel_tmp){
MP_INFO(tvh, "channels=");
while(tv_channel_tmp){
MP_INFO(tvh, "%s-%s",tv_channel_tmp->number,tv_channel_tmp->name);
if(tv_channel_tmp->next)
MP_INFO(tvh, ",");
tv_channel_tmp=tv_channel_tmp->next;
}
MP_INFO(tvh, "\n");
}
if (!tvh->tv_channel_current) tvh->tv_channel_current=tvh->tv_channel_list;
if (tvh->tv_channel_current)
tv_set_freq_float(tvh, tvh->tv_channel_current->freq);
free(tvh->scan);
tvh->scan=NULL;
}else{
cl = tvh->chanlist_s[scan->channel_num];
tv_set_freq_float(tvh, cl.freq);
MP_INFO(tvh, "Trying: %s (%.2f). \n",cl.name,1e-3*cl.freq);
}
}
static int norm_from_string(tvi_handle_t *tvh, char* norm)
{
const tvi_functions_t *funcs = tvh->functions;
char str[20];
int ret;
strncpy(str, norm, sizeof(str)-1);
str[sizeof(str)-1] = '\0';
ret=funcs->control(tvh->priv, TVI_CONTROL_SPC_GET_NORMID, str);
if (ret == TVI_CONTROL_TRUE) {
int *v = (int *)str;
return *v;
}
if(ret!=TVI_CONTROL_UNKNOWN)
{
MP_WARN(tvh, "tv.c: norm_from_string(%s): Bogus norm parameter, setting %s.\n", norm,"default");
return 0;
}
if (!strcasecmp(norm, "pal"))
return TV_NORM_PAL;
else if (!strcasecmp(norm, "ntsc"))
return TV_NORM_NTSC;
else if (!strcasecmp(norm, "secam"))
return TV_NORM_SECAM;
else if (!strcasecmp(norm, "palnc"))
return TV_NORM_PALNC;
else if (!strcasecmp(norm, "palm"))
return TV_NORM_PALM;
else if (!strcasecmp(norm, "paln"))
return TV_NORM_PALN;
else if (!strcasecmp(norm, "ntscjp"))
return TV_NORM_NTSCJP;
else {
MP_WARN(tvh, "tv.c: norm_from_string(%s): Bogus norm parameter, setting %s.\n", norm, "PAL");
return TV_NORM_PAL;
}
}
static void parse_channels(tvi_handle_t *tvh)
{
char** channels = tvh->tv_param->channels;
MP_INFO(tvh, "TV channel names detected.\n");
tvh->tv_channel_list = malloc(sizeof(tv_channels_t));
tvh->tv_channel_list->index=1;
tvh->tv_channel_list->next=NULL;
tvh->tv_channel_list->prev=NULL;
tvh->tv_channel_current = tvh->tv_channel_list;
tvh->tv_channel_current->norm = tvh->norm;
while (*channels) {
char* tmp = *(channels++);
char* sep = strchr(tmp,'-');
int i;
struct CHANLIST cl;
if (!sep) continue; // Wrong syntax, but mplayer should not crash
av_strlcpy(tvh->tv_channel_current->name, sep + 1,
sizeof(tvh->tv_channel_current->name));
sep[0] = '\0';
strncpy(tvh->tv_channel_current->number, tmp, 5);
tvh->tv_channel_current->number[4]='\0';
while ((sep=strchr(tvh->tv_channel_current->name, '_')))
sep[0] = ' ';
// if channel number is a number and larger than 1000 threat it as frequency
// tmp still contain pointer to null-terminated string with channel number here
if (atoi(tmp)>1000){
tvh->tv_channel_current->freq=atoi(tmp);
}else{
tvh->tv_channel_current->freq = 0;
for (i = 0; i < chanlists[tvh->chanlist].count; i++) {
cl = tvh->chanlist_s[i];
if (!strcasecmp(cl.name, tvh->tv_channel_current->number)) {
tvh->tv_channel_current->freq=cl.freq;
break;
}
}
}
if (tvh->tv_channel_current->freq == 0)
MP_ERR(tvh, "Couldn't find frequency for channel %s (%s)\n",
tvh->tv_channel_current->number, tvh->tv_channel_current->name);
else {
sep = strchr(tvh->tv_channel_current->name, '-');
if ( !sep ) sep = strchr(tvh->tv_channel_current->name, '+');
if ( sep ) {
i = atoi (sep+1);
if ( sep[0] == '+' ) tvh->tv_channel_current->freq += i * 100;
if ( sep[0] == '-' ) tvh->tv_channel_current->freq -= i * 100;
sep[0] = '\0';
}
sep = strchr(tvh->tv_channel_current->name, '=');
if ( sep ) {
tvh->tv_channel_current->norm = norm_from_string(tvh, sep+1);
sep[0] = '\0';
}
}
/*MP_INFO(tvh, "-- Detected channel %s - %s (%5.3f)\n",
tvh->tv_channel_current->number, tvh->tv_channel_current->name,
(float)tvh->tv_channel_current->freq/1000);*/
tvh->tv_channel_current->next = malloc(sizeof(tv_channels_t));
tvh->tv_channel_current->next->index = tvh->tv_channel_current->index + 1;
tvh->tv_channel_current->next->prev = tvh->tv_channel_current;
tvh->tv_channel_current->next->next = NULL;
tvh->tv_channel_current = tvh->tv_channel_current->next;
tvh->tv_channel_current->norm = tvh->norm;
}
if (tvh->tv_channel_current->prev)
tvh->tv_channel_current->prev->next = NULL;
free(tvh->tv_channel_current);
}
int tv_set_norm(tvi_handle_t *tvh, char* norm)
{
tvh->norm = norm_from_string(tvh, norm);
MP_VERBOSE(tvh, "Selected norm : %s\n", norm);
if (tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM, &tvh->norm) != TVI_CONTROL_TRUE) {
MP_ERR(tvh, "Error: Cannot set norm!\n");
return 0;
}
return 1;
}
static int tv_set_norm_i(tvi_handle_t *tvh, int norm)
{
tvh->norm = norm;
MP_VERBOSE(tvh, "Selected norm id: %d\n", norm);
if (tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM, &tvh->norm) != TVI_CONTROL_TRUE) {
MP_ERR(tvh, "Error: Cannot set norm!\n");
return 0;
}
return 1;
}
static void set_norm_and_freq(tvi_handle_t *tvh, tv_channels_t *chan)
{
MP_INFO(tvh, "Selected channel: %s - %s (freq: %.3f)\n",
chan->number, chan->name, chan->freq/1000.0);
tv_set_norm_i(tvh, chan->norm);
tv_set_freq_float(tvh, chan->freq);
}
int open_tv(tvi_handle_t *tvh)
{
int i;
const tvi_functions_t *funcs = tvh->functions;
static const int tv_fmt_list[] = {
MP_FOURCC_YV12,
MP_FOURCC_I420,
MP_FOURCC_UYVY,
MP_FOURCC_YUY2,
MP_FOURCC_RGB32,
MP_FOURCC_RGB24,
MP_FOURCC_RGB16,
MP_FOURCC_RGB15
};
if (funcs->control(tvh->priv, TVI_CONTROL_IS_VIDEO, 0) != TVI_CONTROL_TRUE)
{
MP_ERR(tvh, "Error: No video input present!\n");
return 0;
}
if (tvh->tv_param->outfmt == -1)
for (i = 0; i < sizeof (tv_fmt_list) / sizeof (*tv_fmt_list); i++)
{
tvh->tv_param->outfmt = tv_fmt_list[i];
if (funcs->control (tvh->priv, TVI_CONTROL_VID_SET_FORMAT,
&tvh->tv_param->outfmt) == TVI_CONTROL_TRUE)
break;
}
else
{
switch(tvh->tv_param->outfmt)
{
case MP_FOURCC_YV12:
case MP_FOURCC_I420:
case MP_FOURCC_UYVY:
case MP_FOURCC_YUY2:
case MP_FOURCC_RGB32:
case MP_FOURCC_RGB24:
case MP_FOURCC_BGR32:
case MP_FOURCC_BGR24:
case MP_FOURCC_BGR16:
case MP_FOURCC_BGR15:
break;
default:
MP_ERR(tvh, "==================================================================\n"\
" WARNING: UNTESTED OR UNKNOWN OUTPUT IMAGE FORMAT REQUESTED (0x%x)\n"\
" This may cause buggy playback or program crash! Bug reports will\n"\
" be ignored! You should try again with YV12 (which is the default\n"\
" colorspace) and read the documentation!\n"\
"==================================================================\n"
,tvh->tv_param->outfmt);
}
funcs->control(tvh->priv, TVI_CONTROL_VID_SET_FORMAT, &tvh->tv_param->outfmt);
}
/* set some params got from cmdline */
funcs->control(tvh->priv, TVI_CONTROL_SPC_SET_INPUT, &tvh->tv_param->input);
if ((!strcmp(tvh->tv_param->driver, "v4l2") && tvh->tv_param->normid >= 0))
tv_set_norm_i(tvh, tvh->tv_param->normid);
else
tv_set_norm(tvh,tvh->tv_param->norm);
/* limits on w&h are norm-dependent -- JM */
if (tvh->tv_param->width != -1 && tvh->tv_param->height != -1) {
// first tell the driver both width and height, some drivers do not support setting them independently.
int dim[2];
dim[0] = tvh->tv_param->width; dim[1] = tvh->tv_param->height;
funcs->control(tvh->priv, TVI_CONTROL_VID_SET_WIDTH_HEIGHT, dim);
}
/* set width */
if (tvh->tv_param->width != -1)
{
if (funcs->control(tvh->priv, TVI_CONTROL_VID_CHK_WIDTH, &tvh->tv_param->width) == TVI_CONTROL_TRUE)
funcs->control(tvh->priv, TVI_CONTROL_VID_SET_WIDTH, &tvh->tv_param->width);
else
{
MP_ERR(tvh, "Unable to set requested width: %d\n", tvh->tv_param->width);
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &tvh->tv_param->width);
}
}
/* set height */
if (tvh->tv_param->height != -1)
{
if (funcs->control(tvh->priv, TVI_CONTROL_VID_CHK_HEIGHT, &tvh->tv_param->height) == TVI_CONTROL_TRUE)
funcs->control(tvh->priv, TVI_CONTROL_VID_SET_HEIGHT, &tvh->tv_param->height);
else
{
MP_ERR(tvh, "Unable to set requested height: %d\n", tvh->tv_param->height);
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &tvh->tv_param->height);
}
}
if (funcs->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) != TVI_CONTROL_TRUE)
{
MP_WARN(tvh, "Selected input hasn't got a tuner!\n");
goto done;
}
/* select channel list */
for (i = 0; chanlists[i].name != NULL; i++)
{
if (!strcasecmp(chanlists[i].name, tvh->tv_param->chanlist))
{
tvh->chanlist = i;
tvh->chanlist_s = chanlists[i].list;
break;
}
}
if (tvh->chanlist == -1) {
MP_WARN(tvh, "Unable to find selected channel list! (%s)\n",
tvh->tv_param->chanlist);
return 0;
} else
MP_VERBOSE(tvh, "Selected channel list: %s (including %d channels)\n",
chanlists[tvh->chanlist].name, chanlists[tvh->chanlist].count);
if (tvh->tv_param->freq && tvh->tv_param->channel)
{
MP_WARN(tvh, "You can't set frequency and channel simultaneously!\n");
goto done;
}
/* Handle channel names */
if (tvh->tv_param->channels) {
parse_channels(tvh);
} else
tvh->tv_channel_last_real = malloc(5);
if (tvh->tv_channel_list) {
int channel = 0;
if (tvh->tv_param->channel)
{
if (mp_isdigit(*tvh->tv_param->channel))
/* if tvh->tv_param->channel begins with a digit interpret it as a number */
channel = atoi(tvh->tv_param->channel);
else
{
/* if tvh->tv_param->channel does not begin with a digit
set the first channel that contains tvh->tv_param->channel in its name */
tvh->tv_channel_current = tvh->tv_channel_list;
while ( tvh->tv_channel_current ) {
if ( strstr(tvh->tv_channel_current->name, tvh->tv_param->channel) )
break;
tvh->tv_channel_current = tvh->tv_channel_current->next;
}
if ( !tvh->tv_channel_current ) tvh->tv_channel_current = tvh->tv_channel_list;
}
}
else
channel = 1;
if ( channel ) {
tvh->tv_channel_current = tvh->tv_channel_list;
for (int n = 1; n < channel; n++)
if (tvh->tv_channel_current->next)
tvh->tv_channel_current = tvh->tv_channel_current->next;
}
set_norm_and_freq(tvh, tvh->tv_channel_current);
tvh->tv_channel_last = tvh->tv_channel_current;
} else {
/* we need to set frequency */
if (tvh->tv_param->freq)
{
unsigned long freq = tvh->tv_param->freq * 16;
/* set freq in MHz */
funcs->control(tvh->priv, TVI_CONTROL_TUN_SET_FREQ, &freq);
funcs->control(tvh->priv, TVI_CONTROL_TUN_GET_FREQ, &freq);
MP_VERBOSE(tvh, "Selected frequency: %lu (%.3f)\n",
freq, freq/16.0);
}
if (tvh->tv_param->channel) {
struct CHANLIST cl;
MP_VERBOSE(tvh, "Requested channel: %s\n", tvh->tv_param->channel);
for (i = 0; i < chanlists[tvh->chanlist].count; i++)
{
cl = tvh->chanlist_s[i];
// printf("count%d: name: %s, freq: %d\n",
// i, cl.name, cl.freq);
if (!strcasecmp(cl.name, tvh->tv_param->channel))
{
strcpy(tvh->tv_channel_last_real, cl.name);
tvh->channel = i;
MP_INFO(tvh, "Selected channel: %s (freq: %.3f)\n",
cl.name, cl.freq/1000.0);
tv_set_freq_float(tvh, cl.freq);
break;
}
}
}
}
/* grep frequency in chanlist */
{
unsigned long i2 = 0;
int freq;
tv_get_freq(tvh, &i2);
freq = (int) (((float)(i2/16))*1000)+250;
for (i = 0; i < chanlists[tvh->chanlist].count; i++)
{
if (tvh->chanlist_s[i].freq == freq)
{
tvh->channel = i+1;
break;
}
}
}
done:
/* also start device! */
return 1;
}
tvi_handle_t *tv_begin(tv_param_t* tv_param, struct mp_log *log)
{
int i;
tvi_handle_t* h;
if(tv_param->driver && !strcmp(tv_param->driver,"help")){
mp_info(log, "Available drivers:\n");
for(i=0;tvi_driver_list[i];i++){
mp_info(log, " %s\t%s\n",tvi_driver_list[i]->short_name,tvi_driver_list[i]->name);
}
return NULL;
}
for(i=0;tvi_driver_list[i];i++){
if (!tv_param->driver || !strcmp(tvi_driver_list[i]->short_name, tv_param->driver)){
h=tvi_driver_list[i]->tvi_init(log, tv_param);
//Requested driver initialization failed
if (!h && tv_param->driver)
return NULL;
//Driver initialization failed during autodetection process.
if (!h)
continue;
h->tv_param=tv_param;
MP_INFO(h, "Selected driver: %s\n name: %s\n", tvi_driver_list[i]->short_name,
tvi_driver_list[i]->name);
talloc_free(tv_param->driver);
tv_param->driver=talloc_strdup(NULL, tvi_driver_list[i]->short_name);
return h;
}
}
if(tv_param->driver)
mp_err(log, "No such driver: %s\n", tv_param->driver);
else
mp_err(log, "TV driver autodetection failed.\n");
return NULL;
}
int tv_uninit(tvi_handle_t *tvh)
{
int res;
if(!tvh) return 1;
if (!tvh->priv) return 1;
res=tvh->functions->uninit(tvh->priv);
if(res) {
free(tvh->priv);
tvh->priv=NULL;
}
return res;
}
int tv_set_color_options(tvi_handle_t *tvh, int opt, int value)
{
const tvi_functions_t *funcs = tvh->functions;
switch(opt)
{
case TV_COLOR_BRIGHTNESS:
return funcs->control(tvh->priv, TVI_CONTROL_VID_SET_BRIGHTNESS, &value);
case TV_COLOR_HUE:
return funcs->control(tvh->priv, TVI_CONTROL_VID_SET_HUE, &value);
case TV_COLOR_SATURATION:
return funcs->control(tvh->priv, TVI_CONTROL_VID_SET_SATURATION, &value);
case TV_COLOR_CONTRAST:
return funcs->control(tvh->priv, TVI_CONTROL_VID_SET_CONTRAST, &value);
default:
MP_WARN(tvh, "Unknown color option (%d) specified!\n", opt);
}
return TVI_CONTROL_UNKNOWN;
}
int tv_get_color_options(tvi_handle_t *tvh, int opt, int* value)
{
const tvi_functions_t *funcs = tvh->functions;
switch(opt)
{
case TV_COLOR_BRIGHTNESS:
return funcs->control(tvh->priv, TVI_CONTROL_VID_GET_BRIGHTNESS, value);
case TV_COLOR_HUE:
return funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HUE, value);
case TV_COLOR_SATURATION:
return funcs->control(tvh->priv, TVI_CONTROL_VID_GET_SATURATION, value);
case TV_COLOR_CONTRAST:
return funcs->control(tvh->priv, TVI_CONTROL_VID_GET_CONTRAST, value);
default:
MP_WARN(tvh, "Unknown color option (%d) specified!\n", opt);
}
return TVI_CONTROL_UNKNOWN;
}
int tv_get_freq(tvi_handle_t *tvh, unsigned long *freq)
{
if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) == TVI_CONTROL_TRUE)
{
tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_GET_FREQ, freq);
MP_VERBOSE(tvh, "Current frequency: %lu (%.3f)\n",
*freq, *freq/16.0);
}
return 1;
}
int tv_set_freq(tvi_handle_t *tvh, unsigned long freq)
{
if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) == TVI_CONTROL_TRUE)
{
// unsigned long freq = atof(tvh->tv_param->freq)*16;
/* set freq in MHz */
tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_SET_FREQ, &freq);
tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_GET_FREQ, &freq);
MP_VERBOSE(tvh, "Current frequency: %lu (%.3f)\n",
freq, freq/16.0);
}
return 1;
}
int tv_get_signal(tvi_handle_t *tvh)
{
int signal=0;
if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_TUNER, 0) != TVI_CONTROL_TRUE ||
tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_GET_SIGNAL, &signal)!=TVI_CONTROL_TRUE)
return 0;
return signal;
}
/*****************************************************************
* \brief tune current frequency by step_interval value
* \parameter step_interval increment value in 1/16 MHz
* \note frequency is rounded to 1/16 MHz value
* \return 1
*
*/
int tv_step_freq(tvi_handle_t* tvh, float step_interval){
unsigned long frequency = 0;
tvh->tv_param->scan=0;
tv_get_freq(tvh,&frequency);
frequency+=step_interval;
return tv_set_freq(tvh,frequency);
}
int tv_step_channel_real(tvi_handle_t *tvh, int direction)
{
struct CHANLIST cl;
tvh->tv_param->scan=0;
if (direction == TV_CHANNEL_LOWER)
{
if (tvh->channel-1 >= 0)
{
strcpy(tvh->tv_channel_last_real, tvh->chanlist_s[tvh->channel].name);
cl = tvh->chanlist_s[--tvh->channel];
MP_INFO(tvh, "Selected channel: %s (freq: %.3f)\n",
cl.name, cl.freq/1000.0);
tv_set_freq_float(tvh, cl.freq);
}
}
if (direction == TV_CHANNEL_HIGHER)
{
if (tvh->channel+1 < chanlists[tvh->chanlist].count)
{
strcpy(tvh->tv_channel_last_real, tvh->chanlist_s[tvh->channel].name);
cl = tvh->chanlist_s[++tvh->channel];
MP_INFO(tvh, "Selected channel: %s (freq: %.3f)\n",
cl.name, cl.freq/1000.0);
tv_set_freq_float(tvh, cl.freq);
}
}
return 1;
}
int tv_step_channel(tvi_handle_t *tvh, int direction) {
tvh->tv_param->scan=0;
if (tvh->tv_channel_list) {
if (direction == TV_CHANNEL_HIGHER) {
tvh->tv_channel_last = tvh->tv_channel_current;
if (tvh->tv_channel_current->next)
tvh->tv_channel_current = tvh->tv_channel_current->next;
else
tvh->tv_channel_current = tvh->tv_channel_list;
set_norm_and_freq(tvh, tvh->tv_channel_current);
}
if (direction == TV_CHANNEL_LOWER) {
tvh->tv_channel_last = tvh->tv_channel_current;
if (tvh->tv_channel_current->prev)
tvh->tv_channel_current = tvh->tv_channel_current->prev;
else
while (tvh->tv_channel_current->next)
tvh->tv_channel_current = tvh->tv_channel_current->next;
set_norm_and_freq(tvh, tvh->tv_channel_current);
}
} else tv_step_channel_real(tvh, direction);
return 1;
}
int tv_set_channel_real(tvi_handle_t *tvh, char *channel) {
int i;
struct CHANLIST cl;
tvh->tv_param->scan=0;
strcpy(tvh->tv_channel_last_real, tvh->chanlist_s[tvh->channel].name);
for (i = 0; i < chanlists[tvh->chanlist].count; i++)
{
cl = tvh->chanlist_s[i];
// printf("count%d: name: %s, freq: %d\n",
// i, cl.name, cl.freq);
if (!strcasecmp(cl.name, channel))
{
tvh->channel = i;
MP_INFO(tvh, "Selected channel: %s (freq: %.3f)\n",
cl.name, cl.freq/1000.0);
tv_set_freq_float(tvh, cl.freq);
break;
}
}
return 1;
}
int tv_set_channel(tvi_handle_t *tvh, char *channel) {
int i, channel_int;
tvh->tv_param->scan=0;
if (tvh->tv_channel_list) {
tvh->tv_channel_last = tvh->tv_channel_current;
channel_int = atoi(channel);
tvh->tv_channel_current = tvh->tv_channel_list;
for (i = 1; i < channel_int; i++)
if (tvh->tv_channel_current->next)
tvh->tv_channel_current = tvh->tv_channel_current->next;
set_norm_and_freq(tvh, tvh->tv_channel_current);
} else tv_set_channel_real(tvh, channel);
return 1;
}
int tv_last_channel(tvi_handle_t *tvh) {
tvh->tv_param->scan=0;
if (tvh->tv_channel_list) {
tv_channels_t *tmp;
tmp = tvh->tv_channel_last;
tvh->tv_channel_last = tvh->tv_channel_current;
tvh->tv_channel_current = tmp;
set_norm_and_freq(tvh, tvh->tv_channel_current);
} else {
int i;
struct CHANLIST cl;
for (i = 0; i < chanlists[tvh->chanlist].count; i++)
{
cl = tvh->chanlist_s[i];
if (!strcasecmp(cl.name, tvh->tv_channel_last_real))
{
strcpy(tvh->tv_channel_last_real, tvh->chanlist_s[tvh->channel].name);
tvh->channel = i;
MP_INFO(tvh, "Selected channel: %s (freq: %.3f)\n",
cl.name, cl.freq/1000.0);
tv_set_freq_float(tvh, cl.freq);
break;
}
}
}
return 1;
}
int tv_step_norm(tvi_handle_t *tvh)
{
tvh->norm++;
if (tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM,
&tvh->norm) != TVI_CONTROL_TRUE) {
tvh->norm = 0;
if (tvh->functions->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM,
&tvh->norm) != TVI_CONTROL_TRUE) {
MP_ERR(tvh, "Error: Cannot set norm!\n");
return 0;
}
}
return 1;
}
int tv_stream_control(tvi_handle_t *tvh, int cmd, void *arg)
{
switch (cmd) {
case STREAM_CTRL_TV_SET_SCAN:
tv_start_scan(tvh, *(int *)arg);
return STREAM_OK;
case STREAM_CTRL_SET_TV_FREQ:
tv_set_freq(tvh, *(float *)arg * 16.0f);
return STREAM_OK;
case STREAM_CTRL_GET_TV_FREQ: {
unsigned long tmp = 0;
tv_get_freq(tvh, &tmp);
*(float *)arg = tmp / 16.0f;
return STREAM_OK;
}
case STREAM_CTRL_SET_TV_COLORS:
tv_set_color_options(tvh, ((int *)arg)[0], ((int *)arg)[1]);
return STREAM_OK;
case STREAM_CTRL_GET_TV_COLORS:
tv_get_color_options(tvh, ((int *)arg)[0], &((int *)arg)[1]);
return STREAM_OK;
case STREAM_CTRL_TV_SET_NORM:
tv_set_norm(tvh, (char *)arg);
return STREAM_OK;
case STREAM_CTRL_TV_STEP_NORM:
tv_step_norm(tvh);
return STREAM_OK;
case STREAM_CTRL_TV_SET_CHAN:
tv_set_channel(tvh, (char *)arg);
return STREAM_OK;
case STREAM_CTRL_TV_STEP_CHAN:
if (*(int *)arg >= 0) {
tv_step_channel(tvh, TV_CHANNEL_HIGHER);
} else {
tv_step_channel(tvh, TV_CHANNEL_LOWER);
}
return STREAM_OK;
case STREAM_CTRL_TV_LAST_CHAN:
tv_last_channel(tvh);
return STREAM_OK;
}
return STREAM_UNSUPPORTED;
}

View File

@ -1,285 +0,0 @@
/*
* TV interface
*
* Copyright (C) 2001 Alex Beregszászi
* Copyright (C) 2007 Attila Ötvös
* Copyright (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_TV_H
#define MPLAYER_TV_H
#include "osdep/endian.h"
#include "config.h"
#if !HAVE_GPL
#error GPL only
#endif
struct mp_log;
typedef struct tv_params {
float freq;
char *channel;
char *chanlist;
char *norm;
int automute;
int normid;
char *device;
char *driver;
int width;
int height;
int input;
int outfmt;
float fps;
char **channels;
int audio;
int immediate;
int audiorate;
int audio_id;
int amode;
int volume;
int bass;
int treble;
int balance;
int forcechan;
int force_audio;
int buffer_size;
int mjpeg;
int decimation;
int quality;
int alsa;
char* adevice;
int brightness;
int contrast;
int hue;
int saturation;
int gain;
int scan;
int scan_threshold;
float scan_period;
} tv_param_t;
struct tv_stream_params {
char *channel;
int input;
};
typedef struct tvi_info_s
{
struct tvi_handle_s * (*tvi_init)(struct mp_log *log, tv_param_t* tv_param);
const char *name;
const char *short_name;
} tvi_info_t;
struct priv;
typedef struct tvi_functions_s
{
int (*init)(struct priv *priv);
int (*uninit)(struct priv *priv);
int (*control)(struct priv *priv, int cmd, void *arg);
int (*start)(struct priv *priv);
double (*grab_video_frame)(struct priv *priv, char *buffer, int len);
int (*get_video_framesize)(struct priv *priv);
double (*grab_audio_frame)(struct priv *priv, char *buffer, int len);
int (*get_audio_framesize)(struct priv *priv);
} tvi_functions_t;
typedef struct tvi_handle_s {
struct mp_log *log;
const tvi_functions_t *functions;
void *priv;
int seq;
struct demuxer *demuxer;
/* specific */
int norm;
int chanlist;
const struct CHANLIST *chanlist_s;
int channel;
tv_param_t * tv_param;
void * scan;
struct tv_channels_s *tv_channel_list;
struct tv_channels_s *tv_channel_current, *tv_channel_last;
char *tv_channel_last_real;
} tvi_handle_t;
typedef struct tv_channels_s {
int index;
char number[5];
char name[20];
int norm;
int freq;
struct tv_channels_s *next;
struct tv_channels_s *prev;
} tv_channels_t;
typedef struct {
unsigned int scan_timer;
int channel_num;
int new_channels;
} tv_scan_t;
#define TVI_CONTROL_FALSE 0
#define TVI_CONTROL_TRUE 1
#define TVI_CONTROL_NA -1
#define TVI_CONTROL_UNKNOWN -2
/* ======================== CONTROLS =========================== */
/* GENERIC controls */
#define TVI_CONTROL_IS_AUDIO 0x1
#define TVI_CONTROL_IS_VIDEO 0x2
#define TVI_CONTROL_IS_TUNER 0x3
#define TVI_CONTROL_IMMEDIATE 0x4
/* VIDEO controls */
#define TVI_CONTROL_VID_GET_FPS 0x101
#define TVI_CONTROL_VID_GET_PLANES 0x102
#define TVI_CONTROL_VID_GET_BITS 0x103
#define TVI_CONTROL_VID_CHK_BITS 0x104
#define TVI_CONTROL_VID_SET_BITS 0x105
#define TVI_CONTROL_VID_GET_FORMAT 0x106
#define TVI_CONTROL_VID_CHK_FORMAT 0x107
#define TVI_CONTROL_VID_SET_FORMAT 0x108
#define TVI_CONTROL_VID_GET_WIDTH 0x109
#define TVI_CONTROL_VID_CHK_WIDTH 0x110
#define TVI_CONTROL_VID_SET_WIDTH 0x111
#define TVI_CONTROL_VID_GET_HEIGHT 0x112
#define TVI_CONTROL_VID_CHK_HEIGHT 0x113
#define TVI_CONTROL_VID_SET_HEIGHT 0x114
#define TVI_CONTROL_VID_GET_BRIGHTNESS 0x115
#define TVI_CONTROL_VID_SET_BRIGHTNESS 0x116
#define TVI_CONTROL_VID_GET_HUE 0x117
#define TVI_CONTROL_VID_SET_HUE 0x118
#define TVI_CONTROL_VID_GET_SATURATION 0x119
#define TVI_CONTROL_VID_SET_SATURATION 0x11a
#define TVI_CONTROL_VID_GET_CONTRAST 0x11b
#define TVI_CONTROL_VID_SET_CONTRAST 0x11c
#define TVI_CONTROL_VID_GET_PICTURE 0x11d
#define TVI_CONTROL_VID_SET_PICTURE 0x11e
#define TVI_CONTROL_VID_SET_GAIN 0x11f
#define TVI_CONTROL_VID_GET_GAIN 0x120
#define TVI_CONTROL_VID_SET_WIDTH_HEIGHT 0x121
/* TUNER controls */
#define TVI_CONTROL_TUN_GET_FREQ 0x201
#define TVI_CONTROL_TUN_SET_FREQ 0x202
#define TVI_CONTROL_TUN_GET_TUNER 0x203 /* update priv->tuner struct for used input */
#define TVI_CONTROL_TUN_SET_TUNER 0x204 /* update priv->tuner struct for used input */
#define TVI_CONTROL_TUN_GET_NORM 0x205
#define TVI_CONTROL_TUN_SET_NORM 0x206
#define TVI_CONTROL_TUN_GET_SIGNAL 0x207
/* AUDIO controls */
#define TVI_CONTROL_AUD_GET_FORMAT 0x301
#define TVI_CONTROL_AUD_GET_SAMPLERATE 0x302
#define TVI_CONTROL_AUD_GET_CHANNELS 0x304
#define TVI_CONTROL_AUD_SET_SAMPLERATE 0x305
/* SPECIFIC controls */
#define TVI_CONTROL_SPC_GET_INPUT 0x401 /* set input channel (tv,s-video,composite..) */
#define TVI_CONTROL_SPC_SET_INPUT 0x402 /* set input channel (tv,s-video,composite..) */
#define TVI_CONTROL_SPC_GET_NORMID 0x403 /* get normid from norm name */
int tv_set_color_options(tvi_handle_t *tvh, int opt, int val);
int tv_get_color_options(tvi_handle_t *tvh, int opt, int* val);
int tv_step_channel_real(tvi_handle_t *tvh, int direction);
int tv_step_channel(tvi_handle_t *tvh, int direction);
#define TV_CHANNEL_LOWER 1
#define TV_CHANNEL_HIGHER 2
int tv_last_channel(tvi_handle_t *tvh);
int tv_set_channel_real(tvi_handle_t *tvh, char *channel);
int tv_set_channel(tvi_handle_t *tvh, char *channel);
int tv_step_norm(tvi_handle_t *tvh);
int tv_step_chanlist(tvi_handle_t *tvh);
int tv_set_freq(tvi_handle_t *tvh, unsigned long freq);
int tv_get_freq(tvi_handle_t *tvh, unsigned long *freq);
int tv_get_signal(tvi_handle_t *tvh);
int tv_step_freq(tvi_handle_t *tvh, float step_interval);
int tv_set_norm(tvi_handle_t *tvh, char* norm);
void tv_start_scan(tvi_handle_t *tvh, int start);
tvi_handle_t *tv_new_handle(int size, struct mp_log *log, const tvi_functions_t *functions);
void tv_free_handle(tvi_handle_t *h);
#define TV_NORM_PAL 1
#define TV_NORM_NTSC 2
#define TV_NORM_SECAM 3
#define TV_NORM_PALNC 4
#define TV_NORM_PALM 5
#define TV_NORM_PALN 6
#define TV_NORM_NTSCJP 7
int tv_uninit(tvi_handle_t *tvh);
void tv_scan(tvi_handle_t *tvh);
int open_tv(tvi_handle_t *tvh);
tvi_handle_t *tv_begin(tv_param_t* tv_param, struct mp_log *log);
int tv_stream_control(tvi_handle_t *tvh, int cmd, void *arg);
extern const struct m_sub_options tv_params_conf;
#define MP_FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((unsigned)(d)<<24))
#if BYTE_ORDER == BIG_ENDIAN
#define MP_FOURCC_E(a,b,c,d) MP_FOURCC(a,b,c,d)
#else
#define MP_FOURCC_E(a,b,c,d) MP_FOURCC(d,c,b,a)
#endif
#define MP_FOURCC_RGB8 MP_FOURCC_E(8, 'B', 'G', 'R')
#define MP_FOURCC_RGB12 MP_FOURCC_E(12, 'B', 'G', 'R')
#define MP_FOURCC_RGB15 MP_FOURCC_E(15, 'B', 'G', 'R')
#define MP_FOURCC_RGB16 MP_FOURCC_E(16, 'B', 'G', 'R')
#define MP_FOURCC_RGB24 MP_FOURCC_E(24, 'B', 'G', 'R')
#define MP_FOURCC_RGB32 MP_FOURCC_E('A', 'B', 'G', 'R')
#define MP_FOURCC_BGR8 MP_FOURCC_E(8, 'R', 'G', 'B')
#define MP_FOURCC_BGR12 MP_FOURCC_E(12, 'R', 'G', 'B')
#define MP_FOURCC_BGR15 MP_FOURCC_E(15, 'R', 'G', 'B')
#define MP_FOURCC_BGR16 MP_FOURCC_E(16, 'R', 'G', 'B')
#define MP_FOURCC_BGR24 MP_FOURCC_E(24, 'R', 'G', 'B')
#define MP_FOURCC_BGR32 MP_FOURCC_E('A', 'R', 'G', 'B')
#define MP_FOURCC_YVU9 MP_FOURCC('Y', 'U', 'V', '9')
#define MP_FOURCC_YUV9 MP_FOURCC('Y', 'V', 'U', '9')
#define MP_FOURCC_YV12 MP_FOURCC('Y', 'V', '1', '2')
#define MP_FOURCC_I420 MP_FOURCC('I', '4', '2', '0')
#define MP_FOURCC_IYUV MP_FOURCC('I', 'Y', 'U', 'V')
#define MP_FOURCC_Y800 MP_FOURCC('Y', '8', '0', '0')
#define MP_FOURCC_NV12 MP_FOURCC('N', 'V', '1', '2')
#define MP_FOURCC_NV21 MP_FOURCC('N', 'V', '2', '1')
#define MP_FOURCC_UYVY MP_FOURCC('U', 'Y', 'V', 'Y')
#define MP_FOURCC_YUY2 MP_FOURCC('Y', 'U', 'Y', '2')
#define MP_FOURCC_MJPEG MP_FOURCC('M', 'J', 'P', 'G')
#define MP_FOURCC_JPEG MP_FOURCC('J', 'P', 'E', 'G')
#endif /* MPLAYER_TV_H */

View File

@ -1,93 +0,0 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_TVI_DEF_H
#define MPLAYER_TVI_DEF_H
#include <stdlib.h> /* malloc */
#include <string.h> /* memset */
#include "tv.h"
static int init(priv_t *priv);
static int uninit(priv_t *priv);
static int do_control(priv_t *priv, int cmd, void *arg);
static int start(priv_t *priv);
static double grab_video_frame(priv_t *priv, char *buffer, int len);
static int get_video_framesize(priv_t *priv);
static double grab_audio_frame(priv_t *priv, char *buffer, int len);
static int get_audio_framesize(priv_t *priv);
static const tvi_functions_t functions =
{
init,
uninit,
do_control,
start,
grab_video_frame,
get_video_framesize,
grab_audio_frame,
get_audio_framesize
};
/**
Fills video frame in given buffer with blue color for yv12,i420,uyvy,yuy2.
Other formats will be filled with 0xC0
*/
static inline void fill_blank_frame(char* buffer,int len,int fmt){
int i;
// RGB(0,0,255) <-> YVU(41,110,240)
switch(fmt){
case MP_FOURCC_YV12:
memset(buffer, 41,4*len/6); //Y
memset(buffer+4*len/6, 110,len/6);//V
memset(buffer+5*len/6, 240,len/6);//U
break;
case MP_FOURCC_I420:
memset(buffer, 41,4*len/6); //Y
memset(buffer+4*len/6, 240,len/6);//U
memset(buffer+5*len/6, 110,len/6);//V
break;
case MP_FOURCC_UYVY:
for(i=0;i<len;i+=4){
buffer[i]=0xFF;
buffer[i+1]=0;
buffer[i+2]=0;
buffer[i+3]=0;
}
break;
case MP_FOURCC_YUY2:
for(i=0;i<len;i+=4){
buffer[i]=0;
buffer[i+1]=0xFF;
buffer[i+2]=0;
buffer[i+3]=0;
}
break;
case MP_FOURCC_MJPEG:
case MP_FOURCC_JPEG:
/*
This is compressed format. I don't know yet how to fill such frame with blue color.
Keeping frame unchanged.
*/
break;
default:
memset(buffer,0xC0,len);
}
}
#endif /* MPLAYER_TVI_DEF_H */

View File

@ -1,126 +0,0 @@
/*
* Only a sample!
*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include "common/common.h"
#include "tv.h"
static tvi_handle_t *tvi_init_dummy(struct mp_log *log, tv_param_t* tv_param);
/* information about this file */
const tvi_info_t tvi_info_dummy = {
tvi_init_dummy,
"NULL-TV",
"dummy",
};
/* private data's */
typedef struct priv {
int width;
int height;
} priv_t;
#include "tvi_def.h"
/* handler creator - entry point ! */
static tvi_handle_t *tvi_init_dummy(struct mp_log *log, tv_param_t* tv_param)
{
return tv_new_handle(sizeof(priv_t), log, &functions);
}
/* initialisation */
static int init(priv_t *priv)
{
priv->width = 320;
priv->height = 200;
return 1;
}
/* that's the real start, we'got the format parameters (checked with control) */
static int start(priv_t *priv)
{
return 1;
}
static int uninit(priv_t *priv)
{
return 1;
}
static int do_control(priv_t *priv, int cmd, void *arg)
{
switch(cmd)
{
case TVI_CONTROL_IS_VIDEO:
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_GET_FORMAT:
*(int *)arg = MP_FOURCC_YV12;
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_SET_FORMAT:
{
// int req_fmt = *(int *)arg;
int req_fmt = *(int *)arg;
if (req_fmt != MP_FOURCC_YV12)
return TVI_CONTROL_FALSE;
return TVI_CONTROL_TRUE;
}
case TVI_CONTROL_VID_SET_WIDTH:
priv->width = *(int *)arg;
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_GET_WIDTH:
*(int *)arg = priv->width;
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_SET_HEIGHT:
priv->height = *(int *)arg;
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_GET_HEIGHT:
*(int *)arg = priv->height;
return TVI_CONTROL_TRUE;
case TVI_CONTROL_VID_CHK_WIDTH:
case TVI_CONTROL_VID_CHK_HEIGHT:
return TVI_CONTROL_TRUE;
case TVI_CONTROL_TUN_SET_NORM:
return TVI_CONTROL_TRUE;
}
return TVI_CONTROL_UNKNOWN;
}
static double grab_video_frame(priv_t *priv, char *buffer, int len)
{
memset(buffer, 0x42, len);
return MP_NOPTS_VALUE;
}
static int get_video_framesize(priv_t *priv)
{
/* YV12 */
return priv->width * priv->height * 12 / 8;
}
static double grab_audio_frame(priv_t *priv, char *buffer, int len)
{
memset(buffer, 0x42, len);
return MP_NOPTS_VALUE;
}
static int get_audio_framesize(priv_t *priv)
{
return 1;
}

File diff suppressed because it is too large Load Diff

72
wscript
View File

@ -342,35 +342,6 @@ iconv support use --disable-iconv.",
check_statement('zlib.h', 'inflate(0, Z_NO_FLUSH)')),
'req': True,
'fmsg': 'Unable to find development files for zlib.'
}, {
'name': '--libbluray',
'desc': 'Bluray support',
'func': check_pkg_config('libbluray', '>= 0.3.0'),
#'default': 'disable',
}, {
'name': '--dvdread',
'desc': 'dvdread support',
'deps': 'gpl',
'func': check_pkg_config('dvdread', '>= 4.1.0'),
'default': 'disable',
}, {
'name': '--dvdnav',
'desc': 'dvdnav support',
'deps': 'gpl',
'func': check_pkg_config('dvdnav', '>= 4.2.0',
'dvdread', '>= 4.1.0'),
'default': 'disable',
}, {
'name': 'dvdread-common',
'desc': 'DVD/IFO support',
'deps': 'gpl && (dvdread || dvdnav)',
'func': check_true,
}, {
'name': '--cdda',
'desc': 'cdda support (libcdio)',
'deps': 'gpl',
'func': check_pkg_config('libcdio_paranoia'),
'default': 'disable',
}, {
'name': '--uchardet',
'desc': 'uchardet support',
@ -846,47 +817,6 @@ hwaccel_features = [
}
]
radio_and_tv_features = [
{
'name': '--tv',
'desc': 'TV interface',
'deps': 'gpl',
'func': check_true,
'default': 'disable',
}, {
'name': 'sys_videoio_h',
'desc': 'videoio.h',
'func': check_cc(header_name=['sys/time.h', 'sys/videoio.h']),
'deps': 'tv',
}, {
'name': 'videodev',
'desc': 'videodev2.h',
'func': check_cc(header_name=['sys/time.h', 'linux/videodev2.h']),
'deps': 'tv && !sys_videoio_h',
}, {
'name': '--tv-v4l2',
'desc': 'Video4Linux2 TV interface',
'deps': 'tv && (sys_videoio_h || videodev)',
'func': check_true,
}, {
'name': '--libv4l2',
'desc': 'libv4l2 support',
'func': check_pkg_config('libv4l2'),
'deps': 'tv-v4l2',
}, {
'name': '--audio-input',
'desc': 'audio input support',
'deps': 'tv-v4l2',
'func': check_true
} , {
'name': '--dvbin',
'desc': 'DVB input module',
'deps': 'gpl',
'func': check_true,
'default': 'disable',
}
]
standalone_features = [
{
'name': 'win32-executable',
@ -955,7 +885,6 @@ def options(opt):
opt.parse_features('audio outputs', audio_output_features)
opt.parse_features('video outputs', video_output_features)
opt.parse_features('hwaccels', hwaccel_features)
opt.parse_features('tv features', radio_and_tv_features)
opt.parse_features('standalone app', standalone_features)
group = opt.get_option_group("optional features")
@ -1020,7 +949,6 @@ def configure(ctx):
ctx.parse_dependencies(video_output_features)
ctx.parse_dependencies(libav_dependencies)
ctx.parse_dependencies(hwaccel_features)
ctx.parse_dependencies(radio_and_tv_features)
if ctx.options.LUA_VER:
ctx.options.enable_lua = True

View File

@ -274,7 +274,6 @@ def build(ctx):
( "demux/cue.c" ),
( "demux/demux.c" ),
( "demux/demux_cue.c" ),
( "demux/demux_disc.c" ),
( "demux/demux_edl.c" ),
( "demux/demux_lavf.c" ),
( "demux/demux_libarchive.c", "libarchive" ),
@ -283,10 +282,8 @@ def build(ctx):
( "demux/demux_mkv_timeline.c" ),
( "demux/demux_null.c" ),
( "demux/demux_playlist.c" ),
( "demux/demux_rar.c" ),
( "demux/demux_raw.c" ),
( "demux/demux_timeline.c" ),
( "demux/demux_tv.c", "tv" ),
( "demux/ebml.c" ),
( "demux/packet.c" ),
( "demux/timeline.c" ),
@ -353,23 +350,10 @@ def build(ctx):
( "player/video.c" ),
## Streams
( "stream/ai_alsa1x.c", "alsa && audio-input" ),
( "stream/ai_oss.c", "oss-audio && audio-input" ),
( "stream/ai_sndio.c", "sndio && audio-input" ),
( "stream/audio_in.c", "audio-input" ),
( "stream/cookies.c" ),
( "stream/dvb_tune.c", "dvbin" ),
( "stream/frequencies.c", "tv" ),
( "stream/rar.c" ),
( "stream/stream.c" ),
( "stream/stream_avdevice.c" ),
( "stream/stream_bluray.c", "libbluray" ),
( "stream/stream_cb.c" ),
( "stream/stream_cdda.c", "cdda" ),
( "stream/stream_dvb.c", "dvbin" ),
( "stream/stream_dvd.c", "dvdread-common" ),
( "stream/stream_dvd_common.c", "dvdread-common" ),
( "stream/stream_dvdnav.c", "dvdnav" ),
( "stream/stream_edl.c" ),
( "stream/stream_file.c" ),
( "stream/stream_lavf.c" ),
@ -377,12 +361,6 @@ def build(ctx):
( "stream/stream_memory.c" ),
( "stream/stream_mf.c" ),
( "stream/stream_null.c" ),
( "stream/stream_rar.c" ),
( "stream/stream_smb.c", "libsmbclient" ),
( "stream/stream_tv.c", "tv" ),
( "stream/tv.c", "tv" ),
( "stream/tvi_dummy.c", "tv" ),
( "stream/tvi_v4l2.c", "tv-v4l2"),
## Subtitles
( "sub/ass_mp.c", "libass"),