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:
parent
559a400ac3
commit
8f4844b35c
@ -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.
|
||||
|
@ -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
|
||||
-------------------------
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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, ¶ms, 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,
|
||||
};
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
267
demux/demux_tv.c
267
demux/demux_tv.c
@ -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,
|
||||
};
|
@ -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"),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
388
player/command.c
388
player/command.c
@ -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,
|
||||
|
@ -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, "");
|
||||
|
@ -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(¶ms);
|
||||
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;
|
||||
}
|
153
stream/ai_oss.c
153
stream/ai_oss.c
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 */
|
@ -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;
|
||||
}
|
@ -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 */
|
186
stream/dvbin.h
186
stream/dvbin.h
@ -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 */
|
1212
stream/frequencies.c
1212
stream/frequencies.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
451
stream/rar.c
451
stream/rar.c
@ -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;
|
||||
|
||||
}
|
61
stream/rar.h
61
stream/rar.h
@ -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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 },
|
||||
};
|
@ -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 },
|
||||
};
|
1281
stream/stream_dvb.c
1281
stream/stream_dvb.c
File diff suppressed because it is too large
Load Diff
@ -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 },
|
||||
};
|
@ -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;
|
||||
}
|
@ -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 */
|
@ -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 },
|
||||
};
|
@ -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 },
|
||||
};
|
@ -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?
|
||||
};
|
@ -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 },
|
||||
};
|
986
stream/tv.c
986
stream/tv.c
@ -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;
|
||||
}
|
285
stream/tv.h
285
stream/tv.h
@ -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 */
|
@ -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 */
|
@ -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;
|
||||
}
|
1786
stream/tvi_v4l2.c
1786
stream/tvi_v4l2.c
File diff suppressed because it is too large
Load Diff
72
wscript
72
wscript
@ -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
|
||||
|
@ -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"),
|
||||
|
Loading…
Reference in New Issue
Block a user