Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca8dc9147b | ||
|
|
df8b99490c | ||
|
|
c78142a973 | ||
|
|
a8e6caf56b | ||
|
|
e6cca1e43b | ||
|
|
d7538d3597 | ||
|
|
f3eb5b143c | ||
|
|
9993fd8888 | ||
|
|
d9c3a51d87 | ||
|
|
619fa761af | ||
|
|
717d729dcb | ||
|
|
4eb5b62e5a | ||
|
|
b9e32d0d3d | ||
|
|
28e2b95f58 | ||
|
|
491f633a20 | ||
|
|
eb5e45855a | ||
|
|
59e55bb0e1 | ||
|
|
9f46be7373 | ||
|
|
3623fb38ee | ||
|
|
1ad9e322e3 | ||
|
|
cad63810c5 | ||
|
|
da17c64c76 | ||
|
|
57f32f7ca8 | ||
|
|
3d85137495 | ||
|
|
4aff0076a0 | ||
|
|
70f8ea228d | ||
|
|
3196f4cdd7 | ||
|
|
4fa0853c34 | ||
|
|
6598b55c35 | ||
|
|
848d7979e5 | ||
|
|
13301daf6a | ||
|
|
763a38f889 | ||
|
|
ed1e4de452 | ||
|
|
0e2fe55fde | ||
|
|
22cfb48b52 | ||
|
|
4a07120f83 | ||
|
|
d06ff691ed | ||
|
|
34cee2a77d | ||
|
|
8f5eb7b780 | ||
|
|
b991c072f6 | ||
|
|
3c2777994b | ||
|
|
80741f27f3 | ||
|
|
3d18ab77b3 | ||
|
|
8d2baaa318 | ||
|
|
2758bc462d | ||
|
|
2687b43dd0 | ||
|
|
bb40daf367 | ||
|
|
cc1f926441 | ||
|
|
67f653324a | ||
|
|
6e2c29b786 | ||
|
|
e565217f22 | ||
|
|
144c67efaf | ||
|
|
96d7e80ddf | ||
|
|
dde72c5c54 | ||
|
|
577d03da0c | ||
|
|
fc510f0da8 | ||
|
|
8d5775a8d2 | ||
|
|
cf8ff2fd87 | ||
|
|
852c924a60 | ||
|
|
1685d198b9 | ||
|
|
1e2d28f567 | ||
|
|
89973ebca1 | ||
|
|
3f4e528f73 | ||
|
|
6f7c9f516e | ||
|
|
33e0074603 | ||
|
|
b6ed94fa7c | ||
|
|
a419703549 | ||
|
|
5171da86e3 | ||
|
|
7bd670949c | ||
|
|
421b2be348 | ||
|
|
b659476ed9 | ||
|
|
689c3f47a8 | ||
|
|
677d4615ba | ||
|
|
5f788a7812 | ||
|
|
dba25e13c0 | ||
|
|
623b97e45a | ||
|
|
7a645ba62f | ||
|
|
63afe3eb98 | ||
|
|
72c70eb05b | ||
|
|
df0ec55499 | ||
|
|
542bcdfc9d | ||
|
|
218667b5a2 | ||
|
|
13a0e34aec | ||
|
|
76b7a1a713 | ||
|
|
6d6eee2e70 | ||
|
|
89f20cd2f3 | ||
|
|
cce2b675ac | ||
|
|
c1ed4dc242 | ||
|
|
712657a894 | ||
|
|
496956de3d | ||
|
|
483e703793 | ||
|
|
717cdeb95d | ||
|
|
315f3a9218 | ||
|
|
d857722618 | ||
|
|
6d9d597b10 | ||
|
|
c4d905b567 | ||
|
|
e4c69376fc | ||
|
|
842689bbf6 | ||
|
|
5ab2aa84e0 | ||
|
|
229d398ba9 | ||
|
|
5f1b155307 | ||
|
|
241cbe5065 | ||
|
|
76d771a6fa | ||
|
|
19caba0794 | ||
|
|
391546c52f | ||
|
|
a8af6a7852 | ||
|
|
960aca3386 | ||
|
|
e72a327d74 | ||
|
|
87f955f3e9 | ||
|
|
5536ebe82f | ||
|
|
ef8b37ca2a | ||
|
|
d4a5513847 | ||
|
|
d42212cd67 | ||
|
|
674cc1d3a9 | ||
|
|
c47bb9ca42 | ||
|
|
938dad0b88 | ||
|
|
82b5361946 | ||
|
|
f99f48684b | ||
|
|
25864a5539 | ||
|
|
524d9585fd | ||
|
|
95a72baf92 | ||
|
|
48112a74ed | ||
|
|
8427cdc717 | ||
|
|
b695ca1e27 | ||
|
|
801feb6420 | ||
|
|
de6a452039 | ||
|
|
4b764b6cc6 | ||
|
|
7b7c31b190 | ||
|
|
70a66b461d | ||
|
|
844e8f2b51 | ||
|
|
3f4ed8920f | ||
|
|
7ad2999b5f | ||
|
|
14fdbe07ac | ||
|
|
61ed9a8747 | ||
|
|
0801345d68 | ||
|
|
5bd0fe5daf | ||
|
|
cab3c5fbb8 | ||
|
|
99ceae8cc3 | ||
|
|
ebe04ab692 | ||
|
|
808aa08cc0 | ||
|
|
2065e0ba36 | ||
|
|
3c43a022af | ||
|
|
02513cd5df | ||
|
|
4d986d2a98 | ||
|
|
ada6f96811 | ||
|
|
321b3952b0 | ||
|
|
d49b49ff96 | ||
|
|
f088abb2bf | ||
|
|
4a3975bf7e | ||
|
|
a064143899 | ||
|
|
f264d52099 | ||
|
|
74a3c169b5 | ||
|
|
34d9787fcf | ||
|
|
1c0b80d518 | ||
|
|
a9bafeb589 | ||
|
|
3a33db3d92 | ||
|
|
d276f753f7 | ||
|
|
c06000eef8 | ||
|
|
1bc1cd2305 | ||
|
|
a735a2abd7 | ||
|
|
112c1abe31 | ||
|
|
362b35b631 | ||
|
|
49cf0af294 | ||
|
|
1b26874710 | ||
|
|
8fe636da7a | ||
|
|
812f149585 | ||
|
|
635424c8f0 | ||
|
|
2319918721 | ||
|
|
9fabf85a44 | ||
|
|
54fd5d0979 | ||
|
|
d141d281ca | ||
|
|
ebc3d74b04 | ||
|
|
a63fab8921 | ||
|
|
4ee8d8d3f9 | ||
|
|
574a2484c1 | ||
|
|
f5b92bee5e | ||
|
|
ca3d7144db | ||
|
|
86e7c3defb | ||
|
|
0fe1385d80 | ||
|
|
f4e9371d0f | ||
|
|
909185d49e | ||
|
|
c18c324c6f |
No files matched your search
@@ -0,0 +1,139 @@
|
||||
Coding style
|
||||
============
|
||||
|
||||
mpv uses C99 with K&R formatting, with some exceptions.
|
||||
|
||||
General formatting
|
||||
------------------
|
||||
|
||||
- Use the K&R indent style.
|
||||
- Use 4 spaces of indentation, never use tabs (except in Makefiles).
|
||||
- Add a single space between keywords and binary operators. There are some other
|
||||
cases where spaces should be added. Example:
|
||||
|
||||
```C
|
||||
if ((a * b) > c) {
|
||||
// code
|
||||
some_function(a, b, c);
|
||||
}
|
||||
```
|
||||
- Break lines on 80 columns. There is a hard limit of 85 columns. You may ignore
|
||||
this limit if there's a strong case that not breaking the line will increase
|
||||
readability. Going over 85 columns might provoke endless discussions about
|
||||
whether such a limit is needed or not, so avoid it.
|
||||
- If the body of an if/for/while statement has more than 1 physical lines, then
|
||||
always add braces, even if they're technically redundant.
|
||||
|
||||
Bad:
|
||||
|
||||
```C
|
||||
if (a)
|
||||
// do something if b
|
||||
if (b)
|
||||
do_something();
|
||||
```
|
||||
|
||||
Good:
|
||||
|
||||
```C
|
||||
if (a) {
|
||||
// do something if b
|
||||
if (b)
|
||||
do_something();
|
||||
}
|
||||
```
|
||||
- If the body of an if statement uses braces, the else branch should also
|
||||
use braces (and reverse).
|
||||
|
||||
Example:
|
||||
|
||||
```C
|
||||
if (a) {
|
||||
// do something
|
||||
something();
|
||||
something_else();
|
||||
} else {
|
||||
one_line();
|
||||
}
|
||||
```
|
||||
- If an if condition spans multiple physical lines, then put the opening brace
|
||||
for the if body on the next physical line. (Also, preferably always add a
|
||||
brace, even if technically none is needed.)
|
||||
|
||||
Example:
|
||||
|
||||
```C
|
||||
if (very_long_condition_a &&
|
||||
very_long_condition_b)
|
||||
{
|
||||
code();
|
||||
}
|
||||
```
|
||||
- Remove any trailing whitespace.
|
||||
- If the file you're editing uses formatting different from from what is
|
||||
described here, it's probably an old file from times when nobody followed a
|
||||
consistent style. You're free to use the existing style, or the new style, or
|
||||
to send a patch to reformat the file to the new style before making functional
|
||||
changes.
|
||||
|
||||
General coding
|
||||
--------------
|
||||
|
||||
- Use C99. Also freely make use of C99 features if it's appropriate, such as
|
||||
stdbool.h.
|
||||
- Don't use GNU-only features. In some cases they may be warranted, if they
|
||||
are optional (such as attributes enabling printf-like format string checks).
|
||||
But in general, standard C99 should be used.
|
||||
- The same applies to libc functions. We have to be Windows-compatible too. Use
|
||||
functions guaranteed by C99 or POSIX only, unless your use is guarded by a
|
||||
configure check.
|
||||
- Prefer fusing declaration and initialization, rather than putting declarations
|
||||
on the top of a block. Obvious data flow is more important than avoiding
|
||||
mixing declarations and statements, which is just a C90 artifact.
|
||||
- tech-overview.txt might help to get an overview how mpv is structured.
|
||||
- If you add features that require intrusive changes, discuss them on the dev
|
||||
channel first. There might be a better way to add a feature and it can avoid
|
||||
wasted work.
|
||||
|
||||
Sending patches
|
||||
---------------
|
||||
|
||||
- Make a github pull request, or send a link to a plaintext patch created with
|
||||
``git format-patch``. diffs posted as pastebins (especially if the http link
|
||||
returns HTML) just cause extra work for everyone, because they lack commit
|
||||
message and authorship information.
|
||||
- Write informative commit messages. Use present tense to describe the
|
||||
situation with the patch applied, and past tense for the situation before
|
||||
the change.
|
||||
- The subject line (the first line in a commit message) should contain a
|
||||
prefix identifying the sub system, followed by a short description what
|
||||
impact this commit has. This subject line shouldn't be longer than 72
|
||||
characters, because it messes up the output of many git tools otherwise.
|
||||
|
||||
For example, you fixed a crash in af_volume.c:
|
||||
|
||||
Bad: ``fixed the bug (wtf?)``
|
||||
Good: ``af_volume: fix crash due to null pointer access``
|
||||
|
||||
Having a prefix gives context, and is especially useful when trying to find
|
||||
a specific change by looking at the history, or when running ``git blame``.
|
||||
- The body of the commit message (everything else after the subject line) should
|
||||
be as informative as possible and contain everything that isn't obvious. Don't
|
||||
hesitate to dump as much information as you can - it doesn't cost you
|
||||
anything. Put some effort into it. If someone finds a bug months or years
|
||||
later, and finds that it's caused by your commit (even though your commit was
|
||||
supposed to fix another bug), it would be bad if there wasn't enough
|
||||
information to test the original bug. The old bug might be reintroduced while
|
||||
fixing the new bug.
|
||||
|
||||
The commit message should be wrapped on 72 characters per line, because git
|
||||
tools usually do not break text automatically. On the other hand, you do not
|
||||
need to break text that would be unnatural to break (like data for test cases,
|
||||
or long URLs).
|
||||
|
||||
Important: put an empty line between the subject line and the commit message.
|
||||
If this is missing, it will break display in common git tools.
|
||||
- Try to separate cosmetic and functional changes. It's ok to make a few
|
||||
additional cosmetic changes in the same file you're working on, but don't do
|
||||
something like reformatting a whole file and hiding an actual functional
|
||||
change in the same commit.
|
||||
@@ -9,6 +9,8 @@ Using mingw-w64-cmake to setup a MinGW-w64 environment is recommended (this will
|
||||
also build mpv and its dependencies): https://github.com/lachs0r/mingw-w64-cmake
|
||||
|
||||
Alternatively, use MXE: http://mxe.cc
|
||||
With MXE, you have to modify the file settings.mk to target MinGW-w64 (even if
|
||||
you compile to 32 bit).
|
||||
|
||||
Warning: the original MinGW (http://www.mingw.org) is unsupported.
|
||||
|
||||
|
||||
+1
-1
@@ -134,9 +134,9 @@ What works
|
||||
* Deinterlacing/Inverse Telecine with any of mpv's filters for that
|
||||
* Audio file converting: mpv -o outfile.mp3 infile.flac -no-video -oac
|
||||
libmp3lame -oacopts ab=320k
|
||||
* inverse telecine filters (confirmed working: detc, pullup, filmdint)
|
||||
|
||||
What does not work yet
|
||||
======================
|
||||
|
||||
* 3-pass encoding (ensuring constant total size and bitrate constraints while
|
||||
having VBR audio; mencoder calls this "frameno")
|
||||
|
||||
+5
-5
@@ -147,7 +147,7 @@ Available filters are:
|
||||
0 no matrix decoding (default)
|
||||
==== ===================================
|
||||
|
||||
``equalizer=[g1:g2:g3:...:g10]``
|
||||
``equalizer=g1:g2:g3:...:g10``
|
||||
10 octave band graphic equalizer, implemented using 10 IIR band-pass
|
||||
filters. This means that it works regardless of what type of audio is
|
||||
being played back. The center frequencies for the 10 bands are:
|
||||
@@ -266,7 +266,7 @@ Available filters are:
|
||||
Filter for internal use only. Converts between signed/unsigned formats
|
||||
and formats with different endian.
|
||||
|
||||
``volume[=volumedb[:softclip[:s16]]]``
|
||||
``volume[=<volumedb>[:...]]``
|
||||
Implements software volume control. Use this filter with caution since it
|
||||
can reduce the signal to noise ratio of the sound. In most cases it is
|
||||
best to use the *Master* volume control of your sound card or the volume
|
||||
@@ -279,14 +279,14 @@ Available filters are:
|
||||
Sets the desired gain in dB for all channels in the stream from -200dB
|
||||
to +60dB, where -200dB mutes the sound completely and +60dB equals a
|
||||
gain of 1000 (default: 0).
|
||||
``<softclip>``
|
||||
``softclip``
|
||||
Turns soft clipping on. Soft-clipping can make the
|
||||
sound more smooth if very high volume levels are used. Enable this
|
||||
option if the dynamic range of the loudspeakers is very low.
|
||||
|
||||
*WARNING*: This feature creates distortion and should be considered a
|
||||
last resort.
|
||||
``<s16>``
|
||||
``s16``
|
||||
Force S16 sample format if set. Lower quality, but might be faster
|
||||
in some situations.
|
||||
|
||||
@@ -296,7 +296,7 @@ Available filters are:
|
||||
Would amplify the sound by 10.1dB and hard-clip if the sound level
|
||||
is too high.
|
||||
|
||||
``pan=n[:<matrix>]``
|
||||
``pan=n:[<matrix>]``
|
||||
Mixes channels arbitrarily. Basically a combination of the volume and the
|
||||
channels filter that can be used to down-mix many channels to only a few,
|
||||
e.g. stereo to mono, or vary the "width" of the center speaker in a
|
||||
|
||||
+25
-1
@@ -18,7 +18,12 @@ normal driver parameters.
|
||||
|
||||
.. note::
|
||||
|
||||
See ``--ao=help`` for a list of compiled-in audio output drivers.
|
||||
See ``--ao=help`` for a list of compiled-in audio output drivers. The
|
||||
driver ``--ao=alsa`` is preferred. ``--ao=pulse`` is preferred on systems
|
||||
where PulseAudio is used. On Windows, ``--ao=wasapi`` is preferred,
|
||||
though it might cause trouble sometimes, in which case ``--ao=dsound``
|
||||
should be used. On BSD systems, ``--ao=oss`` or `--ao=sndio`` may work
|
||||
(the latter being experimental). On OSX systems, use ``--ao=coreaudio``.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
@@ -118,6 +123,9 @@ Available audio output drivers are:
|
||||
``openal``
|
||||
Experimental OpenAL audio output driver
|
||||
|
||||
.. note:: This driver is not very useful. Playing multi-channel audio with
|
||||
it is slow.
|
||||
|
||||
``pulse``
|
||||
PulseAudio audio output driver
|
||||
|
||||
@@ -130,6 +138,10 @@ Available audio output drivers are:
|
||||
PortAudio audio output driver. This works on all platforms, and has
|
||||
extensive MS Windows support.
|
||||
|
||||
.. note:: This driver is not very useful. It was added in the hope of
|
||||
providing portable audio API across all platforms, but turned
|
||||
out semi-broken and underfeatured.
|
||||
|
||||
``device``
|
||||
Specify the subdevice to use. Giving ``help`` as device name lists all
|
||||
devices found by PortAudio. Devices can be given as numeric values,
|
||||
@@ -138,6 +150,8 @@ Available audio output drivers are:
|
||||
``dsound`` (Windows only)
|
||||
DirectX DirectSound audio output driver
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``device=<devicenum>``
|
||||
Sets the device number to use. Playing a file with ``-v`` will show a
|
||||
list of available devices.
|
||||
@@ -147,6 +161,10 @@ Available audio output drivers are:
|
||||
1.2, but may require the ``SDL_AUDIODRIVER`` environment variable to be set
|
||||
appropriately for your system.
|
||||
|
||||
.. note:: This driver is for compatibility with extremely foreign
|
||||
environments, such as systems where none of the other drivers
|
||||
are available.
|
||||
|
||||
``buflen=<length>``
|
||||
Sets the audio buffer length in seconds. Is used only as a hint by the
|
||||
sound system. Playing a file with ``-v`` will show the requested and
|
||||
@@ -186,6 +204,10 @@ Available audio output drivers are:
|
||||
``rsound``
|
||||
Audio output to an RSound daemon
|
||||
|
||||
.. note:: Completely useless, unless you intend to run RSound. Not to be
|
||||
confused with RoarAudio, which is something completely
|
||||
different.
|
||||
|
||||
``host=<name/path>``
|
||||
Set the address of the server (default: localhost). Can be either a
|
||||
network hostname for TCP connections or a Unix domain socket path
|
||||
@@ -197,6 +219,8 @@ Available audio output drivers are:
|
||||
``sndio``
|
||||
Audio output to the OpenBSD sndio sound system
|
||||
|
||||
.. note:: Experimental. There are known bugs and issues.
|
||||
|
||||
(Note: only supports mono, stereo, 4.0, 5.1 and 7.1 channel
|
||||
layouts.)
|
||||
|
||||
|
||||
@@ -52,11 +52,6 @@ You can bind multiple commands to one key. For example:
|
||||
|
||||
| a show_text "command 1" ; show_text "command 2"
|
||||
|
||||
Note that some magic is disabled for keys: seek commands inside lists are not
|
||||
coalesced (seeking will appear slower), and no check is done for abort commands
|
||||
(so these commands can't be used to abort playback if the network cache is
|
||||
stuck).
|
||||
|
||||
List of Input Commands
|
||||
----------------------
|
||||
|
||||
|
||||
+31
-8
@@ -103,6 +103,13 @@ OPTIONS
|
||||
Override movie aspect ratio, in case aspect information is incorrect or
|
||||
missing in the file being played. See also ``--no-aspect``.
|
||||
|
||||
Two values have special meaning:
|
||||
|
||||
:0: disable aspect ratio handling, pretend the video has square pixels
|
||||
:-1: use the video stream or container aspect (default)
|
||||
|
||||
But note that handling of these special values might change in the future.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
- ``--aspect=4:3`` or ``--aspect=1.3333``
|
||||
@@ -789,7 +796,7 @@ OPTIONS
|
||||
|
||||
``--field-dominance=<auto|top|bottom>``
|
||||
Set first field for interlaced content. Useful for deinterlacers that
|
||||
double the framerate: ``--vf=yadif=1`` and ``--vo=vdpau:deint``.
|
||||
double the framerate: ``--vf=yadif=field`` and ``--vo=vdpau:deint``.
|
||||
|
||||
:auto: (default) If the decoder does not export the appropriate
|
||||
information, it falls back on ``top`` (top field first).
|
||||
@@ -1142,7 +1149,12 @@ OPTIONS
|
||||
Allow hardware decoding for a given list of codecs only. The special value
|
||||
``all`` always allows all codecs.
|
||||
|
||||
By default this is set to ``h264,vc1,wmv3``.
|
||||
You can get the list of allowed codecs with ``mpv --vd=help``. Remove the
|
||||
prefix, e.g. instead of ``lavc:h264`` use ``h264``.
|
||||
|
||||
By default this is set to ``h264,vc1,wmv3``. Note that the hardware
|
||||
acceleration special codecs like ``h264_vdpau`` are not relevant anymore,
|
||||
and in fact have been removed from Libav in this form.
|
||||
|
||||
This is usually only needed with broken GPUs, where a codec is reported
|
||||
as supported, but decoding causes more problems than it solves.
|
||||
@@ -1546,10 +1558,10 @@ OPTIONS
|
||||
``--osd-level=<0-3>``
|
||||
Specifies which mode the OSD should start in.
|
||||
|
||||
:0: subtitles only
|
||||
:1: volume + seek (default)
|
||||
:2: volume + seek + timer + percentage
|
||||
:3: volume + seek + timer + percentage + total time
|
||||
:0: OSD completely disabled (subtitles only)
|
||||
:1: enabled (shows up only on user interaction)
|
||||
:2: enabled + current time visible by default
|
||||
:3: enabled + ``--osd-status-msg`` (current time and status by default)
|
||||
|
||||
``--osd-margin-x=<size>, --sub-text-margin-x=<size>``
|
||||
Left and right screen margin for the OSD/subs in scaled pixels (see
|
||||
@@ -1743,6 +1755,15 @@ OPTIONS
|
||||
|
||||
Also see ``--really-quiet`` and ``--msglevel``.
|
||||
|
||||
``--quvi-fetch-subtitles=<yes|no>``
|
||||
Toggles fetching of subtitles from streaming sites with libquvi. Disabled
|
||||
by default, because it's unreliable and slow. Note that when enabled,
|
||||
subtitles will always be fetched, even if subtitles are explicitly
|
||||
disabled with ``--no-sub`` (because you might want to enable subtitles
|
||||
at runtime).
|
||||
|
||||
Supported when using libquvi 0.9.x.
|
||||
|
||||
``--quvi-format=<best|default|...>``
|
||||
Video format/quality that is directly passed to libquvi (default: ``best``).
|
||||
This is used when opening links to streaming sites like YouTube. The
|
||||
@@ -1761,7 +1782,7 @@ OPTIONS
|
||||
libquvi 0.9.x:
|
||||
|
||||
The following explanations are relevant:
|
||||
`<http://quvi.sourceforge.net/doc/0.9/glossary_termino.html#m_stream_id>`_
|
||||
`<http://quvi.sourceforge.net/r/api/0.9/glossary_termino.html#m_stream_id>`_
|
||||
|
||||
The ``quvi-format`` property can be used at runtime to cycle through the
|
||||
list of formats. Unfortunately, this is slow. On libquvi 0.4.x, this
|
||||
@@ -2366,7 +2387,7 @@ OPTIONS
|
||||
Some ``--sws`` options are tunable. The description of the ``scale``
|
||||
video filter has further information.
|
||||
|
||||
``--term-osd, --no-term-osd``
|
||||
``--term-osd, --no-term-osd``, ``--term-osd=force``
|
||||
Display OSD messages on the console when no video output is available.
|
||||
Enabled by default.
|
||||
|
||||
@@ -2375,6 +2396,8 @@ OPTIONS
|
||||
console. The escape sequence should move the pointer to the beginning of
|
||||
the line used for the OSD and clear it (default: ``^[[A\r^[[K``).
|
||||
|
||||
``force`` enables terminal OSD even if a video window is created.
|
||||
|
||||
``--title=<string>``
|
||||
Set the window title. Properties are expanded on playback start.
|
||||
(See `Property Expansion`_.)
|
||||
|
||||
+3
-3
@@ -98,7 +98,7 @@ Available filters are:
|
||||
:2: Rotate by 90 degrees counterclockwise.
|
||||
:3: Rotate by 90 degrees counterclockwise and flip.
|
||||
|
||||
``scale[=w:h[:interlaced[:chr_drop[:par[:par2[:noup[:arnd]]]]]]]``
|
||||
``scale[=w:h[:interlaced[:chr-drop[:param[:param2[:noup[:arnd]]]]]]]``
|
||||
Scales the image with the software scaler (slow) and performs a YUV<->RGB
|
||||
colorspace conversion (see also ``--sws``).
|
||||
|
||||
@@ -114,7 +114,7 @@ Available filters are:
|
||||
:-(n+8): Like -n above, but rounding the dimension to the closest
|
||||
multiple of 16.
|
||||
|
||||
``<chr_drop>``
|
||||
``<chr-drop>``
|
||||
chroma skipping
|
||||
|
||||
:0: Use all available input lines for chroma.
|
||||
@@ -122,7 +122,7 @@ Available filters are:
|
||||
:2: Use only every 4. input line for chroma.
|
||||
:3: Use only every 8. input line for chroma.
|
||||
|
||||
``<par>[:<par2>]`` (see also ``--sws``)
|
||||
``<param>[:<param2>]`` (see also ``--sws``)
|
||||
Set some scaling parameters depending on the type of scaler selected
|
||||
with ``--sws``::
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ normal driver parameters.
|
||||
|
||||
See ``--vo=help`` for a list of compiled-in video output drivers.
|
||||
|
||||
The recommended output drivers are ``--vo=vdpau`` and ``--vo=opengl-hq``.
|
||||
All other drivers are just for compatibility or special purposes.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``--vo=opengl,xv,``
|
||||
@@ -32,6 +35,8 @@ Available video output drivers are:
|
||||
the most compatible VO on X, but may be low-quality, and has issues with
|
||||
OSD and subtitle display.
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``adaptor=<number>``
|
||||
Select a specific XVideo adaptor (check xvinfo results).
|
||||
``port=<number>``
|
||||
@@ -172,6 +177,9 @@ Available video output drivers are:
|
||||
``direct3d_shaders`` (Windows only)
|
||||
Video output driver that uses the Direct3D interface.
|
||||
|
||||
.. note:: This driver is for compatibility with systems that don't provide
|
||||
proper OpenGL drivers.
|
||||
|
||||
``prefer-stretchrect``
|
||||
Use ``IDirect3DDevice9::StretchRect`` over other methods if possible.
|
||||
|
||||
@@ -228,11 +236,15 @@ Available video output drivers are:
|
||||
Same as ``direct3d_shaders``, but with the options ``disable-textures``
|
||||
and ``disable-shaders`` forced.
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``corevideo`` (Mac OS X 10.6 and later)
|
||||
Mac OS X CoreVideo video output driver. Uses the CoreVideo APIs to fill
|
||||
PixelBuffers and generate OpenGL textures from them (useful as a fallback
|
||||
for ``opengl``).
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``opengl``
|
||||
OpenGL video output driver. It supports extended scaling methods, dithering
|
||||
and color management.
|
||||
@@ -519,6 +531,8 @@ Available video output drivers are:
|
||||
implementation, even if you do not get any error message. Use ``glxinfo``
|
||||
or a similar tool to display the supported OpenGL extensions.
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``(no-)ati-hack``
|
||||
ATI drivers may give a corrupted image when PBOs are used (when using
|
||||
``force-pbo``). This option fixes this, at the expense of using a bit
|
||||
@@ -692,6 +706,9 @@ Available video output drivers are:
|
||||
hardware acceleration. Should work on all platforms supported by SDL 2.0.
|
||||
For tuning, refer to your copy of the file ``SDL_hints.h``.
|
||||
|
||||
.. note:: This driver is for compatibility with systems that don't provide
|
||||
proper graphics drivers, or which support GLES only.
|
||||
|
||||
``sw``
|
||||
Continue even if a software renderer is detected.
|
||||
|
||||
@@ -704,6 +721,9 @@ Available video output drivers are:
|
||||
hardware decoding to save power on laptops, or possibly preventing video
|
||||
tearing with some setups.
|
||||
|
||||
.. note:: This driver is for compatibility with crappy systems. You can
|
||||
use vaapi hardware decoding with ``--vo=opengl`` too.
|
||||
|
||||
``scaling=<algorithm>``
|
||||
default
|
||||
Driver default (mpv default as well).
|
||||
@@ -744,6 +764,8 @@ Available video output drivers are:
|
||||
``caca``
|
||||
Color ASCII art video output driver that works on a text console.
|
||||
|
||||
.. note:: This driver is a joke.
|
||||
|
||||
``image``
|
||||
Output each frame into an image file in the current directory. Each file
|
||||
takes the frame number padded with leading zeros as name.
|
||||
@@ -789,6 +811,9 @@ Available video output drivers are:
|
||||
``wayland`` (Wayland only)
|
||||
Wayland shared memory video output as fallback for ``opengl``.
|
||||
|
||||
.. note:: This driver is for compatibility with systems that don't provide
|
||||
working OpenGL drivers.
|
||||
|
||||
``default-format``
|
||||
Use the default RGB32 format instead of an auto-detected one.
|
||||
|
||||
|
||||
@@ -45,13 +45,24 @@ Essential dependencies (incomplete list):
|
||||
- gcc or clang
|
||||
- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
|
||||
- Audio output development headers (libasound, pulseaudio)
|
||||
- fribidi, freetype, fontconfig development headers (for libass)
|
||||
- libass
|
||||
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libpostproc)
|
||||
- libjpeg
|
||||
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavresample)
|
||||
At least FFmpeg 1.2.4 or Libav 9.10 is required. (If FFmpeg was compiled
|
||||
without libavresample, at least FFmpeg 2.0 is required.)
|
||||
- libjpeg (for screenshots)
|
||||
- libquvi if you want to play Youtube videos directly
|
||||
|
||||
Libass dependencies:
|
||||
|
||||
- gcc or clang, yasm on x86 and x86_64
|
||||
- fribidi, freetype, fontconfig development headers (for libass)
|
||||
|
||||
FFmpeg dependencies:
|
||||
|
||||
- gcc or clang, yasm on x86 and x86_64
|
||||
- OpenSSL (has to be explicitly enabled when compiling ffmpeg)
|
||||
- libx264/libmp3lame/libfdk-aac if you want to use encoding (has to be
|
||||
explicitly enabled when compiling ffmpeg)
|
||||
- Libav also works, but some features will not work. (See section below.)
|
||||
|
||||
Most of the above libraries are available in suitable versions on normal
|
||||
Linux distributions. However FFmpeg is an exception (distro versions may be
|
||||
|
||||
@@ -132,7 +132,8 @@ sub _bin2int($) {
|
||||
# creates a floating-point number with the given mantissa and exponent
|
||||
sub _ldexp {
|
||||
my ($mantissa, $exponent) = @_;
|
||||
return $mantissa * Math::BigRat->new(2)**$exponent;
|
||||
my $r = new Math::BigRat($mantissa);
|
||||
return $r * Math::BigRat->new(2)**$exponent;
|
||||
}
|
||||
|
||||
# NOTE: the read_* functions are hard to read because they're ports
|
||||
@@ -252,25 +253,27 @@ bytes from the internal filehandle.
|
||||
Only lengths C<4> and C<8> are supported (C C<float> and C<double>).
|
||||
|
||||
=cut
|
||||
sub read_float {
|
||||
my ($self, $length) = @_;
|
||||
my $i = $self->read_uint($length);
|
||||
my $f;
|
||||
{
|
||||
my $b1 = new Math::BigInt 1;
|
||||
|
||||
use bigrat try => BIGINT_TRY;
|
||||
sub read_float {
|
||||
my ($self, $length) = @_;
|
||||
my $i = new Math::BigInt $self->read_uint($length)->bstr;
|
||||
my $f;
|
||||
|
||||
# These evil expressions reinterpret an unsigned int as IEEE binary floats
|
||||
if ($length == 4) {
|
||||
$f = _ldexp(($i & (1<<23 - 1)) + (1<<23), ($i>>23 & (1<<8 - 1)) - 150);
|
||||
$f = -$f if $i & (1<<31);
|
||||
} elsif ($length == 8) {
|
||||
$f = _ldexp(($i & (1<<52 - 1)) + (1<<52), ($i>>52 & (1<<12 - 1)) - 1075);
|
||||
$f = -$f if $i & (1<<63);
|
||||
} else {
|
||||
croak "Matroska Syntax error: unsupported IEEE float byte size $length";
|
||||
# These evil expressions reinterpret an unsigned int as IEEE binary floats
|
||||
if ($length == 4) {
|
||||
$f = _ldexp(($i & ((1<<23) - 1)) + (1<<23), ($i>>23 & ((1<<8) - 1)) - 150);
|
||||
$f = -$f if $i & ($b1<<31);
|
||||
} elsif ($length == 8) {
|
||||
$f = _ldexp(($i & (($b1<<52) - 1)) + ($b1<<52), ($i>>52 & ((1<<12) - 1)) - 1075);
|
||||
$f = -$f if $i & ($b1<<63);
|
||||
} else {
|
||||
croak "Matroska Syntax error: unsupported IEEE float byte size $length";
|
||||
}
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
=method read_ebml_id($length)
|
||||
|
||||
+48
-17
@@ -3,20 +3,18 @@
|
||||
"""
|
||||
This script emulates "unique application" functionality on Linux. When starting
|
||||
playback with this script, it will try to reuse an already running instance of
|
||||
mpv (but only if that was started with umpv).
|
||||
mpv (but only if that was started with umpv). Other mpv instances (not started
|
||||
by umpv) are ignored, and the script doesn't know about them.
|
||||
|
||||
This script only uses mpv instances started with umpv. Other instances are
|
||||
ignored, and don't exist for the script.
|
||||
|
||||
This only takes filenames as arguments. If mpv is already running, they are
|
||||
appended to mpv's internal playlist. If the file does not exist or is otherwise
|
||||
not playable, mpv will skip the playlist entry when attempting to play it (from
|
||||
the GUI perspective, it's silently ignored).
|
||||
This only takes filenames as arguments. Custom options can't be used; the script
|
||||
interprets them as filenames. If mpv is already running, the files passed to
|
||||
umpv are appended to mpv's internal playlist. If a file does not exist or is
|
||||
otherwise not playable, mpv will skip the playlist entry when attempting to
|
||||
play it (from the GUI perspective, it's silently ignored).
|
||||
|
||||
If mpv isn't running yet, this script will start mpv and let it control the
|
||||
current terminal. If you play an audio-only file, mpv will not open a window
|
||||
(that is its normal behavior; but it might not be what a user expects if this
|
||||
script is used in a GUI setting).
|
||||
current terminal. It will not write output to stdout/stderr, because this
|
||||
will typically just fill ~/.xsession-errors with garbage.
|
||||
|
||||
mpv will terminate if there are no more files to play, and running the umpv
|
||||
script after that will start a new mpv instance.
|
||||
@@ -25,10 +23,18 @@ Note that you can control the mpv instance by writing to the command fifo:
|
||||
|
||||
echo "cycle fullscreen" > /tmp/umpv-fifo-*
|
||||
|
||||
Note: you can supply custom options with the UMPV_OPTIONS environment variable.
|
||||
The environment variable will be split on whitespace, and each item is
|
||||
passed as option to mpv _if_ the script starts mpv. If mpv is not started
|
||||
by the script (i.e. mpv is already running), the options will be ignored.
|
||||
|
||||
Warning:
|
||||
|
||||
The script attempts to make sure the FIFO is safely created (i.e. not world-
|
||||
readable, not trying to open a bogus
|
||||
writable), and checks that it's really a FIFO. This is important for security,
|
||||
because the FIFO allows anyone with write access to run arbitrary commands
|
||||
in mpv's context using the "run" input command. If you are worried about
|
||||
security, you should verify that the code handles these concerns correctly.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -37,13 +43,33 @@ import errno
|
||||
import subprocess
|
||||
import fcntl
|
||||
import stat
|
||||
import string
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("error: needs at least one file as argument.")
|
||||
if len(sys.argv) < 1:
|
||||
sys.exit(1)
|
||||
files = sys.argv[1:]
|
||||
|
||||
# this is about the same method mpv uses to decide this
|
||||
def is_url(filename):
|
||||
parts = filename.split(":", 1)
|
||||
if len(parts) < 2:
|
||||
return False
|
||||
# protocol prefix has no special characters => it's an URL
|
||||
prefix = parts[0]
|
||||
for c in prefix:
|
||||
if string.ascii_letters.find(c) < 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# make them absolute; also makes them safe against interpretation as options
|
||||
files = [os.path.abspath(f) for f in files]
|
||||
def make_abs(filename):
|
||||
if not is_url(filename):
|
||||
return os.path.abspath(filename)
|
||||
return filename
|
||||
files = [make_abs(f) for f in files]
|
||||
|
||||
opts_env = os.getenv("UMPV_OPTIONS")
|
||||
opts_env = opts_env.split() if opts_env else []
|
||||
|
||||
FIFO = "/tmp/umpv-fifo-" + os.getenv("USER")
|
||||
|
||||
@@ -86,6 +112,11 @@ else:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
os.mkfifo(FIFO, 0600)
|
||||
os.mkfifo(FIFO, int("0600", 8))
|
||||
|
||||
subprocess.check_call(["mpv", "--input-file=" + FIFO] + files)
|
||||
opts = ["mpv", "--really-quiet", "--force-window", "--input-file=" + FIFO]
|
||||
opts.extend(opts_env)
|
||||
opts.append("--")
|
||||
opts.extend(files)
|
||||
|
||||
subprocess.check_call(opts)
|
||||
+7
-2
@@ -173,10 +173,15 @@ void mp_audio_realloc(struct mp_audio *mpa, int samples)
|
||||
}
|
||||
|
||||
// Like mp_audio_realloc(), but only reallocate if the audio grows in size.
|
||||
// If the buffer is reallocated, also preallocate.
|
||||
void mp_audio_realloc_min(struct mp_audio *mpa, int samples)
|
||||
{
|
||||
if (samples > mp_audio_get_allocated_size(mpa))
|
||||
mp_audio_realloc(mpa, samples);
|
||||
if (samples > mp_audio_get_allocated_size(mpa)) {
|
||||
size_t alloc = ta_calc_prealloc_elems(samples);
|
||||
if (alloc > INT_MAX)
|
||||
abort(); // oom
|
||||
mp_audio_realloc(mpa, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the size allocated for the data, in number of samples. If the allocated
|
||||
|
||||
+1
-1
@@ -485,5 +485,5 @@ void mp_chmap_print_help(struct mp_log *log)
|
||||
std_layout_names[n][0], std_layout_names[n][1]);
|
||||
}
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
mp_info(log, " unknown%d\n", n);
|
||||
mp_info(log, " unknown%d\n", n + 1);
|
||||
}
|
||||
+4
-10
@@ -172,11 +172,8 @@ static void set_from_wf(AVCodecContext *avctx, MP_WAVEFORMATEX *wf)
|
||||
avctx->block_align = wf->nBlockAlign;
|
||||
avctx->bits_per_coded_sample = wf->wBitsPerSample;
|
||||
|
||||
if (wf->cbSize > 0) {
|
||||
avctx->extradata = av_mallocz(wf->cbSize + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
avctx->extradata_size = wf->cbSize;
|
||||
memcpy(avctx->extradata, wf + 1, avctx->extradata_size);
|
||||
}
|
||||
if (wf->cbSize > 0)
|
||||
mp_lavc_set_extradata(avctx, wf + 1, wf->cbSize);
|
||||
}
|
||||
|
||||
static int init(struct dec_audio *da, const char *decoder)
|
||||
@@ -244,11 +241,8 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
// demux_mkv, demux_mpg
|
||||
if (sh_audio->codecdata_len && sh_audio->codecdata &&
|
||||
!lavc_context->extradata) {
|
||||
lavc_context->extradata = av_malloc(sh_audio->codecdata_len +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
lavc_context->extradata_size = sh_audio->codecdata_len;
|
||||
memcpy(lavc_context->extradata, (char *)sh_audio->codecdata,
|
||||
lavc_context->extradata_size);
|
||||
mp_lavc_set_extradata(lavc_context, sh_audio->codecdata,
|
||||
sh_audio->codecdata_len);
|
||||
}
|
||||
|
||||
if (sh->lav_headers)
|
||||
|
||||
+18
-18
@@ -345,26 +345,28 @@ static int af_count_filters(struct af_stream *s)
|
||||
// (So we know what conversion filter with what format to insert next.)
|
||||
static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
|
||||
{
|
||||
#define NUM_FMT 64
|
||||
#define NUM_FILT 32
|
||||
#define NUM_NODES (NUM_FMT * NUM_FILT)
|
||||
#define MAX_NODES (64 * 32)
|
||||
int num_fmt = 0, num_filt = 0;
|
||||
for (int n = 0; filter_list[n]; n++)
|
||||
assert(n < NUM_FILT);
|
||||
num_filt = n + 1;
|
||||
for (int n = 0; af_fmtstr_table[n].format; n++)
|
||||
assert(n < NUM_FMT);
|
||||
num_fmt = n + 1;
|
||||
|
||||
bool visited[NUM_NODES] = {0};
|
||||
unsigned char distance[NUM_NODES];
|
||||
short previous[NUM_NODES] = {0};
|
||||
for (int n = 0; n < NUM_NODES; n++) {
|
||||
int num_nodes = num_fmt * num_filt;
|
||||
assert(num_nodes < MAX_NODES);
|
||||
|
||||
bool visited[MAX_NODES] = {0};
|
||||
unsigned char distance[MAX_NODES];
|
||||
short previous[MAX_NODES] = {0};
|
||||
for (int n = 0; n < num_nodes; n++) {
|
||||
distance[n] = 255;
|
||||
if (af_fmtstr_table[n % NUM_FMT].format == srcfmt)
|
||||
if (af_fmtstr_table[n % num_fmt].format == srcfmt)
|
||||
distance[n] = 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int next = -1;
|
||||
for (int n = 0; n < NUM_NODES; n++) {
|
||||
for (int n = 0; n < num_nodes; n++) {
|
||||
if (!visited[n] && (next < 0 || (distance[n] < distance[next])))
|
||||
next = n;
|
||||
}
|
||||
@@ -372,13 +374,13 @@ static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
|
||||
return NULL;
|
||||
visited[next] = true;
|
||||
|
||||
int fmt = next % NUM_FMT;
|
||||
int fmt = next % num_fmt;
|
||||
if (af_fmtstr_table[fmt].format == *dstfmt) {
|
||||
// Best match found
|
||||
for (int cur = next; cur >= 0; cur = previous[cur] - 1) {
|
||||
if (distance[cur] == 1) {
|
||||
*dstfmt = af_fmtstr_table[cur % NUM_FMT].format;
|
||||
return (char *)filter_list[cur / NUM_FMT]->name;
|
||||
*dstfmt = af_fmtstr_table[cur % num_fmt].format;
|
||||
return (char *)filter_list[cur / num_fmt]->name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -392,7 +394,7 @@ static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
|
||||
if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format,
|
||||
af_fmtstr_table[i].format))
|
||||
{
|
||||
int other = n * NUM_FMT + i;
|
||||
int other = n * num_fmt + i;
|
||||
int ndist = distance[next] + 1;
|
||||
if (ndist < distance[other]) {
|
||||
distance[other] = ndist;
|
||||
@@ -403,9 +405,7 @@ static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
#undef NUM_FMT
|
||||
#undef NUM_FILT
|
||||
#undef NODE_N
|
||||
#undef MAX_NODES
|
||||
}
|
||||
|
||||
static bool af_is_conversion_filter(struct af_instance *af)
|
||||
|
||||
@@ -74,7 +74,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
struct mp_audio *in = arg;
|
||||
struct mp_audio orig_in = *in;
|
||||
|
||||
if (AF_FORMAT_IS_AC3(in->format) || in->nch < s->cfg_min_channel_num)
|
||||
if (AF_FORMAT_IS_SPECIAL(in->format) || in->nch < s->cfg_min_channel_num)
|
||||
return AF_DETACH;
|
||||
|
||||
mp_audio_set_format(in, s->in_sampleformat);
|
||||
|
||||
@@ -153,13 +153,13 @@ static int af_open(struct af_instance* af){
|
||||
int j,k;
|
||||
|
||||
int nch = s->nch;
|
||||
if(AF_OK != control(af,AF_CONTROL_SET_PAN_NOUT, &nch))
|
||||
if(nch && AF_OK != control(af,AF_CONTROL_SET_PAN_NOUT, &nch))
|
||||
return AF_ERROR;
|
||||
|
||||
// Read pan values
|
||||
char *cp = s->matrixstr;
|
||||
j = 0; k = 0;
|
||||
while(k < AF_NCH){
|
||||
while(cp && k < AF_NCH){
|
||||
sscanf(cp, "%f%n" , &s->level[j][k], &n);
|
||||
MP_VERBOSE(af, "[pan] Pan level from channel %i to"
|
||||
" channel %i = %f\n",k,j,s->level[j][k]);
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ enum af_format {
|
||||
// Planar variants
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT|AF_FORMAT_NE),
|
||||
|
||||
|
||||
+10
-6
@@ -100,7 +100,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
float f_multi;
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
return CONTROL_TRUE;
|
||||
return CONTROL_FALSE;
|
||||
|
||||
//allocate simple id
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
@@ -561,9 +561,11 @@ static void audio_pause(struct ao *ao)
|
||||
int err;
|
||||
|
||||
if (p->can_pause) {
|
||||
p->delay_before_pause = get_delay(ao);
|
||||
err = snd_pcm_pause(p->alsa, 1);
|
||||
CHECK_ALSA_ERROR("pcm pause error");
|
||||
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
|
||||
p->delay_before_pause = get_delay(ao);
|
||||
err = snd_pcm_pause(p->alsa, 1);
|
||||
CHECK_ALSA_ERROR("pcm pause error");
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "pause not supported by hardware\n");
|
||||
if (snd_pcm_delay(p->alsa, &p->prepause_frames) < 0
|
||||
@@ -591,8 +593,10 @@ static void audio_resume(struct ao *ao)
|
||||
}
|
||||
|
||||
if (p->can_pause) {
|
||||
err = snd_pcm_pause(p->alsa, 0);
|
||||
CHECK_ALSA_ERROR("pcm resume error");
|
||||
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
|
||||
err = snd_pcm_pause(p->alsa, 0);
|
||||
CHECK_ALSA_ERROR("pcm resume error");
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "resume not supported by hardware\n");
|
||||
err = snd_pcm_prepare(p->alsa);
|
||||
|
||||
@@ -431,6 +431,32 @@ static bool ca_bitmap_from_ch_tag(struct ao *ao, AudioChannelLayout *layout,
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_log_layout(struct ao *ao, AudioChannelLayout layout)
|
||||
{
|
||||
if (!mp_msg_test(ao->log, MSGL_V))
|
||||
return;
|
||||
|
||||
AudioChannelDescription *descs = layout.mChannelDescriptions;
|
||||
|
||||
MP_VERBOSE(ao, "layout: tag: <%d>, bitmap: <%d>, "
|
||||
"descriptions <%d>\n",
|
||||
layout.mChannelLayoutTag,
|
||||
layout.mChannelBitmap,
|
||||
layout.mNumberChannelDescriptions);
|
||||
|
||||
for (int i = 0; i < layout.mNumberChannelDescriptions; i++) {
|
||||
AudioChannelDescription d = descs[i];
|
||||
MP_VERBOSE(ao, " - description %d: label <%d, %d>, flags: <%u>, "
|
||||
"coords: <%f, %f, %f>\n", i,
|
||||
d.mChannelLabel,
|
||||
ca_label_to_mp_speaker_id(d.mChannelLabel),
|
||||
d.mChannelFlags,
|
||||
d.mCoordinates[0],
|
||||
d.mCoordinates[1],
|
||||
d.mCoordinates[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void ca_bitmaps_from_layouts(struct ao *ao,
|
||||
AudioChannelLayout *layouts, size_t n_layouts,
|
||||
uint32_t **bitmaps, size_t *n_bitmaps)
|
||||
@@ -440,6 +466,7 @@ void ca_bitmaps_from_layouts(struct ao *ao,
|
||||
|
||||
for (int i=0; i < n_layouts; i++) {
|
||||
uint32_t bitmap = 0;
|
||||
ca_log_layout(ao, layouts[i]);
|
||||
|
||||
switch (layouts[i].mChannelLayoutTag) {
|
||||
case kAudioChannelLayoutTag_UseChannelBitmap:
|
||||
|
||||
+19
-3
@@ -60,10 +60,12 @@ struct priv {
|
||||
int connect;
|
||||
int autostart;
|
||||
int stdlayout;
|
||||
int last_chunk;
|
||||
volatile int paused;
|
||||
volatile int underrun;
|
||||
volatile float callback_interval;
|
||||
volatile float callback_time;
|
||||
volatile float last_ok_time; // last time real audio was played
|
||||
|
||||
int num_ports;
|
||||
struct port_ring ports[MP_NUM_CHANNELS];
|
||||
@@ -128,11 +130,15 @@ process(jack_nframes_t nframes, void *arg)
|
||||
underrun = 1;
|
||||
}
|
||||
|
||||
float now = mp_time_us() / 1000000.0;
|
||||
|
||||
if (underrun)
|
||||
p->underrun = 1;
|
||||
|
||||
if (!p->underrun)
|
||||
p->last_ok_time = now;
|
||||
|
||||
if (p->estimate) {
|
||||
float now = mp_time_us() / 1000000.0;
|
||||
float diff = p->callback_time + p->callback_interval - now;
|
||||
if ((diff > -0.002) && (diff < 0.002))
|
||||
p->callback_time += p->callback_interval;
|
||||
@@ -289,14 +295,23 @@ static float get_delay(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
int buffered = mp_ring_buffered(p->ports[0].ring); // could be less
|
||||
float in_jack = p->jack_latency;
|
||||
float now = mp_time_us() / 1000000.0;
|
||||
|
||||
if (p->estimate && p->callback_interval > 0) {
|
||||
float elapsed = mp_time_us() / 1000000.0 - p->callback_time;
|
||||
in_jack += p->callback_interval - elapsed;
|
||||
if (in_jack < 0)
|
||||
in_jack = 0;
|
||||
}
|
||||
|
||||
if (p->underrun && !buffered && p->last_chunk) {
|
||||
// Report correct buffer drainage, when our buffer is empty, but jack
|
||||
// and/or the audio device still have some audio to play.
|
||||
// Assumes audio clock goes at about the same speed as the system time.
|
||||
in_jack += p->last_ok_time - now;
|
||||
}
|
||||
|
||||
if (in_jack < 0)
|
||||
in_jack = 0;
|
||||
|
||||
return (float)buffered / (float)ao->bps + in_jack;
|
||||
}
|
||||
|
||||
@@ -369,6 +384,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
}
|
||||
|
||||
p->underrun = 0;
|
||||
p->last_chunk = flags & AOPLAY_FINAL_CHUNK;
|
||||
return ret / ao->sstride;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ static void uninit(struct ao *ao, bool cut_audio)
|
||||
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
|
||||
|
||||
if (!encode_lavc_start(ectx)) {
|
||||
MP_WARN(ao, "not even ready to encode audio at end -> dropped");
|
||||
MP_WARN(ao, "not even ready to encode audio at end -> dropped\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,8 +111,8 @@ static int init(struct ao *ao)
|
||||
return -1;
|
||||
}
|
||||
|
||||
rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate);
|
||||
rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num);
|
||||
rsd_set_param(priv->rd, RSD_SAMPLERATE, (int[]) { ao->samplerate });
|
||||
rsd_set_param(priv->rd, RSD_CHANNELS, (int[]) { ao->channels.num });
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
|
||||
@@ -36,10 +36,6 @@
|
||||
#include "misc/ring.h"
|
||||
#include "ao.h"
|
||||
|
||||
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
|
||||
#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE 0x00000001
|
||||
#endif
|
||||
|
||||
#ifndef PKEY_Device_FriendlyName
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,
|
||||
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
|
||||
@@ -136,17 +132,6 @@ static int fill_VistaBlob(wasapi_state *state)
|
||||
{
|
||||
if (!state)
|
||||
return 1;
|
||||
HMODULE hkernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
if (!hkernel32)
|
||||
return 1;
|
||||
WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
|
||||
(WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(hkernel32, "SetDllDirectoryW");
|
||||
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
|
||||
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(hkernel32, "SetSearchPathMode");
|
||||
if (pSetSearchPathMode)
|
||||
pSetDllDirectory(L""); /* Attempt to use safe search paths */
|
||||
if (pSetSearchPathMode)
|
||||
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
|
||||
state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll");
|
||||
if (!state->VistaBlob.hAvrt)
|
||||
goto exit_label;
|
||||
@@ -160,8 +145,6 @@ static int fill_VistaBlob(wasapi_state *state)
|
||||
exit_label:
|
||||
if (state->VistaBlob.hAvrt)
|
||||
FreeLibrary(state->VistaBlob.hAvrt);
|
||||
if (pSetSearchPathMode)
|
||||
pSetDllDirectory(NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
from __future__ import print_function
|
||||
import os, sys, stat, hashlib, subprocess
|
||||
|
||||
WAFRELEASE = "waf-1.7.13"
|
||||
WAFRELEASE = "waf-1.7.15"
|
||||
WAFURL = "https://waf.googlecode.com/files/" + WAFRELEASE
|
||||
SHA256HASH = "03cc750049350ee01cdbc584b70924e333fcc17ba4a2d04648dab1535538a873"
|
||||
SHA256HASH = "e5ae7028f9b2d8ce1acb9fe1092e8010a90ba764d3ac065ea4e846743290b1d6"
|
||||
|
||||
if os.path.exists("waf"):
|
||||
wafver = subprocess.check_output(['./waf', '--version']).decode()
|
||||
|
||||
+14
-10
@@ -29,6 +29,19 @@
|
||||
|
||||
#include "osdep/numcores.h"
|
||||
|
||||
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size)
|
||||
{
|
||||
if (size) {
|
||||
av_free(avctx->extradata);
|
||||
avctx->extradata_size = 0;
|
||||
avctx->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (!avctx->extradata)
|
||||
return -1;
|
||||
avctx->extradata_size = size;
|
||||
memcpy(avctx->extradata, ptr, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the codec-related fields from st into avctx. This does not set the
|
||||
// codec itself, only codec related header data provided by libavformat.
|
||||
@@ -38,16 +51,7 @@
|
||||
// This is strictly for decoding only.
|
||||
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
|
||||
{
|
||||
if (st->extradata_size) {
|
||||
av_free(avctx->extradata);
|
||||
avctx->extradata_size = 0;
|
||||
avctx->extradata =
|
||||
av_mallocz(st->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (avctx->extradata) {
|
||||
avctx->extradata_size = st->extradata_size;
|
||||
memcpy(avctx->extradata, st->extradata, st->extradata_size);
|
||||
}
|
||||
}
|
||||
mp_lavc_set_extradata(avctx, st->extradata, st->extradata_size);
|
||||
avctx->codec_tag = st->codec_tag;
|
||||
avctx->stream_codec_tag = st->stream_codec_tag;
|
||||
avctx->bit_rate = st->bit_rate;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
struct mp_decoder_list;
|
||||
struct demux_packet;
|
||||
|
||||
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size);
|
||||
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
|
||||
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb);
|
||||
int64_t mp_pts_to_av(double mp_pts, AVRational *tb);
|
||||
|
||||
+8
-2
@@ -226,7 +226,7 @@ struct demux_packet *demux_copy_packet(struct demux_packet *dp)
|
||||
struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
|
||||
{
|
||||
if (demuxer->num_streams > MAX_SH_STREAMS) {
|
||||
MP_WARN(demuxer, "Too many streams.");
|
||||
MP_WARN(demuxer, "Too many streams.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ static bool demux_check_queue_full(demuxer_t *demux)
|
||||
overflow:
|
||||
|
||||
if (!demux->warned_queue_overflow) {
|
||||
MP_ERR(demux, "\nToo many packets in the demuxer "
|
||||
MP_ERR(demux, "Too many packets in the demuxer "
|
||||
"packet queue (video: %d packets in %d bytes, audio: %d "
|
||||
"packets in %d bytes, sub: %d packets in %d bytes).\n",
|
||||
count_packs(demux, STREAM_VIDEO), count_bytes(demux, STREAM_VIDEO),
|
||||
@@ -522,6 +522,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
.glog = log,
|
||||
.filename = talloc_strdup(demuxer, stream->url),
|
||||
.metadata = talloc_zero(demuxer, struct mp_tags),
|
||||
.initializing = true,
|
||||
};
|
||||
demuxer->params = params; // temporary during open()
|
||||
stream_seek(stream, stream->start_pos);
|
||||
@@ -554,6 +555,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
"File is not seekable, but there's a cache: enabling seeking.\n");
|
||||
demuxer->seekable = true;
|
||||
}
|
||||
demuxer->initializing = false;
|
||||
return demuxer;
|
||||
}
|
||||
|
||||
@@ -724,6 +726,8 @@ int demux_info_add_bstr(demuxer_t *demuxer, struct bstr opt, struct bstr param)
|
||||
if (oldval) {
|
||||
if (bstrcmp0(param, oldval) == 0)
|
||||
return 0;
|
||||
}
|
||||
if (!demuxer->initializing) {
|
||||
MP_INFO(demuxer, "Demuxer info %.*s changed to %.*s\n",
|
||||
BSTR_P(opt), BSTR_P(param));
|
||||
}
|
||||
@@ -740,6 +744,8 @@ int demux_info_print(demuxer_t *demuxer)
|
||||
if (!info || !info->num_keys)
|
||||
return 0;
|
||||
|
||||
demux_info_update(demuxer);
|
||||
|
||||
mp_info(demuxer->glog, "Clip info:\n");
|
||||
for (n = 0; n < info->num_keys; n++) {
|
||||
mp_info(demuxer->glog, " %s: %s\n", info->keys[n], info->values[n]);
|
||||
|
||||
@@ -170,6 +170,7 @@ typedef struct demuxer {
|
||||
// File format allows PTS resets (even if the current file is without)
|
||||
bool ts_resets_possible;
|
||||
bool warned_queue_overflow;
|
||||
bool initializing;
|
||||
|
||||
struct sh_stream **streams;
|
||||
int num_streams;
|
||||
|
||||
+35
-27
@@ -90,12 +90,16 @@ struct format_hack {
|
||||
const char *mime_type;
|
||||
int probescore;
|
||||
float analyzeduration;
|
||||
bool max_probe; // use probescore only if max. probe size reached
|
||||
};
|
||||
|
||||
static const struct format_hack format_hacks[] = {
|
||||
// for webradios
|
||||
{"aac", "audio/aacp", 25, 0.5},
|
||||
{"aac", "audio/aac", 25, 0.5},
|
||||
{"mp3", "audio/mpeg", 25, 0.5},
|
||||
// some mp3 files don't detect correctly
|
||||
{"mp3", NULL, 24, .max_probe = true},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -104,15 +108,6 @@ static const char *format_blacklist[] = {
|
||||
0
|
||||
};
|
||||
|
||||
static const struct format_hack *find_format_from_mime_type(char *mime_type)
|
||||
{
|
||||
for (int n = 0; format_hacks[n].ff_name; n++) {
|
||||
if (strcasecmp(format_hacks[n].mime_type, mime_type) == 0)
|
||||
return &format_hacks[n];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mp_read(void *opaque, uint8_t *buf, int size)
|
||||
{
|
||||
struct demuxer *demuxer = opaque;
|
||||
@@ -223,17 +218,9 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
priv->filename = sep + 1;
|
||||
}
|
||||
|
||||
const char *expected_format = NULL;
|
||||
int expected_format_probescore = AVPROBE_SCORE_MAX;
|
||||
char *mime_type = demuxer->stream->mime_type;
|
||||
|
||||
if (lavfdopts->allow_mimetype && mime_type) {
|
||||
priv->format_hack = find_format_from_mime_type(mime_type);
|
||||
if (priv->format_hack) {
|
||||
expected_format = priv->format_hack->ff_name;
|
||||
expected_format_probescore = priv->format_hack->probescore;
|
||||
}
|
||||
}
|
||||
if (!lavfdopts->allow_mimetype || !mime_type)
|
||||
mime_type = "";
|
||||
|
||||
const char *format = lavfdopts->format;
|
||||
if (!format)
|
||||
@@ -264,17 +251,18 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
AVProbeData avpd = {
|
||||
// Disable file-extension matching with normal checks
|
||||
.filename = check <= DEMUX_CHECK_REQUEST ? priv->filename : NULL,
|
||||
.filename = check <= DEMUX_CHECK_REQUEST ? priv->filename : "",
|
||||
.buf_size = 0,
|
||||
.buf = av_mallocz(PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE),
|
||||
};
|
||||
|
||||
while (avpd.buf_size < PROBE_BUF_SIZE) {
|
||||
bool final_probe = false;
|
||||
do {
|
||||
int nsize = av_clip(avpd.buf_size * 2, INITIAL_PROBE_SIZE,
|
||||
PROBE_BUF_SIZE);
|
||||
bstr buf = stream_peek(s, nsize);
|
||||
if (buf.len <= avpd.buf_size)
|
||||
break;
|
||||
final_probe = true;
|
||||
memcpy(avpd.buf, buf.start, buf.len);
|
||||
avpd.buf_size = buf.len;
|
||||
|
||||
@@ -283,20 +271,32 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
if (priv->avif) {
|
||||
MP_VERBOSE(demuxer, "Found '%s' at score=%d size=%d.\n",
|
||||
priv->avif->name, score, avpd.buf_size);
|
||||
priv->avif->name, score, avpd.buf_size);
|
||||
|
||||
priv->format_hack = NULL;
|
||||
for (int n = 0; format_hacks[n].ff_name; n++) {
|
||||
const struct format_hack *entry = &format_hacks[n];
|
||||
if (strcmp(entry->ff_name, priv->avif->name) != 0)
|
||||
continue;
|
||||
if (entry->mime_type && strcasecmp(entry->mime_type, mime_type) != 0)
|
||||
continue;
|
||||
priv->format_hack = entry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (score >= min_probe)
|
||||
break;
|
||||
|
||||
if (expected_format) {
|
||||
if (strcmp(priv->avif->name, expected_format) == 0 &&
|
||||
score >= expected_format_probescore)
|
||||
if (priv->format_hack) {
|
||||
if (score >= priv->format_hack->probescore &&
|
||||
(!priv->format_hack->max_probe || final_probe))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
priv->avif = NULL;
|
||||
}
|
||||
priv->format_hack = NULL;
|
||||
} while (!final_probe);
|
||||
|
||||
av_free(avpd.buf);
|
||||
|
||||
@@ -458,6 +458,14 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
memcpy(sh_sub->extradata, codec->extradata, codec->extradata_size);
|
||||
sh_sub->extradata_len = codec->extradata_size;
|
||||
}
|
||||
sh_sub->w = codec->width;
|
||||
sh_sub->h = codec->height;
|
||||
|
||||
// Hack for MicroDVD: if time_base matches the ffmpeg microdvd reader's
|
||||
// default FPS (23.976), assume the MicroDVD file did not declare a
|
||||
// FPS, and the MicroDVD file uses frame based timing.
|
||||
if (codec->time_base.num == 125 && codec->time_base.den == 2997)
|
||||
sh_sub->frame_based = true;
|
||||
break;
|
||||
}
|
||||
case AVMEDIA_TYPE_ATTACHMENT: {
|
||||
|
||||
+23
-12
@@ -723,7 +723,7 @@ static int demux_mkv_read_cues(demuxer_t *demuxer)
|
||||
stream_t *s = demuxer->stream;
|
||||
|
||||
if (opts->index_mode == 0 || opts->index_mode == 2) {
|
||||
ebml_read_skip(s, NULL);
|
||||
ebml_read_skip(demuxer->log, -1, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1128,7 +1128,7 @@ static int read_header_element(struct demuxer *demuxer, uint32_t id,
|
||||
res = 2;
|
||||
}
|
||||
if (!at_filepos && id != EBML_ID_INVALID)
|
||||
ebml_read_skip(s, NULL);
|
||||
ebml_read_skip(demuxer->log, -1, s);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1173,6 +1173,7 @@ typedef struct {
|
||||
char *id;
|
||||
int fourcc;
|
||||
int extradata;
|
||||
bool parse;
|
||||
} videocodec_info_t;
|
||||
|
||||
static const videocodec_info_t vinfo[] = {
|
||||
@@ -1185,11 +1186,11 @@ static const videocodec_info_t vinfo[] = {
|
||||
{MKV_V_MPEG4_AVC, MP_FOURCC('a', 'v', 'c', '1'), 1},
|
||||
{MKV_V_THEORA, MP_FOURCC('t', 'h', 'e', 'o'), 1},
|
||||
{MKV_V_VP8, MP_FOURCC('V', 'P', '8', '0'), 0},
|
||||
{MKV_V_VP9, MP_FOURCC('V', 'P', '9', '0'), 0},
|
||||
{MKV_V_VP9, MP_FOURCC('V', 'P', '9', '0'), 0, true},
|
||||
{MKV_V_DIRAC, MP_FOURCC('d', 'r', 'a', 'c'), 0},
|
||||
{MKV_V_PRORES, MP_FOURCC('p', 'r', '0', '0'), 0},
|
||||
{MKV_V_HEVC, MP_FOURCC('H', 'E', 'V', 'C'), 1},
|
||||
{NULL, 0, 0}
|
||||
{0}
|
||||
};
|
||||
|
||||
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
@@ -1273,6 +1274,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
extradata = track->private_data;
|
||||
extradata_size = track->private_size;
|
||||
}
|
||||
track->parse = vi->parse;
|
||||
if (!vi->id) {
|
||||
MP_WARN(demuxer, "Unknown/unsupported "
|
||||
"CodecID (%s) or missing/bad CodecPrivate\n"
|
||||
@@ -1336,7 +1338,7 @@ static struct mkv_audio_tag {
|
||||
bool parse;
|
||||
} mkv_audio_tags[] = {
|
||||
{ MKV_A_MP2, 0, 0x0055 },
|
||||
{ MKV_A_MP3, 0, 0x0055 },
|
||||
{ MKV_A_MP3, 0, 0x0055, true },
|
||||
{ MKV_A_AC3, 1, 0x2000 },
|
||||
{ MKV_A_EAC3, 1, MP_FOURCC('E', 'A', 'C', '3') },
|
||||
{ MKV_A_DTS, 0, 0x2001 },
|
||||
@@ -2270,7 +2272,7 @@ static void index_block(demuxer_t *demuxer, struct block_info *block)
|
||||
}
|
||||
}
|
||||
|
||||
static int read_block(demuxer_t *demuxer, struct block_info *block)
|
||||
static int read_block(demuxer_t *demuxer, int64_t end, struct block_info *block)
|
||||
{
|
||||
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
|
||||
stream_t *s = demuxer->stream;
|
||||
@@ -2281,7 +2283,7 @@ static int read_block(demuxer_t *demuxer, struct block_info *block)
|
||||
|
||||
free_block(block);
|
||||
length = ebml_read_length(s, NULL);
|
||||
if (length > 500000000)
|
||||
if (length > 500000000 || stream_tell(s) + length > (uint64_t)end)
|
||||
goto exit;
|
||||
block->alloc = malloc(length + AV_LZO_INPUT_PADDING);
|
||||
if (!block->alloc)
|
||||
@@ -2442,7 +2444,7 @@ static int read_block_group(demuxer_t *demuxer, int64_t end,
|
||||
break;
|
||||
|
||||
case MATROSKA_ID_BLOCK:
|
||||
if (read_block(demuxer, block) < 0)
|
||||
if (read_block(demuxer, end, block) < 0)
|
||||
goto error;
|
||||
break;
|
||||
|
||||
@@ -2454,11 +2456,12 @@ static int read_block_group(demuxer_t *demuxer, int64_t end,
|
||||
block->keyframe = false;
|
||||
break;
|
||||
|
||||
case MATROSKA_ID_CLUSTER:
|
||||
case EBML_ID_INVALID:
|
||||
goto error;
|
||||
|
||||
default:
|
||||
if (ebml_read_skip_or_resync_cluster(demuxer->log, s, NULL) != 0)
|
||||
if (ebml_read_skip(demuxer->log, end, s) != 0)
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
@@ -2491,6 +2494,8 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
|
||||
case MATROSKA_ID_BLOCKGROUP: {
|
||||
int64_t end = ebml_read_length(s, NULL);
|
||||
end += stream_tell(s);
|
||||
if (end > mkv_d->cluster_end)
|
||||
goto find_next_cluster;
|
||||
int res = read_block_group(demuxer, end, block);
|
||||
if (res < 0)
|
||||
goto find_next_cluster;
|
||||
@@ -2501,7 +2506,7 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
|
||||
|
||||
case MATROSKA_ID_SIMPLEBLOCK: {
|
||||
*block = (struct block_info){ .simple = true };
|
||||
int res = read_block(demuxer, block);
|
||||
int res = read_block(demuxer, mkv_d->cluster_end, block);
|
||||
if (res < 0)
|
||||
goto find_next_cluster;
|
||||
if (res > 0)
|
||||
@@ -2517,7 +2522,7 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
|
||||
goto find_next_cluster;
|
||||
|
||||
default: ;
|
||||
if (ebml_read_skip_or_resync_cluster(demuxer->log, s, NULL) != 0)
|
||||
if (ebml_read_skip(demuxer->log, mkv_d->cluster_end, s) != 0)
|
||||
goto find_next_cluster;
|
||||
break;
|
||||
}
|
||||
@@ -2532,7 +2537,13 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
|
||||
break;
|
||||
if (s->eof)
|
||||
return -1;
|
||||
ebml_read_skip_or_resync_cluster(demuxer->log, s, NULL);
|
||||
// For the sake of robustness, consider even unknown level 1
|
||||
// elements the same as unknown/broken IDs.
|
||||
if (!ebml_is_mkv_level1_id(id) ||
|
||||
ebml_read_skip(demuxer->log, -1, s) != 0)
|
||||
{
|
||||
ebml_resync_cluster(demuxer->log, s);
|
||||
}
|
||||
}
|
||||
next_cluster:
|
||||
mkv_d->cluster_end = ebml_read_length(s, NULL);
|
||||
|
||||
+36
-36
@@ -41,6 +41,26 @@
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#endif
|
||||
|
||||
// Whether the id is a known Matroska level 1 element (allowed as element on
|
||||
// global file level, after the level 0 MATROSKA_ID_SEGMENT).
|
||||
// This (intentionally) doesn't include "global" elements.
|
||||
bool ebml_is_mkv_level1_id(uint32_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case MATROSKA_ID_SEEKHEAD:
|
||||
case MATROSKA_ID_INFO:
|
||||
case MATROSKA_ID_CLUSTER:
|
||||
case MATROSKA_ID_TRACKS:
|
||||
case MATROSKA_ID_CUES:
|
||||
case MATROSKA_ID_ATTACHMENTS:
|
||||
case MATROSKA_ID_CHAPTERS:
|
||||
case MATROSKA_ID_TAGS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read: the element content data ID.
|
||||
* Return: the ID.
|
||||
@@ -252,21 +272,32 @@ char *ebml_read_utf8(stream_t *s, uint64_t *length)
|
||||
|
||||
/*
|
||||
* Skip the current element.
|
||||
* end: the end of the parent element or -1 (for robust error handling)
|
||||
*/
|
||||
int ebml_read_skip(stream_t *s, uint64_t *length)
|
||||
int ebml_read_skip(struct mp_log *log, int64_t end, stream_t *s)
|
||||
{
|
||||
uint64_t len;
|
||||
int l;
|
||||
|
||||
int64_t pos = stream_tell(s);
|
||||
|
||||
len = ebml_read_length(s, &l);
|
||||
if (len == EBML_UINT_INVALID)
|
||||
return 1;
|
||||
if (length)
|
||||
*length = len + l;
|
||||
goto invalid;
|
||||
|
||||
stream_skip(s, len);
|
||||
int64_t pos2 = stream_tell(s);
|
||||
if (len >= INT64_MAX - pos2 || (end > 0 && pos2 + len > end))
|
||||
goto invalid;
|
||||
|
||||
if (!stream_skip(s, len))
|
||||
goto invalid;
|
||||
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
mp_err(log, "Invalid EBML length at position %"PRId64"\n", pos);
|
||||
stream_seek(s, pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -291,37 +322,6 @@ int ebml_resync_cluster(struct mp_log *log, stream_t *s)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip the current element, or on error, call ebml_resync_cluster().
|
||||
*/
|
||||
int ebml_read_skip_or_resync_cluster(struct mp_log *log, stream_t *s,
|
||||
uint64_t *length)
|
||||
{
|
||||
uint64_t len;
|
||||
int l;
|
||||
|
||||
len = ebml_read_length(s, &l);
|
||||
if (len == EBML_UINT_INVALID)
|
||||
goto resync;
|
||||
|
||||
if (length)
|
||||
*length = len + l;
|
||||
|
||||
int64_t pos = stream_tell(s);
|
||||
|
||||
// When reading corrupted elements, len will often be a random high number,
|
||||
// and stream_skip() will fail when skipping past EOF.
|
||||
if (!stream_skip(s, len)) {
|
||||
stream_seek(s, pos);
|
||||
goto resync;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
resync:
|
||||
return ebml_resync_cluster(log, s) < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the next element, but only the header. The contents
|
||||
* are supposed to be sub-elements which can be read separately.
|
||||
|
||||
+2
-3
@@ -92,6 +92,7 @@ struct ebml_parse_ctx {
|
||||
#define EBML_FLOAT_INVALID -1000000000.0
|
||||
|
||||
|
||||
bool ebml_is_mkv_level1_id(uint32_t id);
|
||||
uint32_t ebml_read_id (stream_t *s, int *length);
|
||||
uint64_t ebml_read_vlen_uint (bstr *buffer);
|
||||
int64_t ebml_read_vlen_int (bstr *buffer);
|
||||
@@ -101,9 +102,7 @@ int64_t ebml_read_int (stream_t *s, uint64_t *length);
|
||||
double ebml_read_float (stream_t *s, uint64_t *length);
|
||||
char *ebml_read_ascii (stream_t *s, uint64_t *length);
|
||||
char *ebml_read_utf8 (stream_t *s, uint64_t *length);
|
||||
int ebml_read_skip (stream_t *s, uint64_t *length);
|
||||
int ebml_read_skip_or_resync_cluster(struct mp_log *log, stream_t *s,
|
||||
uint64_t *length);
|
||||
int ebml_read_skip(struct mp_log *log, int64_t end, stream_t *s);
|
||||
int ebml_resync_cluster(struct mp_log *log, stream_t *s);
|
||||
uint32_t ebml_read_master (stream_t *s, uint64_t *length);
|
||||
|
||||
|
||||
+3
-3
@@ -64,11 +64,11 @@ mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filename)
|
||||
if (filename[0] == '@') {
|
||||
FILE *lst_f = fopen(filename + 1, "r");
|
||||
if (lst_f) {
|
||||
char *fname = talloc_size(mf, MP_PATH_MAX);
|
||||
while (fgets(fname, MP_PATH_MAX, lst_f)) {
|
||||
char *fname = talloc_size(mf, 512);
|
||||
while (fgets(fname, 512, lst_f)) {
|
||||
/* remove spaces from end of fname */
|
||||
char *t = fname + strlen(fname) - 1;
|
||||
while (t > fname && isspace(*t))
|
||||
while (t > fname && isspace((unsigned char)*t))
|
||||
*(t--) = 0;
|
||||
if (!mp_path_exists(fname)) {
|
||||
mp_verbose(log, "file not found: '%s'\n", fname);
|
||||
|
||||
@@ -87,6 +87,7 @@ typedef struct sh_video {
|
||||
typedef struct sh_sub {
|
||||
unsigned char *extradata; // extra header data passed from demuxer
|
||||
int extradata_len;
|
||||
int w, h; // mp4 vobsubs
|
||||
int frame_based; // timestamps are frame-based
|
||||
bool is_utf8; // if false, subtitle packet charset is unknown
|
||||
struct ass_track *track; // loaded by libass
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
# Play Finnish audio if available, fall back to English otherwise.
|
||||
#alang = fi,en
|
||||
|
||||
# Change subtitle encoding. For Arabic subtitles use 'cp1256'.
|
||||
# If the file seems to be valid UTF-8, prefer UTF-8.
|
||||
#subcp = utf8:cp1256
|
||||
|
||||
# Enable hardware decoding if available. Often, this requires using an certain
|
||||
# video output, otherwise no hardware decoding will be used.
|
||||
#hwdec = auto
|
||||
|
||||
+1
-1
@@ -23,5 +23,5 @@ Exec=mpv --really-quiet --force-window -- %F
|
||||
Terminal=false
|
||||
NoDisplay=true
|
||||
Categories=AudioVideo;Audio;Video;Player;TV;
|
||||
MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;
|
||||
MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;
|
||||
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb
|
||||
+15
-1
@@ -253,7 +253,7 @@ bool mp_replace_legacy_cmd(void *t, bstr *s)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mp_input_is_abort_cmd(int cmd_id)
|
||||
static bool is_abort_cmd(int cmd_id)
|
||||
{
|
||||
switch (cmd_id) {
|
||||
case MP_CMD_QUIT:
|
||||
@@ -264,6 +264,20 @@ bool mp_input_is_abort_cmd(int cmd_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mp_input_is_abort_cmd(struct mp_cmd *cmd)
|
||||
{
|
||||
if (is_abort_cmd(cmd->id))
|
||||
return true;
|
||||
if (cmd->id == MP_CMD_COMMAND_LIST) {
|
||||
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
|
||||
{
|
||||
if (mp_input_is_abort_cmd(cmd))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mp_print_cmd_list(struct mp_log *out)
|
||||
{
|
||||
for (int i = 0; mp_cmds[i].name; i++) {
|
||||
|
||||
+2
-1
@@ -107,7 +107,8 @@ enum mp_command_type {
|
||||
};
|
||||
|
||||
// Executing this command will abort playback (play something else, or quit).
|
||||
bool mp_input_is_abort_cmd(int cmd_id);
|
||||
struct mp_cmd;
|
||||
bool mp_input_is_abort_cmd(struct mp_cmd *cmd);
|
||||
|
||||
struct bstr;
|
||||
bool mp_replace_legacy_cmd(void *talloc_ctx, struct bstr *s);
|
||||
|
||||
+4
-4
@@ -237,7 +237,7 @@ static bool queue_has_abort_cmds(struct cmd_queue *queue)
|
||||
{
|
||||
bool ret = false;
|
||||
for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next)
|
||||
if (mp_input_is_abort_cmd(cmd->id)) {
|
||||
if (mp_input_is_abort_cmd(cmd)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
@@ -544,9 +544,9 @@ static void release_down_cmd(struct input_ctx *ictx, bool drop_current)
|
||||
|
||||
static int find_key_down(struct input_ctx *ictx, int code)
|
||||
{
|
||||
code &= ~(MP_KEY_STATE_UP | MP_KEY_STATE_DOWN);
|
||||
code &= ~(MP_KEY_STATE_UP | MP_KEY_STATE_DOWN | MP_KEY_MODIFIER_MASK);
|
||||
for (int j = 0; j < ictx->num_key_down; j++) {
|
||||
if (ictx->key_down[j] == code)
|
||||
if ((ictx->key_down[j] & ~MP_KEY_MODIFIER_MASK) == code)
|
||||
return j;
|
||||
}
|
||||
return -1;
|
||||
@@ -580,7 +580,7 @@ static bool should_drop_cmd(struct input_ctx *ictx, struct mp_cmd *cmd)
|
||||
{
|
||||
struct cmd_queue *queue = &ictx->cmd_queue;
|
||||
return (queue_count_cmds(queue) >= ictx->key_fifo_size &&
|
||||
(!mp_input_is_abort_cmd(cmd->id) || queue_has_abort_cmds(queue)));
|
||||
(!mp_input_is_abort_cmd(cmd) || queue_has_abort_cmds(queue)));
|
||||
}
|
||||
|
||||
static void interpret_key(struct input_ctx *ictx, int code, double scale)
|
||||
|
||||
+2
-2
@@ -1169,10 +1169,10 @@ fi
|
||||
|
||||
if test "$cc_vendor" = "gnu" ; then
|
||||
cflag_check -Wundef && WARNFLAGS="-Wundef $WARNFLAGS"
|
||||
# -std=gnu99 is not a warning flag but is placed in WARN_CFLAGS because
|
||||
# -std=c99 is not a warning flag but is placed in WARN_CFLAGS because
|
||||
# that's the only variable specific to C now, and this option is not valid
|
||||
# for C++.
|
||||
cflag_check -std=gnu99 && WARN_CFLAGS="-std=gnu99 $WARN_CFLAGS"
|
||||
cflag_check -std=c99 && WARN_CFLAGS="-std=c99 $WARN_CFLAGS"
|
||||
cflag_check -Wno-pointer-sign && WARN_CFLAGS="-Wno-pointer-sign $WARN_CFLAGS"
|
||||
cflag_check -Wdisabled-optimization && WARN_CFLAGS="-Wdisabled-optimization $WARN_CFLAGS"
|
||||
cflag_check -Wmissing-prototypes && WARN_CFLAGS="-Wmissing-prototypes $WARN_CFLAGS"
|
||||
|
||||
+1
-2
@@ -27,8 +27,7 @@ SOURCES_AUDIO_INPUT-$(ALSA) += stream/ai_alsa1x.c
|
||||
SOURCES_AUDIO_INPUT-$(OSS) += stream/ai_oss.c
|
||||
SOURCES_AUDIO_INPUT-$(SNDIO) += stream/ai_sndio.c
|
||||
SOURCES-$(AUDIO_INPUT) += $(SOURCES_AUDIO_INPUT-yes)
|
||||
SOURCES-$(CDDA) += stream/stream_cdda.c \
|
||||
stream/cdinfo.c
|
||||
SOURCES-$(CDDA) += stream/stream_cdda.c
|
||||
SOURCES-$(DVBIN) += stream/dvb_tune.c \
|
||||
stream/stream_dvb.c
|
||||
SOURCES-$(DVDREAD) += stream/stream_dvd.c \
|
||||
|
||||
@@ -316,6 +316,7 @@ const m_option_t mp_opts[] = {
|
||||
({"no", 0}, {"attachment", 1})),
|
||||
|
||||
OPT_STRING("quvi-format", quvi_format, 0),
|
||||
OPT_FLAG("quvi-fetch-subtitles", quvi_fetch_subtitles, 0),
|
||||
|
||||
#if HAVE_CDDA
|
||||
{ "cdda", (void *)&cdda_opts, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
|
||||
@@ -718,6 +719,7 @@ const struct MPOpts mp_default_opts = {
|
||||
.sub_visibility = 1,
|
||||
.sub_pos = 100,
|
||||
.sub_speed = 1.0,
|
||||
.quvi_fetch_subtitles = 0,
|
||||
.audio_output_channels = MP_CHMAP_INIT_STEREO,
|
||||
.audio_output_format = 0, // AF_FORMAT_UNKNOWN
|
||||
.playback_speed = 1.,
|
||||
|
||||
@@ -158,6 +158,7 @@ typedef struct MPOpts {
|
||||
int forced_subs_only;
|
||||
int stretch_dvd_subs;
|
||||
char *quvi_format;
|
||||
int quvi_fetch_subtitles;
|
||||
|
||||
// subreader.c
|
||||
int suboverlap_enabled;
|
||||
|
||||
@@ -330,7 +330,8 @@ int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[])
|
||||
pthread_create(&playback_thread_id, NULL, playback_thread, &ctx);
|
||||
|
||||
[mpv_shared_app().input_ready lock];
|
||||
[mpv_shared_app().input_ready wait];
|
||||
while (!mpv_shared_app().inputContext)
|
||||
[mpv_shared_app().input_ready wait];
|
||||
[mpv_shared_app().input_ready unlock];
|
||||
|
||||
cocoa_run_runloop();
|
||||
@@ -388,13 +389,10 @@ void cocoa_stop_runloop(void)
|
||||
|
||||
void cocoa_set_input_context(struct input_ctx *input_context)
|
||||
{
|
||||
if (input_context) {
|
||||
[mpv_shared_app().input_ready lock];
|
||||
[mpv_shared_app().input_ready signal];
|
||||
[mpv_shared_app().input_ready unlock];
|
||||
}
|
||||
|
||||
[mpv_shared_app().input_ready lock];
|
||||
mpv_shared_app().inputContext = input_context;
|
||||
[mpv_shared_app().input_ready signal];
|
||||
[mpv_shared_app().input_ready unlock];
|
||||
}
|
||||
|
||||
void cocoa_post_fake_event(void)
|
||||
|
||||
+5
-1
@@ -59,7 +59,11 @@ char *mp_get_win_config_path(const char *filename)
|
||||
res = mp_path_join(NULL, bstr0(temp), bstr0(filename));
|
||||
if (!mp_path_exists(res) || mp_path_isdir(res)) {
|
||||
talloc_free(res);
|
||||
res = NULL;
|
||||
res = mp_path_join(NULL, bstr0(dir), bstr0(filename));
|
||||
if (!mp_path_exists(res) || mp_path_isdir(res)) {
|
||||
talloc_free(res);
|
||||
res = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-3
@@ -47,6 +47,8 @@
|
||||
#include "input/keycodes.h"
|
||||
#include "terminal.h"
|
||||
|
||||
static int use_terminal;
|
||||
|
||||
#if HAVE_TERMIOS
|
||||
static volatile struct termios tio_orig;
|
||||
static volatile int tio_orig_set;
|
||||
@@ -84,6 +86,9 @@ static char *term_smkx = NULL;
|
||||
#endif
|
||||
|
||||
static keycode_st *keys_push(char *p, int code) {
|
||||
if (strlen(p) > 8)
|
||||
return NULL;
|
||||
|
||||
if (getch2_keys.len == getch2_keys.cap) {
|
||||
getch2_keys.cap *= 2;
|
||||
if (getch2_keys.cap == 0)
|
||||
@@ -454,7 +459,7 @@ static volatile int getch2_enabled = 0;
|
||||
|
||||
static void do_activate_getch2(void)
|
||||
{
|
||||
if (getch2_active)
|
||||
if (getch2_active || !use_terminal)
|
||||
return;
|
||||
|
||||
#if HAVE_TERMINFO || HAVE_TERMCAP
|
||||
@@ -513,7 +518,7 @@ static int setsigaction(int signo, void (*handler) (int),
|
||||
else
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
sa.sa_flags = flags;
|
||||
sa.sa_flags = flags | SA_RESTART;
|
||||
return sigaction(signo, &sa, NULL);
|
||||
}
|
||||
|
||||
@@ -612,6 +617,8 @@ void terminal_setup_stdin_cmd_input(struct input_ctx *ictx)
|
||||
|
||||
int terminal_init(void)
|
||||
{
|
||||
load_termcap(NULL);
|
||||
use_terminal = isatty(1);
|
||||
if (use_terminal)
|
||||
load_termcap(NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -334,11 +334,13 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize)
|
||||
res = audio_decode(d_audio, ao->buffer, a);
|
||||
if (skip_samples <= mp_audio_buffer_samples(ao->buffer)) {
|
||||
mp_audio_buffer_skip(ao->buffer, skip_samples);
|
||||
ao->buffer_playable_samples = 0;
|
||||
if (res < 0)
|
||||
return res;
|
||||
return audio_decode(d_audio, ao->buffer, playsize);
|
||||
}
|
||||
mp_audio_buffer_clear(ao->buffer);
|
||||
ao->buffer_playable_samples = 0;
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
@@ -442,6 +444,7 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
mp_audio_buffer_peek(ao->buffer, &data);
|
||||
data.samples = MPMIN(data.samples, playsize);
|
||||
int played = write_to_ao(mpctx, &data, playflags, written_audio_pts(mpctx));
|
||||
assert(played >= 0 && played <= data.samples);
|
||||
ao->buffer_playable_samples = playsize - played;
|
||||
|
||||
if (played > 0) {
|
||||
|
||||
+5
-7
@@ -700,8 +700,8 @@ static int tag_property(m_option_t *prop, int action, void *arg,
|
||||
char **slist = NULL;
|
||||
int num = 0;
|
||||
for (int n = 0; n < tags->num_keys; n++) {
|
||||
MP_TARRAY_APPEND(NULL, slist, num, tags->keys[n]);
|
||||
MP_TARRAY_APPEND(NULL, slist, num, tags->values[n]);
|
||||
MP_TARRAY_APPEND(NULL, slist, num, talloc_strdup(NULL, tags->keys[n]));
|
||||
MP_TARRAY_APPEND(NULL, slist, num, talloc_strdup(NULL, tags->values[n]));
|
||||
}
|
||||
MP_TARRAY_APPEND(NULL, slist, num, NULL);
|
||||
*(char ***)arg = slist;
|
||||
@@ -750,11 +750,9 @@ static int mp_property_chapter_metadata(m_option_t *prop, int action, void *arg,
|
||||
{
|
||||
struct demuxer *demuxer = mpctx->master_demuxer;
|
||||
int chapter = get_current_chapter(mpctx);
|
||||
if (!demuxer || chapter < 0)
|
||||
if (!demuxer || chapter < 0 || chapter >= demuxer->num_chapters)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
assert(chapter < demuxer->num_chapters);
|
||||
|
||||
return tag_property(prop, action, arg, demuxer->chapters[chapter].metadata);
|
||||
}
|
||||
|
||||
@@ -2941,7 +2939,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
}
|
||||
#endif /* HAVE_PVR */
|
||||
#if HAVE_DVBIN
|
||||
if (mpctx->stream->type == STREAMTYPE_DVB) {
|
||||
if (mpctx->stream && mpctx->stream->type == STREAMTYPE_DVB) {
|
||||
int dir;
|
||||
int v = cmd->args[0].v.i;
|
||||
|
||||
@@ -2979,7 +2977,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
|
||||
#if HAVE_DVBIN
|
||||
case MP_CMD_DVB_SET_CHANNEL:
|
||||
if (mpctx->stream->type == STREAMTYPE_DVB) {
|
||||
if (mpctx->stream && mpctx->stream->type == STREAMTYPE_DVB) {
|
||||
mpctx->last_dvb_step = 1;
|
||||
|
||||
if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
|
||||
|
||||
+6
-1
@@ -140,7 +140,10 @@ void mp_handle_nav(struct MPContext *mpctx)
|
||||
case MP_NAV_EVENT_RESET_ALL: {
|
||||
mpctx->stop_play = PT_RELOAD_DEMUXER;
|
||||
MP_VERBOSE(nav, "reload\n");
|
||||
break;
|
||||
// return immediately.
|
||||
// other events should be handled after reloaded.
|
||||
talloc_free(ev);
|
||||
return;
|
||||
}
|
||||
case MP_NAV_EVENT_RESET: {
|
||||
nav->nav_still_frame = 0;
|
||||
@@ -237,6 +240,8 @@ void mp_nav_get_highlight(struct osd_state *osd, struct mp_osd_res res,
|
||||
talloc_free(sub->bitmap);
|
||||
sub->bitmap = talloc_array(sub, uint32_t, sizes[0] * sizes[1]);
|
||||
memset(sub->bitmap, 0x80, talloc_get_size(sub->bitmap));
|
||||
nav->subsize[0] = sizes[0];
|
||||
nav->subsize[1] = sizes[1];
|
||||
}
|
||||
|
||||
sub->x = nav->highlight[0];
|
||||
|
||||
+15
-9
@@ -443,13 +443,16 @@ static struct track *add_stream_track(struct MPContext *mpctx,
|
||||
track->preloaded = !!stream->sub->track;
|
||||
|
||||
// Needed for DVD and Blu-ray.
|
||||
if (!track->lang) {
|
||||
struct stream *st = track->demuxer->stream;
|
||||
if (!track->lang && (st->uncached_type == STREAMTYPE_BLURAY ||
|
||||
st->uncached_type == STREAMTYPE_DVD))
|
||||
{
|
||||
struct stream_lang_req req = {
|
||||
.type = track->type,
|
||||
.id = map_id_from_demuxer(track->demuxer, track->type,
|
||||
track->demuxer_id)
|
||||
};
|
||||
stream_control(track->demuxer->stream, STREAM_CTRL_GET_LANG, &req);
|
||||
stream_control(st, STREAM_CTRL_GET_LANG, &req);
|
||||
if (req.name[0])
|
||||
track->lang = talloc_strdup(track, req.name);
|
||||
}
|
||||
@@ -472,6 +475,8 @@ static void add_dvd_tracks(struct MPContext *mpctx)
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
struct stream *stream = demuxer->stream;
|
||||
struct stream_dvd_info_req info;
|
||||
if (stream->uncached_type != STREAMTYPE_DVD)
|
||||
return;
|
||||
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
|
||||
for (int n = 0; n < info.num_subs; n++) {
|
||||
struct track *track = talloc_ptrtype(NULL, track);
|
||||
@@ -667,6 +672,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
|
||||
}
|
||||
}
|
||||
|
||||
osd_changed_all(mpctx->osd);
|
||||
|
||||
talloc_free(mpctx->track_layout_hash);
|
||||
mpctx->track_layout_hash = talloc_steal(mpctx, track_layout_hash(mpctx));
|
||||
}
|
||||
@@ -1007,7 +1014,7 @@ static bool demux_was_interrupted(struct MPContext *mpctx)
|
||||
mp_cmd_t *cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
||||
if (!cmd)
|
||||
break;
|
||||
if (mp_input_is_abort_cmd(cmd->id))
|
||||
if (mp_input_is_abort_cmd(cmd))
|
||||
run_command(mpctx, cmd);
|
||||
mp_cmd_free(cmd);
|
||||
}
|
||||
@@ -1300,14 +1307,13 @@ goto_reopen_demuxer: ;
|
||||
|
||||
// If there's a timeline force an absolute seek to initialize state
|
||||
double startpos = rel_time_to_abs(mpctx, opts->play_start, -1);
|
||||
if (startpos != -1 || mpctx->timeline) {
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
||||
execute_queued_seek(mpctx);
|
||||
}
|
||||
if (startpos == -1 && mpctx->resolve_result &&
|
||||
mpctx->resolve_result->start_time > 0)
|
||||
{
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->resolve_result->start_time, 0);
|
||||
startpos = mpctx->resolve_result->start_time;
|
||||
if (startpos == -1 && mpctx->timeline)
|
||||
startpos = 0;
|
||||
if (startpos != -1) {
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
||||
execute_queued_seek(mpctx);
|
||||
}
|
||||
if (opts->chapterrange[0] > 0) {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
|
||||
|
||||
+27
-5
@@ -64,6 +64,14 @@
|
||||
#include "command.h"
|
||||
#include "screenshot.h"
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
|
||||
#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE (0x0001)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAVE_X11
|
||||
#include "video/out/x11_common.h"
|
||||
#endif
|
||||
@@ -76,10 +84,6 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
const char mp_help_text[] =
|
||||
"Usage: mpv [options] [url|path/]filename\n"
|
||||
"\n"
|
||||
@@ -260,7 +264,23 @@ static void osdep_preinit(int *p_argc, char ***p_argv)
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// stop Windows from showing all kinds of annoying error dialogs
|
||||
SetErrorMode(0x8003);
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
// Enable heap corruption detection
|
||||
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
||||
|
||||
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
|
||||
(WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(kernel32, "SetDllDirectoryW");
|
||||
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
|
||||
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(kernel32, "SetSearchPathMode");
|
||||
|
||||
// Always use safe search paths for DLLs and other files, ie. never use the
|
||||
// current directory
|
||||
if (pSetSearchPathMode)
|
||||
pSetDllDirectory(L"");
|
||||
if (pSetSearchPathMode)
|
||||
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
|
||||
#endif
|
||||
|
||||
terminal_init();
|
||||
@@ -381,8 +401,10 @@ static int mpv_main(int argc, char *argv[])
|
||||
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
|
||||
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
|
||||
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
|
||||
m_config_set_option0(mpctx->mconfig, "keep-open", "no");
|
||||
m_config_set_option0(mpctx->mconfig, "force-window", "no");
|
||||
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
|
||||
m_config_set_option0(mpctx->mconfig, "resume-playback", "no");
|
||||
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -106,6 +106,10 @@ double get_start_time(struct MPContext *mpctx)
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
if (!demuxer)
|
||||
return 0;
|
||||
// We reload the demuxer on menu transitions; don't make it use the first
|
||||
// timestamp it finds as start PTS.
|
||||
if (mpctx->nav_state)
|
||||
return 0;
|
||||
return demuxer_get_start_time(demuxer);
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -435,9 +435,12 @@ static void add_seek_osd_messages(struct MPContext *mpctx)
|
||||
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
|
||||
}
|
||||
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_TEXT) {
|
||||
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
|
||||
mpctx->opts->osd_duration);
|
||||
msg->show_position = true;
|
||||
// Never in term-osd mode
|
||||
if (mpctx->video_out && mpctx->opts->term_osd != 1) {
|
||||
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
|
||||
mpctx->opts->osd_duration);
|
||||
msg->show_position = true;
|
||||
}
|
||||
}
|
||||
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CHAPTER_TEXT) {
|
||||
char *chapter = chapter_display_name(mpctx, get_current_chapter(mpctx));
|
||||
|
||||
+19
-14
@@ -66,13 +66,13 @@ static const char av_desync_help_text[] =
|
||||
" Experiment with different values for --autosync, 30 is a good start.\n"
|
||||
" If you have PulseAudio, try --ao=alsa .\n"
|
||||
"- Slow video output.\n"
|
||||
" Try a different -vo driver (-vo help for a list) or try -framedrop!\n"
|
||||
" Try a different --vo driver (--vo=help for a list) or try --framedrop!\n"
|
||||
"- Playing a video file with --vo=opengl with higher FPS than the monitor.\n"
|
||||
" This is due to vsync limiting the framerate.\n"
|
||||
"- Playing from a slow network source.\n"
|
||||
" Download the file instead.\n"
|
||||
"- Try to find out whether audio or video is causing this by experimenting\n"
|
||||
" with --no-video and --no-audio.\n"
|
||||
"- Try to find out whether audio/video/subs are causing this by experimenting\n"
|
||||
" with --no-video, --no-audio, or --no-sub.\n"
|
||||
"- If you swiched audio or video tracks, try seeking to force synchronization.\n"
|
||||
"If none of this helps you, file a bug report.\n\n";
|
||||
|
||||
@@ -168,14 +168,14 @@ void add_step_frame(struct MPContext *mpctx, int dir)
|
||||
}
|
||||
}
|
||||
|
||||
static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
||||
static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
|
||||
{
|
||||
if (mpctx->d_video) {
|
||||
video_reset_decoding(mpctx->d_video);
|
||||
vo_seek_reset(mpctx->video_out);
|
||||
}
|
||||
|
||||
if (mpctx->d_audio) {
|
||||
if (mpctx->d_audio && reset_ac) {
|
||||
audio_reset_decoding(mpctx->d_audio);
|
||||
if (reset_ao)
|
||||
clear_audio_output_buffers(mpctx);
|
||||
@@ -204,9 +204,6 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
||||
}
|
||||
|
||||
// return -1 if seek failed (non-seekable stream?), 0 otherwise
|
||||
// timeline_fallthrough: true if used to explicitly switch timeline - in this
|
||||
// case, don't drop buffered AO audio data, so that
|
||||
// timeline segment transition is seemless
|
||||
static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
bool timeline_fallthrough)
|
||||
{
|
||||
@@ -253,6 +250,9 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
seek.amount += get_current_time(mpctx);
|
||||
}
|
||||
|
||||
/* At least the liba52 decoder wants to read from the input stream
|
||||
* during initialization, so reinit must be done after the demux_seek()
|
||||
* call that clears possible stream EOF. */
|
||||
bool need_reset = false;
|
||||
double demuxer_amount = seek.amount;
|
||||
if (mpctx->timeline) {
|
||||
@@ -271,7 +271,6 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
}
|
||||
if (need_reset) {
|
||||
reinit_video_chain(mpctx);
|
||||
reinit_audio_chain(mpctx);
|
||||
reinit_subs(mpctx, 0);
|
||||
reinit_subs(mpctx, 1);
|
||||
}
|
||||
@@ -296,8 +295,10 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
demuxer_amount -= hr_seek_offset;
|
||||
int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, demuxer_style);
|
||||
if (seekresult == 0) {
|
||||
if (need_reset)
|
||||
seek_reset(mpctx, !timeline_fallthrough);
|
||||
if (need_reset) {
|
||||
reinit_audio_chain(mpctx);
|
||||
seek_reset(mpctx, !timeline_fallthrough, false);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -315,7 +316,11 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
}
|
||||
}
|
||||
|
||||
seek_reset(mpctx, !timeline_fallthrough);
|
||||
if (need_reset)
|
||||
reinit_audio_chain(mpctx);
|
||||
/* If we just reinitialized audio it doesn't need to be reset,
|
||||
* and resetting could lose audio some decoders produce during init. */
|
||||
seek_reset(mpctx, !timeline_fallthrough, !need_reset);
|
||||
|
||||
if (timeline_fallthrough) {
|
||||
// Important if video reinit happens.
|
||||
@@ -571,7 +576,7 @@ bool mp_seek_chapter(struct MPContext *mpctx, int chapter)
|
||||
if (res >= 0) {
|
||||
if (pts == -1) {
|
||||
// for DVD/BD - seek happened via stream layer
|
||||
seek_reset(mpctx, true);
|
||||
seek_reset(mpctx, true, true);
|
||||
mpctx->seek = (struct seek_params){0};
|
||||
return true;
|
||||
}
|
||||
@@ -924,7 +929,7 @@ static double get_wakeup_period(struct MPContext *mpctx)
|
||||
*/
|
||||
double sleeptime = WAKEUP_PERIOD;
|
||||
|
||||
#ifndef HAVE_POSIX_SELECT
|
||||
#if !HAVE_POSIX_SELECT
|
||||
// No proper file descriptor event handling; keep waking up to poll input
|
||||
sleeptime = MPMIN(sleeptime, 0.02);
|
||||
#endif
|
||||
|
||||
@@ -164,7 +164,7 @@ static void copy_chapters(struct chapter **chapters, int *num_chapters,
|
||||
double time = demuxer_chapter_time(src, n);
|
||||
if (time >= start && time <= start + len) {
|
||||
struct chapter ch = {
|
||||
.start = dest_offset + time,
|
||||
.start = dest_offset + time - start,
|
||||
.name = talloc_steal(*chapters, demuxer_chapter_name(src, n)),
|
||||
};
|
||||
MP_TARRAY_APPEND(NULL, *chapters, *num_chapters, ch);
|
||||
|
||||
+34
-12
@@ -33,6 +33,9 @@
|
||||
// the cache is active.
|
||||
#define CACHE_UPDATE_CONTROLS_TIME 2.0
|
||||
|
||||
// Time in seconds the cache prints a new message at all.
|
||||
#define CACHE_NO_SPAM 5.0
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -72,11 +75,13 @@ struct priv {
|
||||
int64_t fill_limit; // we should fill buffer only if space>=fill_limit
|
||||
int64_t seek_limit; // keep filling cache if distance is less that seek limit
|
||||
struct byte_meta *bm; // additional per-byte metadata
|
||||
bool seekable; // underlying stream is seekable
|
||||
|
||||
struct mp_log *log;
|
||||
|
||||
// Owned by the main thread
|
||||
stream_t *cache; // wrapper stream, used by demuxer etc.
|
||||
double last_warn_time;
|
||||
|
||||
// Owned by the cache thread
|
||||
stream_t *stream; // "real" stream, used to read from the source media
|
||||
@@ -142,15 +147,19 @@ static int cache_wakeup_and_wait(struct priv *s, double *retry_time)
|
||||
if (stream_check_interrupt(0))
|
||||
return CACHE_INTERRUPTED;
|
||||
|
||||
// Print a "more severe" warning after waiting 1 second and no new data
|
||||
if ((*retry_time) >= 1.0) {
|
||||
MP_ERR(s, "Cache keeps not responding.\n");
|
||||
} else if (*retry_time > 0.1) {
|
||||
MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
|
||||
}
|
||||
|
||||
double start = mp_time_sec();
|
||||
|
||||
if (!s->last_warn_time || start - s->last_warn_time >= CACHE_NO_SPAM) {
|
||||
// Print a "more severe" warning after waiting 1 second and no new data
|
||||
if ((*retry_time) >= 1.0) {
|
||||
MP_ERR(s, "Cache keeps not responding.\n");
|
||||
s->last_warn_time = start;
|
||||
} else if (*retry_time > 0.1) {
|
||||
MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
|
||||
s->last_warn_time = start;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
mpthread_cond_timed_wait(&s->wakeup, &s->mutex, CACHE_WAIT_TIME);
|
||||
|
||||
@@ -467,6 +476,7 @@ static int cache_seek(stream_t *cache, int64_t pos)
|
||||
{
|
||||
struct priv *s = cache->priv;
|
||||
assert(s->cache_thread_running);
|
||||
int r = 1;
|
||||
|
||||
pthread_mutex_lock(&s->mutex);
|
||||
|
||||
@@ -474,12 +484,21 @@ static int cache_seek(stream_t *cache, int64_t pos)
|
||||
" (cur=%" PRId64 ") <= %" PRId64 " \n",
|
||||
s->min_filepos, pos, s->read_filepos, s->max_filepos);
|
||||
|
||||
cache->pos = s->read_filepos = pos;
|
||||
s->eof = false; // so that cache_read() will actually wait for new data
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
if (!s->seekable && pos > s->max_filepos) {
|
||||
MP_ERR(s, "Attempting to seek past cached data in unseekable stream.\n");
|
||||
r = 0;
|
||||
} else if (!s->seekable && pos < s->min_filepos) {
|
||||
MP_ERR(s, "Attempting to seek before cached data in unseekable stream.\n");
|
||||
r = 0;
|
||||
} else {
|
||||
cache->pos = s->read_filepos = pos;
|
||||
s->eof = false; // so that cache_read() will actually wait for new data
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
|
||||
return 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int cache_control(stream_t *cache, int cmd, void *arg)
|
||||
@@ -495,7 +514,7 @@ static int cache_control(stream_t *cache, int cmd, void *arg)
|
||||
if (r != STREAM_ERROR)
|
||||
goto done;
|
||||
|
||||
MP_VERBOSE(s, "[cache] blocking for STREAM_CTRL %d\n", cmd);
|
||||
MP_VERBOSE(s, "blocking for STREAM_CTRL %d\n", cmd);
|
||||
|
||||
s->control = cmd;
|
||||
s->control_arg = arg;
|
||||
@@ -592,6 +611,9 @@ int stream_cache_init(stream_t *cache, stream_t *stream, int64_t size,
|
||||
if (min > s->buffer_size - s->fill_limit)
|
||||
min = s->buffer_size - s->fill_limit;
|
||||
|
||||
s->seekable = (stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK &&
|
||||
stream->end_pos > 0;
|
||||
|
||||
if (pthread_create(&s->cache_thread, NULL, cache_thread, s) != 0) {
|
||||
MP_ERR(s, "Starting cache process/thread failed: %s.\n",
|
||||
strerror(errno));
|
||||
|
||||
@@ -1,58 +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_CDD_H
|
||||
#define MPLAYER_CDD_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct mp_log;
|
||||
|
||||
typedef struct cd_track {
|
||||
char *name;
|
||||
unsigned int track_nb;
|
||||
unsigned int min;
|
||||
unsigned int sec;
|
||||
unsigned int msec;
|
||||
unsigned long frame_begin;
|
||||
unsigned long frame_length;
|
||||
struct cd_track *prev;
|
||||
struct cd_track *next;
|
||||
} cd_track_t;
|
||||
|
||||
typedef struct {
|
||||
char *artist;
|
||||
char *album;
|
||||
char *genre;
|
||||
unsigned int nb_tracks;
|
||||
unsigned int min;
|
||||
unsigned int sec;
|
||||
unsigned msec;
|
||||
cd_track_t *first;
|
||||
cd_track_t *last;
|
||||
cd_track_t *current;
|
||||
} cd_info_t;
|
||||
|
||||
cd_info_t* cd_info_new(void);
|
||||
void cd_info_free(cd_info_t *cd_info);
|
||||
cd_track_t* cd_info_add_track(cd_info_t *cd_info, char *track_name, unsigned int track_nb, unsigned int min, unsigned int sec, unsigned int msec, unsigned long frame_begin, unsigned long frame_length);
|
||||
cd_track_t* cd_info_get_track(cd_info_t *cd_info, unsigned int track_nb);
|
||||
|
||||
void cd_info_debug(cd_info_t *cd_info, struct mp_log *log);
|
||||
|
||||
#endif /* MPLAYER_CDD_H */
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* CD Info
|
||||
*
|
||||
* Copyright (C) 2002 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "common/msg.h"
|
||||
#include "cdd.h"
|
||||
|
||||
/*******************************************************************************************************************
|
||||
*
|
||||
* xmcd parser, cd info list
|
||||
*
|
||||
*******************************************************************************************************************/
|
||||
|
||||
cd_info_t*
|
||||
cd_info_new(void) {
|
||||
cd_info_t *cd_info = NULL;
|
||||
|
||||
cd_info = malloc(sizeof(cd_info_t));
|
||||
if( cd_info==NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(cd_info, 0, sizeof(cd_info_t));
|
||||
|
||||
return cd_info;
|
||||
}
|
||||
|
||||
void
|
||||
cd_info_free(cd_info_t *cd_info) {
|
||||
cd_track_t *cd_track, *cd_track_next;
|
||||
if( cd_info==NULL ) return;
|
||||
free(cd_info->artist);
|
||||
free(cd_info->album);
|
||||
free(cd_info->genre);
|
||||
|
||||
cd_track_next = cd_info->first;
|
||||
while( cd_track_next!=NULL ) {
|
||||
cd_track = cd_track_next;
|
||||
cd_track_next = cd_track->next;
|
||||
free(cd_track->name);
|
||||
free(cd_track);
|
||||
}
|
||||
}
|
||||
|
||||
cd_track_t*
|
||||
cd_info_add_track(cd_info_t *cd_info, char *track_name, unsigned int track_nb, unsigned int min, unsigned int sec, unsigned int msec, unsigned long frame_begin, unsigned long frame_length) {
|
||||
cd_track_t *cd_track;
|
||||
|
||||
if( cd_info==NULL || track_name==NULL ) return NULL;
|
||||
|
||||
cd_track = malloc(sizeof(cd_track_t));
|
||||
if( cd_track==NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
memset(cd_track, 0, sizeof(cd_track_t));
|
||||
|
||||
cd_track->name = malloc(strlen(track_name)+1);
|
||||
if( cd_track->name==NULL ) {
|
||||
free(cd_track);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(cd_track->name, track_name);
|
||||
cd_track->track_nb = track_nb;
|
||||
cd_track->min = min;
|
||||
cd_track->sec = sec;
|
||||
cd_track->msec = msec;
|
||||
cd_track->frame_begin = frame_begin;
|
||||
cd_track->frame_length = frame_length;
|
||||
|
||||
if( cd_info->first==NULL ) {
|
||||
cd_info->first = cd_track;
|
||||
}
|
||||
if( cd_info->last!=NULL ) {
|
||||
cd_info->last->next = cd_track;
|
||||
}
|
||||
|
||||
cd_track->prev = cd_info->last;
|
||||
|
||||
cd_info->last = cd_track;
|
||||
cd_info->current = cd_track;
|
||||
|
||||
cd_info->nb_tracks++;
|
||||
|
||||
return cd_track;
|
||||
}
|
||||
|
||||
cd_track_t*
|
||||
cd_info_get_track(cd_info_t *cd_info, unsigned int track_nb) {
|
||||
cd_track_t *cd_track=NULL;
|
||||
|
||||
if( cd_info==NULL ) return NULL;
|
||||
|
||||
cd_track = cd_info->first;
|
||||
while( cd_track!=NULL ) {
|
||||
if( cd_track->track_nb==track_nb ) {
|
||||
return cd_track;
|
||||
}
|
||||
cd_track = cd_track->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cd_info_debug(cd_info_t *cd_info, struct mp_log *log)
|
||||
{
|
||||
cd_track_t *current_track;
|
||||
mp_info(log, "================ CD INFO === start =========\n");
|
||||
if( cd_info==NULL ) {
|
||||
mp_info(log, "cd_info is NULL\n");
|
||||
return;
|
||||
}
|
||||
mp_info(log, " artist=[%s]\n", cd_info->artist);
|
||||
mp_info(log, " album=[%s]\n", cd_info->album);
|
||||
mp_info(log, " genre=[%s]\n", cd_info->genre);
|
||||
mp_info(log, " nb_tracks=%d\n", cd_info->nb_tracks);
|
||||
mp_info(log, " length= %2d:%02d.%02d\n", cd_info->min, cd_info->sec, cd_info->msec);
|
||||
|
||||
mp_info(log, "ID_CDDB_INFO_ARTIST=%s\n", cd_info->artist);
|
||||
mp_info(log, "ID_CDDB_INFO_ALBUM=%s\n", cd_info->album);
|
||||
mp_info(log, "ID_CDDB_INFO_GENRE=%s\n", cd_info->genre);
|
||||
mp_info(log, "ID_CDDB_INFO_LENGTH_MSF=%02d:%02d.%02d\n", cd_info->min, cd_info->sec, cd_info->msec);
|
||||
mp_info(log, "ID_CDDB_INFO_TRACKS=%d\n", cd_info->nb_tracks);
|
||||
|
||||
current_track = cd_info->first;
|
||||
while( current_track!=NULL ) {
|
||||
mp_info(log, " #%2d %2d:%02d.%02d @ %7ld\t[%s] \n", current_track->track_nb, current_track->min, current_track->sec, current_track->msec, current_track->frame_begin, current_track->name);
|
||||
mp_info(log, "ID_CDDB_INFO_TRACK_%d_NAME=%s\n", current_track->track_nb, current_track->name);
|
||||
mp_info(log, "ID_CDDB_INFO_TRACK_%d_MSF=%02d:%02d.%02d\n", current_track->track_nb, current_track->min, current_track->sec, current_track->msec);
|
||||
current_track = current_track->next;
|
||||
}
|
||||
mp_info(log, "================ CD INFO === end =========\n");
|
||||
}
|
||||
+1
-1
@@ -61,7 +61,7 @@ static char *col_dup(void *talloc_ctx, const char *src)
|
||||
}
|
||||
|
||||
/* Finds the start of all the columns */
|
||||
static int parse_line(char **ptr, char *cols[6])
|
||||
static int parse_line(char **ptr, char *cols[7])
|
||||
{
|
||||
int col;
|
||||
cols[0] = *ptr;
|
||||
|
||||
@@ -111,7 +111,7 @@ struct mp_resolve_result *mp_resolve_quvi(const char *url,
|
||||
quvi_media_free(media);
|
||||
}
|
||||
|
||||
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_SUBTITLE)) {
|
||||
if (opts->quvi_fetch_subtitles && quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_SUBTITLE)) {
|
||||
mp_info(log, "Getting subtitles...\n");
|
||||
quvi_subtitle_t qsub = quvi_subtitle_new(q, url);
|
||||
if (mp_quvi_ok(q, log)) {
|
||||
|
||||
+8
-2
@@ -396,16 +396,22 @@ stream_t *open_output_stream(const char *filename, struct mpv_global *global)
|
||||
static int stream_reconnect(stream_t *s)
|
||||
{
|
||||
#define MAX_RECONNECT_RETRIES 5
|
||||
#define RECONNECT_SLEEP_MS 1000
|
||||
#define RECONNECT_SLEEP_MAX_MS 500
|
||||
if (!s->streaming)
|
||||
return 0;
|
||||
if (!(s->flags & MP_STREAM_SEEK_FW))
|
||||
return 0;
|
||||
int64_t pos = s->pos;
|
||||
int sleep_ms = 5;
|
||||
for (int retry = 0; retry < MAX_RECONNECT_RETRIES; retry++) {
|
||||
MP_WARN(s, "Connection lost! Attempting to reconnect (%d)...\n", retry + 1);
|
||||
|
||||
if (stream_check_interrupt(retry ? RECONNECT_SLEEP_MS : 0))
|
||||
if (retry) {
|
||||
mp_sleep_us(sleep_ms * 1000);
|
||||
sleep_ms = MPMIN(sleep_ms * 2, RECONNECT_SLEEP_MAX_MS);
|
||||
}
|
||||
|
||||
if (stream_check_interrupt(0))
|
||||
return 0;
|
||||
|
||||
s->eof = 1;
|
||||
|
||||
@@ -35,6 +35,7 @@ enum streamtype {
|
||||
STREAMTYPE_RADIO,
|
||||
STREAMTYPE_DVB,
|
||||
STREAMTYPE_DVD,
|
||||
STREAMTYPE_BLURAY,
|
||||
STREAMTYPE_PVR,
|
||||
STREAMTYPE_TV,
|
||||
STREAMTYPE_MF,
|
||||
|
||||
@@ -187,6 +187,7 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
|
||||
return STREAM_UNSUPPORTED;
|
||||
|
||||
*((double *) arg) = BD_TIME_TO_MP(ti->duration);
|
||||
bd_free_title_info(ti);
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
@@ -390,6 +391,7 @@ err_no_info:
|
||||
b->current_angle = angle;
|
||||
b->current_title = title;
|
||||
|
||||
s->type = STREAMTYPE_BLURAY;
|
||||
s->end_pos = title_size;
|
||||
s->sector_size = BLURAY_SECTOR_SIZE;
|
||||
s->flags = MP_STREAM_SEEK;
|
||||
|
||||
+16
-68
@@ -46,8 +46,6 @@
|
||||
#include "libavutil/common.h"
|
||||
#include "compat/mpbswap.h"
|
||||
|
||||
#include "cdd.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -56,7 +54,6 @@ typedef struct {
|
||||
int sector;
|
||||
int start_sector;
|
||||
int end_sector;
|
||||
cd_info_t *cd_info;
|
||||
|
||||
// options
|
||||
int speed;
|
||||
@@ -132,8 +129,8 @@ static bool print_cdtext(stream_t *s, int track)
|
||||
#else
|
||||
cdtext_t *text = cdio_get_cdtext(p->cd->p_cdio);
|
||||
#endif
|
||||
int header = 0;
|
||||
if (text) {
|
||||
MP_INFO(s, "CD-Text (%s):\n", track ? "track" : "CD");
|
||||
for (int i = 0; i < sizeof(cdtext_name) / sizeof(cdtext_name[0]); i++) {
|
||||
const char *name = cdtext_name[i];
|
||||
#ifdef OLD_API
|
||||
@@ -141,8 +138,12 @@ static bool print_cdtext(stream_t *s, int track)
|
||||
#else
|
||||
const char *value = cdtext_get_const(text, i, track);
|
||||
#endif
|
||||
if (name && value)
|
||||
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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -151,17 +152,8 @@ static bool print_cdtext(stream_t *s, int track)
|
||||
|
||||
static void print_track_info(stream_t *s, int track)
|
||||
{
|
||||
cdda_priv* p = (cdda_priv*)s->priv;
|
||||
cd_track_t *cd_track = cd_info_get_track(p->cd_info, track);
|
||||
if( cd_track!=NULL ) {
|
||||
MP_INFO(s, "\n%s\n", cd_track->name);
|
||||
MP_INFO(s, "ID_CDDA_TRACK=%d\n",
|
||||
cd_track->track_nb);
|
||||
}
|
||||
if (print_cdtext(s, track)) {
|
||||
// hack for term OSD overwriting the last line of CDTEXT
|
||||
MP_INFO(s, "\n");
|
||||
}
|
||||
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)
|
||||
@@ -242,7 +234,6 @@ static void close_cdda(stream_t *s)
|
||||
cdda_priv *p = (cdda_priv *)s->priv;
|
||||
paranoia_free(p->cdp);
|
||||
cdda_close(p->cd);
|
||||
cd_info_free(p->cd_info);
|
||||
free(p);
|
||||
}
|
||||
|
||||
@@ -273,37 +264,18 @@ static int control(stream_t *stream, int cmd, void *arg)
|
||||
*(unsigned int *)arg = end_track + 1 - start_track;
|
||||
return STREAM_OK;
|
||||
}
|
||||
case STREAM_CTRL_SEEK_TO_CHAPTER:
|
||||
case STREAM_CTRL_GET_CHAPTER_TIME:
|
||||
{
|
||||
int r;
|
||||
unsigned int track = *(unsigned int *)arg;
|
||||
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);
|
||||
int seek_sector;
|
||||
if (start_track == -1 || end_track == -1)
|
||||
track += start_track + 1;
|
||||
if (track > end_track)
|
||||
return STREAM_ERROR;
|
||||
track += start_track;
|
||||
if (track > end_track) {
|
||||
seek(stream, (p->end_sector + 1) * CDIO_CD_FRAMESIZE_RAW);
|
||||
// seeking beyond EOF should not be an error,
|
||||
// the cache cannot handle changing stream pos and
|
||||
// returning error.
|
||||
return STREAM_OK;
|
||||
}
|
||||
seek_sector = track == 0 ? p->start_sector
|
||||
: p->cd->disc_toc[track].dwStartSector;
|
||||
r = seek(stream, seek_sector * CDIO_CD_FRAMESIZE_RAW);
|
||||
if (r)
|
||||
return STREAM_OK;
|
||||
break;
|
||||
}
|
||||
case STREAM_CTRL_GET_CURRENT_CHAPTER:
|
||||
{
|
||||
int start_track = get_track_by_sector(p, p->start_sector);
|
||||
int cur_track = get_track_by_sector(p, p->sector);
|
||||
if (start_track == -1 || cur_track == -1)
|
||||
return STREAM_ERROR;
|
||||
*(unsigned int *)arg = cur_track - start_track;
|
||||
int64_t sector = p->cd->disc_toc[track].dwStartSector;
|
||||
int64_t pos = sector * (CDIO_CD_FRAMESIZE_RAW + 1) - 1;
|
||||
// Assume standard audio CD: 44.1khz, 2 channels, s16 samples
|
||||
*(double *)arg = pos / (44100.0 * 2 * 2);
|
||||
return STREAM_OK;
|
||||
}
|
||||
}
|
||||
@@ -317,10 +289,7 @@ static int open_cdda(stream_t *st, int m)
|
||||
int mode = p->paranoia_mode;
|
||||
int offset = p->toc_offset;
|
||||
cdrom_drive_t *cdd = NULL;
|
||||
cd_info_t *cd_info;
|
||||
unsigned int audiolen = 0;
|
||||
int last_track;
|
||||
int i;
|
||||
|
||||
if (m != STREAM_READ) {
|
||||
return STREAM_UNSUPPORTED;
|
||||
@@ -355,29 +324,9 @@ static int open_cdda(stream_t *st, int m)
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
cd_info = cd_info_new();
|
||||
MP_INFO(st, "Found audio CD with %d tracks.\n",
|
||||
(int)cdda_tracks(cdd));
|
||||
for (i = 0; i < cdd->tracks; i++) {
|
||||
char track_name[80];
|
||||
long sec = cdda_track_firstsector(cdd, i + 1);
|
||||
long off = cdda_track_lastsector(cdd, i + 1) - sec + 1;
|
||||
|
||||
sprintf(track_name, "Track %d", i + 1);
|
||||
cd_info_add_track(cd_info, track_name, i + 1,
|
||||
(unsigned int)(off / (60 * 75)),
|
||||
(unsigned int)((off / 75) % 60),
|
||||
(unsigned int)(off % 75), sec, off);
|
||||
audiolen += off;
|
||||
}
|
||||
cd_info->min = (unsigned int)(audiolen / (60 * 75));
|
||||
cd_info->sec = (unsigned int)((audiolen / 75) % 60);
|
||||
cd_info->msec = (unsigned int)(audiolen % 75);
|
||||
|
||||
priv = malloc(sizeof(cdda_priv));
|
||||
memset(priv, 0, sizeof(cdda_priv));
|
||||
priv->cd = cdd;
|
||||
priv->cd_info = cd_info;
|
||||
|
||||
if (p->toc_bias)
|
||||
offset -= cdda_track_firstsector(cdd, 1);
|
||||
@@ -411,7 +360,6 @@ static int open_cdda(stream_t *st, int m)
|
||||
if (priv->cdp == NULL) {
|
||||
cdda_close(cdd);
|
||||
free(priv);
|
||||
cd_info_free(cd_info);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
+37
-30
@@ -298,42 +298,47 @@ static void handle_cmd(stream_t *s, struct mp_nav_cmd *ev)
|
||||
|
||||
}
|
||||
|
||||
static inline bool set_event_type(struct priv *priv, int type,
|
||||
struct mp_nav_event *event)
|
||||
{
|
||||
if (!(priv->next_event & (1 << type)))
|
||||
return false;
|
||||
priv->next_event &= ~(1 << type);
|
||||
event->event = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fill_next_event(stream_t *s, struct mp_nav_event **ret)
|
||||
{
|
||||
struct priv *priv = s->priv;
|
||||
struct mp_nav_event e = {0};
|
||||
for (int n = 0; n < 30; n++) {
|
||||
if (priv->next_event & (1 << n)) {
|
||||
priv->next_event &= ~(1 << n);
|
||||
e.event = n;
|
||||
switch (e.event) {
|
||||
case MP_NAV_EVENT_HIGHLIGHT: {
|
||||
dvdnav_highlight_event_t hlev = priv->hlev;
|
||||
e.u.highlight.display = hlev.display;
|
||||
e.u.highlight.sx = hlev.sx;
|
||||
e.u.highlight.sy = hlev.sy;
|
||||
e.u.highlight.ex = hlev.ex;
|
||||
e.u.highlight.ey = hlev.ey;
|
||||
e.u.highlight.palette = hlev.palette;
|
||||
break;
|
||||
}
|
||||
case MP_NAV_EVENT_MENU_MODE:
|
||||
e.u.menu_mode.enable = !dvdnav_is_domain_vts(priv->dvdnav);
|
||||
break;
|
||||
case MP_NAV_EVENT_STILL_FRAME:
|
||||
e.u.still_frame.seconds = priv->still_length;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!set_event_type(priv, MP_NAV_EVENT_RESET_ALL, &e))
|
||||
for (int n = 0; n < 30 && !set_event_type(priv, n, &e); n++) ;
|
||||
switch (e.event) {
|
||||
case MP_NAV_EVENT_NONE:
|
||||
return;
|
||||
case MP_NAV_EVENT_HIGHLIGHT: {
|
||||
dvdnav_highlight_event_t hlev = priv->hlev;
|
||||
e.u.highlight.display = hlev.display;
|
||||
e.u.highlight.sx = hlev.sx;
|
||||
e.u.highlight.sy = hlev.sy;
|
||||
e.u.highlight.ex = hlev.ex;
|
||||
e.u.highlight.ey = hlev.ey;
|
||||
e.u.highlight.palette = hlev.palette;
|
||||
break;
|
||||
}
|
||||
if (e.event) {
|
||||
*ret = talloc(NULL, struct mp_nav_event);
|
||||
**ret = e;
|
||||
case MP_NAV_EVENT_MENU_MODE:
|
||||
e.u.menu_mode.enable = !dvdnav_is_domain_vts(priv->dvdnav);
|
||||
break;
|
||||
case MP_NAV_EVENT_STILL_FRAME:
|
||||
e.u.still_frame.seconds = priv->still_length;
|
||||
break;
|
||||
}
|
||||
*ret = talloc(NULL, struct mp_nav_event);
|
||||
**ret = e;
|
||||
|
||||
MP_VERBOSE(s, "DVDNAV: player event '%s'\n",
|
||||
LOOKUP_NAME(mp_nav_event_types, e.event));
|
||||
}
|
||||
MP_VERBOSE(s, "DVDNAV: player event '%s'\n",
|
||||
LOOKUP_NAME(mp_nav_event_types, e.event));
|
||||
}
|
||||
|
||||
static int fill_buffer(stream_t *s, char *buf, int max_len)
|
||||
@@ -414,6 +419,8 @@ static int fill_buffer(stream_t *s, char *buf, int max_len)
|
||||
priv->had_initial_vts = true;
|
||||
break;
|
||||
}
|
||||
// clear all previous events
|
||||
priv->next_event = 0;
|
||||
priv->next_event |= 1 << MP_NAV_EVENT_RESET;
|
||||
priv->next_event |= 1 << MP_NAV_EVENT_RESET_ALL;
|
||||
if (dvdnav_current_title_info(dvdnav, &tit, &part) == DVDNAV_STATUS_OK)
|
||||
|
||||
@@ -33,6 +33,7 @@ mf_stream_open (stream_t *stream, int mode)
|
||||
{
|
||||
stream->type = STREAMTYPE_MF;
|
||||
stream->demuxer = "mf";
|
||||
stream->allow_caching = false;
|
||||
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ pvr_uninit (struct pvr_t *pvr)
|
||||
return;
|
||||
|
||||
/* close device */
|
||||
if (pvr->dev_fd)
|
||||
if (pvr->dev_fd != -1)
|
||||
close (pvr->dev_fd);
|
||||
|
||||
free (pvr->video_dev);
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ static mp_vcd_priv_t* vcd_read_toc(stream_t *stream, int fd){
|
||||
|
||||
static int vcd_read(mp_vcd_priv_t* vcd,char *mem){
|
||||
#ifndef sun
|
||||
memcpy(vcd->buf,&vcd->entry.cdte_addr.msf,sizeof(struct cdrom_msf));
|
||||
memcpy(vcd->buf,&vcd->entry.cdte_addr.msf,sizeof(vcd->entry.cdte_addr.msf));
|
||||
if(ioctl(vcd->fd,CDROMREADRAW,vcd->buf)==-1) return 0; // EOF?
|
||||
memcpy(mem,&vcd->buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA);
|
||||
#else
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ void mp_ass_set_style(ASS_Style *style, double res_y,
|
||||
void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts)
|
||||
{
|
||||
if (opts->ass_styles_file && opts->ass_style_override)
|
||||
ass_read_styles(track, opts->ass_styles_file, opts->sub_cp);
|
||||
ass_read_styles(track, opts->ass_styles_file, NULL);
|
||||
|
||||
if (track->n_styles == 0) {
|
||||
if (!track->PlayResY) {
|
||||
|
||||
+3
-1
@@ -175,6 +175,8 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh)
|
||||
struct sd init_sd = sub->init_sd;
|
||||
init_sd.codec = sh->codec;
|
||||
init_sd.ass_track = sh->sub->track;
|
||||
init_sd.sub_stream_w = sh->sub->w;
|
||||
init_sd.sub_stream_h = sh->sub->h;
|
||||
|
||||
while (sub->num_sd < MAX_NUM_SD) {
|
||||
struct sd *sd = talloc(NULL, struct sd);
|
||||
@@ -420,7 +422,7 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh)
|
||||
|
||||
// 23.976 FPS is used as default timebase for frame based formats
|
||||
if (sub->video_fps && sh->sub->frame_based)
|
||||
sub_speed *= sub->video_fps / 23.976;
|
||||
sub_speed *= 23.976 / sub->video_fps;
|
||||
|
||||
if (opts->sub_fps && sub->video_fps)
|
||||
sub_speed *= opts->sub_fps / sub->video_fps;
|
||||
|
||||
@@ -52,11 +52,9 @@ struct sub_bitmap {
|
||||
int x, y;
|
||||
int dw, dh;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t color;
|
||||
} libass;
|
||||
};
|
||||
struct {
|
||||
uint32_t color;
|
||||
} libass;
|
||||
};
|
||||
|
||||
struct sub_bitmaps {
|
||||
|
||||
@@ -25,6 +25,9 @@ struct sd {
|
||||
// the resolution of the VO, nor does it have to be the OSD resolution.
|
||||
int sub_video_w, sub_video_h;
|
||||
|
||||
// Resolution hints stored in mp4 files.
|
||||
int sub_stream_w, sub_stream_h;
|
||||
|
||||
// Make sd_ass use an existing track
|
||||
struct ass_track *ass_track;
|
||||
|
||||
|
||||
+4
-1
@@ -19,6 +19,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
#include <ass/ass.h>
|
||||
@@ -142,9 +143,11 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
|
||||
opts->ass_vsfilter_aspect_compat))
|
||||
{
|
||||
// Let's use the original video PAR for vsfilter compatibility:
|
||||
scale = scale
|
||||
double par = scale
|
||||
* (ctx->video_params.d_w / (double)ctx->video_params.d_h)
|
||||
/ (ctx->video_params.w / (double)ctx->video_params.h);
|
||||
if (isnormal(par))
|
||||
scale = par;
|
||||
}
|
||||
mp_ass_configure(renderer, opts, &dim);
|
||||
ass_set_aspect_ratio(renderer, scale, 1);
|
||||
|
||||
+50
-5
@@ -17,15 +17,18 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/av_common.h"
|
||||
#include "options/options.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "video/csputils.h"
|
||||
#include "sd.h"
|
||||
#include "dec_sub.h"
|
||||
|
||||
@@ -88,9 +91,42 @@ static void get_resolution(struct sd *sd, int wh[2])
|
||||
struct sd_lavc_priv *priv = sd->priv;
|
||||
wh[0] = priv->avctx->width;
|
||||
wh[1] = priv->avctx->height;
|
||||
if (wh[0] <= 0 || wh[1] <= 0) {
|
||||
wh[0] = priv->video_params.w;
|
||||
wh[1] = priv->video_params.h;
|
||||
}
|
||||
guess_resolution(priv->avctx->codec_id, &wh[0], &wh[1]);
|
||||
}
|
||||
|
||||
static void set_mp4_vobsub_idx(AVCodecContext *avctx, char *src, int w, int h)
|
||||
{
|
||||
char pal_s[128];
|
||||
int pal_s_pos = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
unsigned int e = AV_RB32(src + i * 4);
|
||||
|
||||
// lavc doesn't accept YUV palette - "does god hate me?"
|
||||
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
|
||||
csp.int_bits_in = 8;
|
||||
csp.int_bits_out = 8;
|
||||
float cmatrix[3][4];
|
||||
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
|
||||
int c[3] = {(e >> 16) & 0xff, (e >> 8) & 0xff, e & 0xff};
|
||||
mp_map_int_color(cmatrix, 8, c);
|
||||
e = (c[2] << 16) | (c[1] << 8) | c[0];
|
||||
|
||||
snprintf(pal_s + pal_s_pos, sizeof(pal_s) - pal_s_pos, "%06x%s", e,
|
||||
i != 15 ? ", " : "");
|
||||
pal_s_pos = strlen(pal_s);
|
||||
if (pal_s_pos >= sizeof(pal_s))
|
||||
break;
|
||||
}
|
||||
|
||||
char buf[256] = "";
|
||||
snprintf(buf, sizeof(buf), "size: %dx%d\npalette: %s\n", w, h, pal_s);
|
||||
mp_lavc_set_extradata(avctx, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static int init(struct sd *sd)
|
||||
{
|
||||
struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
|
||||
@@ -102,8 +138,12 @@ static int init(struct sd *sd)
|
||||
ctx = avcodec_alloc_context3(sub_codec);
|
||||
if (!ctx)
|
||||
goto error;
|
||||
ctx->extradata_size = sd->extradata_len;
|
||||
ctx->extradata = sd->extradata;
|
||||
mp_lavc_set_extradata(ctx, sd->extradata, sd->extradata_len);
|
||||
if (sd->extradata_len == 64 && sd->sub_stream_w && sd->sub_stream_h &&
|
||||
cid == AV_CODEC_ID_DVD_SUBTITLE)
|
||||
{
|
||||
set_mp4_vobsub_idx(ctx, sd->extradata, sd->sub_stream_w, sd->sub_stream_h);
|
||||
}
|
||||
if (avcodec_open2(ctx, sub_codec, NULL) < 0)
|
||||
goto error;
|
||||
priv->avctx = ctx;
|
||||
@@ -182,11 +222,13 @@ static void decode(struct sd *sd, struct demux_packet *packet)
|
||||
sub.num_rects);
|
||||
for (int i = 0; i < sub.num_rects; i++) {
|
||||
struct AVSubtitleRect *r = sub.rects[i];
|
||||
struct sub_bitmap *b = &priv->inbitmaps[i];
|
||||
struct osd_bmp_indexed *img = &priv->imgs[i];
|
||||
struct sub_bitmap *b = &priv->inbitmaps[priv->count];
|
||||
struct osd_bmp_indexed *img = &priv->imgs[priv->count];
|
||||
if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) &&
|
||||
opts->forced_subs_only)
|
||||
continue;
|
||||
if (r->w == 0 || r->h == 0)
|
||||
continue;
|
||||
img->bitmap = r->pict.data[0];
|
||||
assert(r->nb_colors > 0);
|
||||
assert(r->nb_colors * 4 <= sizeof(img->palette));
|
||||
@@ -234,9 +276,11 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
|
||||
if (priv->avctx->codec_id == AV_CODEC_ID_DVD_SUBTITLE &&
|
||||
opts->stretch_dvd_subs) {
|
||||
// For DVD subs, try to keep the subtitle PAR at display PAR.
|
||||
video_par =
|
||||
double par =
|
||||
(priv->video_params.d_w / (double)priv->video_params.d_h)
|
||||
/ (priv->video_params.w / (double)priv->video_params.h);
|
||||
if (isnormal(par))
|
||||
video_par = par;
|
||||
}
|
||||
int insize[2];
|
||||
get_resolution(sd, insize);
|
||||
@@ -259,6 +303,7 @@ static void uninit(struct sd *sd)
|
||||
|
||||
clear(priv);
|
||||
avcodec_close(priv->avctx);
|
||||
av_free(priv->avctx->extradata);
|
||||
av_free(priv->avctx);
|
||||
talloc_free(priv);
|
||||
}
|
||||
|
||||
+2
-30
@@ -26,7 +26,6 @@
|
||||
|
||||
struct sd_spu_priv {
|
||||
void *spudec;
|
||||
struct mp_image_params video_params;
|
||||
};
|
||||
|
||||
static bool is_dvd_sub(const char *t)
|
||||
@@ -72,22 +71,8 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
|
||||
spudec_set_forced_subs_only(priv->spudec, opts->forced_subs_only);
|
||||
spudec_heartbeat(priv->spudec, pts * 90000);
|
||||
|
||||
if (spudec_visible(priv->spudec)) {
|
||||
double xscale = 1;
|
||||
double yscale = 1;
|
||||
if (opts->stretch_dvd_subs) {
|
||||
// For DVD subs, try to keep the subtitle PAR at display PAR.
|
||||
double video_par =
|
||||
(priv->video_params.d_w / (double)priv->video_params.d_h)
|
||||
/ (priv->video_params.w / (double)priv->video_params.h);
|
||||
if (video_par > 1.0) {
|
||||
xscale /= video_par;
|
||||
} else {
|
||||
yscale *= video_par;
|
||||
}
|
||||
}
|
||||
spudec_get_indexed(priv->spudec, &d, xscale, yscale, res);
|
||||
}
|
||||
if (spudec_visible(priv->spudec))
|
||||
spudec_get_indexed(priv->spudec, &d, 1, 1, res);
|
||||
}
|
||||
|
||||
static void reset(struct sd *sd)
|
||||
@@ -105,25 +90,12 @@ static void uninit(struct sd *sd)
|
||||
talloc_free(priv);
|
||||
}
|
||||
|
||||
static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
|
||||
{
|
||||
struct sd_spu_priv *priv = sd->priv;
|
||||
switch (cmd) {
|
||||
case SD_CTRL_SET_VIDEO_PARAMS:
|
||||
priv->video_params = *(struct mp_image_params *)arg;
|
||||
return CONTROL_OK;
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const struct sd_functions sd_spu = {
|
||||
.name = "spu",
|
||||
.supports_format = supports_format,
|
||||
.init = init,
|
||||
.decode = decode,
|
||||
.get_bitmaps = get_bitmaps,
|
||||
.control = control,
|
||||
.reset = reset,
|
||||
.uninit = uninit,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ will be replaced with TA calls, and the talloc wrapper will be removed.
|
||||
|
||||
Documentation for the talloc API is here:
|
||||
|
||||
http://git.samba.org/?p=samba.git;a=blob;f=lib/talloc/talloc.h;hb=HEAD
|
||||
http://talloc.samba.org/talloc/doc/html/modules.html
|
||||
|
||||
There are some minor differences with mpv's talloc bridge. mpv calls abort()
|
||||
on allocation failures, and the talloc_set_destructor() signature is slightly
|
||||
|
||||
@@ -399,10 +399,10 @@ int video_reconfig_filters(struct dec_video *d_video,
|
||||
}
|
||||
|
||||
float force_aspect = opts->movie_aspect;
|
||||
if (force_aspect > -1.0 && d_video->stream_aspect != 0.0)
|
||||
if (force_aspect > 0.0 && d_video->stream_aspect != 0.0)
|
||||
force_aspect = d_video->stream_aspect;
|
||||
|
||||
if (force_aspect > 0)
|
||||
if (force_aspect >= 0.0)
|
||||
vf_set_dar(&p.d_w, &p.d_h, p.w, p.h, force_aspect);
|
||||
|
||||
if (abs(p.d_w - p.w) >= 4 || abs(p.d_h - p.h) >= 4) {
|
||||
|
||||
@@ -91,5 +91,6 @@ void mp_buffer_ref(struct FrameBuffer *buffer);
|
||||
void mp_buffer_unref(struct FrameBuffer *buffer);
|
||||
bool mp_buffer_is_unique(struct FrameBuffer *buffer);
|
||||
void mp_buffer_pool_free(struct FramePool **pool);
|
||||
bool mp_buffer_check(struct FrameBuffer *buffer);
|
||||
|
||||
#endif
|
||||
@@ -204,6 +204,16 @@ void mp_buffer_ref(struct FrameBuffer *buf)
|
||||
pool_unlock();
|
||||
}
|
||||
|
||||
bool mp_buffer_check(struct FrameBuffer *buf)
|
||||
{
|
||||
pool_lock();
|
||||
bool ok = buf->refcount > 0;
|
||||
if (ok)
|
||||
buf->refcount++;
|
||||
pool_unlock();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void mp_buffer_unref(struct FrameBuffer *buf)
|
||||
{
|
||||
FramePool *pool = buf->pool;
|
||||
|
||||
+20
-20
@@ -318,10 +318,7 @@ static void set_from_bih(AVCodecContext *avctx, uint32_t format,
|
||||
if (bih->biSize <= sizeof(*bih))
|
||||
break;
|
||||
av_opt_set_int(avctx, "extern_huff", 1, AV_OPT_SEARCH_CHILDREN);
|
||||
avctx->extradata_size = bih->biSize - sizeof(*bih);
|
||||
avctx->extradata = av_mallocz(avctx->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
memcpy(avctx->extradata, bih + 1, avctx->extradata_size);
|
||||
mp_lavc_set_extradata(avctx, bih + 1, bih->biSize - sizeof(*bih));
|
||||
break;
|
||||
|
||||
case MP_FOURCC('R','V','1','0'):
|
||||
@@ -331,29 +328,21 @@ static void set_from_bih(AVCodecContext *avctx, uint32_t format,
|
||||
case MP_FOURCC('R','V','4','0'):
|
||||
if (bih->biSize < sizeof(*bih) + 8) {
|
||||
// only 1 packet per frame & sub_id from fourcc
|
||||
avctx->extradata_size = 8;
|
||||
avctx->extradata = av_mallocz(avctx->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
((uint32_t *)avctx->extradata)[0] = 0;
|
||||
((uint32_t *)avctx->extradata)[1] =
|
||||
format == MP_FOURCC('R','V','1','3') ?
|
||||
0x10003001 : 0x10000000;
|
||||
uint32_t extradata[2] = {
|
||||
0,
|
||||
format == MP_FOURCC('R','V','1','3') ? 0x10003001 : 0x10000000,
|
||||
};
|
||||
mp_lavc_set_extradata(avctx, &extradata, 8);
|
||||
} else {
|
||||
// has extra slice header (demux_rm or rm->avi streamcopy)
|
||||
avctx->extradata_size = bih->biSize - sizeof(*bih);
|
||||
avctx->extradata = av_mallocz(avctx->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
memcpy(avctx->extradata, bih + 1, avctx->extradata_size);
|
||||
mp_lavc_set_extradata(avctx, bih + 1, bih->biSize - sizeof(*bih));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (bih->biSize <= sizeof(*bih))
|
||||
break;
|
||||
avctx->extradata_size = bih->biSize - sizeof(*bih);
|
||||
avctx->extradata = av_mallocz(avctx->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
memcpy(avctx->extradata, bih + 1, avctx->extradata_size);
|
||||
mp_lavc_set_extradata(avctx, bih + 1, bih->biSize - sizeof(*bih));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -717,7 +706,16 @@ static struct mp_image *image_from_decoder(struct dec_video *vd)
|
||||
mp_image_copy_attributes(mpi, &new);
|
||||
} else if (ctx->do_dr1 && pic->opaque) {
|
||||
struct FrameBuffer *fb = pic->opaque;
|
||||
mp_buffer_ref(fb); // initial reference for mpi
|
||||
// initial reference for mpi
|
||||
if (!new.planes[0] || !mp_buffer_check(fb)) {
|
||||
// Decoder returned an unreferenced buffer! Taking this would just
|
||||
// lead to an eventual double-free. Nothing we can do about this.
|
||||
// So just say "fuck you" in a nice way.
|
||||
MP_FATAL(vd,
|
||||
"Impossible condition detected! This version of Libav/FFmpeg is not\n"
|
||||
"supported anymore. Please update.\n");
|
||||
return NULL;
|
||||
}
|
||||
mpi = mp_image_new_external_ref(&new, fb, fb_ref, fb_unref,
|
||||
fb_is_unique, NULL);
|
||||
} else {
|
||||
@@ -763,6 +761,8 @@ static int decode(struct dec_video *vd, struct demux_packet *packet,
|
||||
|
||||
// Note: potentially resets ctx->pic as it is transferred to mpi
|
||||
struct mp_image *mpi = image_from_decoder(vd);
|
||||
if (!mpi)
|
||||
return 0;
|
||||
assert(mpi->planes[0]);
|
||||
mp_image_set_params(mpi, ¶ms);
|
||||
|
||||
|
||||
@@ -126,7 +126,8 @@ static bool create_vdp_decoder(struct lavc_ctx *ctx)
|
||||
goto fail;
|
||||
}
|
||||
if (p->vid_width > maxw || p->vid_height > maxh) {
|
||||
MP_ERR(p, "Video too large.\n");
|
||||
MP_ERR(p, "Video resolution(%dx%d) is larger than the maximum size(%dx%d) supported.\n",
|
||||
p->vid_width, p->vid_height, maxw, maxh);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
+22
-2
@@ -115,6 +115,8 @@ static const vf_info_t *const filter_list[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void vf_uninit_filter(vf_instance_t *vf);
|
||||
|
||||
static bool get_desc(struct m_obj_desc *dst, int index)
|
||||
{
|
||||
if (index >= MP_ARRAY_SIZE(filter_list) - 1)
|
||||
@@ -270,6 +272,17 @@ static vf_instance_t *vf_open_filter(struct vf_chain *c, const char *name,
|
||||
return vf_open(c, name, args);
|
||||
}
|
||||
|
||||
void vf_remove_filter(struct vf_chain *c, struct vf_instance *vf)
|
||||
{
|
||||
assert(vf != c->first && vf != c->last); // these are sentinels
|
||||
struct vf_instance *prev = c->first;
|
||||
while (prev && prev->next != vf)
|
||||
prev = prev->next;
|
||||
assert(prev); // not inserted
|
||||
prev->next = vf->next;
|
||||
vf_uninit_filter(vf);
|
||||
}
|
||||
|
||||
struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
|
||||
char **args)
|
||||
{
|
||||
@@ -439,6 +452,7 @@ static void update_formats(struct vf_chain *c, struct vf_instance *vf,
|
||||
MP_INFO(c, "Using conversion filter.\n");
|
||||
struct vf_instance *conv = vf_open(c, "scale", NULL);
|
||||
if (conv) {
|
||||
conv->autoinserted = true;
|
||||
conv->next = vf->next;
|
||||
vf->next = conv;
|
||||
update_formats(c, conv, vf->last_outfmts);
|
||||
@@ -492,6 +506,12 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params)
|
||||
{
|
||||
struct mp_image_params cur = *params;
|
||||
int r = 0;
|
||||
for (struct vf_instance *vf = c->first; vf; ) {
|
||||
struct vf_instance *next = vf->next;
|
||||
if (vf->autoinserted)
|
||||
vf_remove_filter(c, vf);
|
||||
vf = next;
|
||||
}
|
||||
c->first->fmt_in = *params;
|
||||
uint8_t unused[IMGFMT_END - IMGFMT_START];
|
||||
update_formats(c, c->first, unused);
|
||||
@@ -599,11 +619,11 @@ void vf_set_dar(int *d_w, int *d_h, int w, int h, double dar)
|
||||
*d_w = w;
|
||||
*d_h = h;
|
||||
if (dar > 0.01) {
|
||||
*d_w = h * dar;
|
||||
*d_w = h * dar + 0.5;
|
||||
// we don't like horizontal downscale
|
||||
if (*d_w < w) {
|
||||
*d_w = w;
|
||||
*d_h = w / dar;
|
||||
*d_h = w / dar + 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ typedef struct vf_instance {
|
||||
void (*uninit)(struct vf_instance *vf);
|
||||
|
||||
char *label;
|
||||
bool autoinserted;
|
||||
|
||||
struct mp_image_params fmt_in, fmt_out;
|
||||
|
||||
@@ -134,6 +135,7 @@ struct mp_image *vf_output_queued_frame(struct vf_chain *c);
|
||||
void vf_seek_reset(struct vf_chain *c);
|
||||
struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
|
||||
char **args);
|
||||
void vf_remove_filter(struct vf_chain *c, struct vf_instance *vf);
|
||||
int vf_append_filter_list(struct vf_chain *c, struct m_obj_settings *list);
|
||||
struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label);
|
||||
void vf_print_filter_chain(struct vf_chain *c, int msglevel);
|
||||
|
||||
@@ -469,7 +469,6 @@ int vf_open(vf_instance_t *vf)
|
||||
vf->filter = filter;
|
||||
vf->uninit = uninit;
|
||||
|
||||
vf->priv = malloc (sizeof (vf_eq2_t));
|
||||
eq2 = vf->priv;
|
||||
eq2->log = vf->log;
|
||||
|
||||
|
||||
@@ -175,6 +175,10 @@ static int mp_chroma_div_up(int size, int shift)
|
||||
// Caller has to make sure this doesn't exceed the allocated plane data/strides.
|
||||
void mp_image_set_size(struct mp_image *mpi, int w, int h)
|
||||
{
|
||||
// av_image_check_size has similar checks and triggers around 16000*16000
|
||||
if (w >= (1 << 14) || h >= (1 << 14) || w < 0 || h < 0)
|
||||
abort();
|
||||
|
||||
mpi->w = mpi->display_w = w;
|
||||
mpi->h = mpi->display_h = h;
|
||||
for (int n = 0; n < mpi->num_planes; n++) {
|
||||
|
||||
@@ -221,8 +221,6 @@ static void vo_cocoa_update_screen_info(struct vo *vo)
|
||||
aspect_save_screenres(vo, r.size.width, r.size.height);
|
||||
opts->screenwidth = r.size.width;
|
||||
opts->screenheight = r.size.height;
|
||||
vo->xinerama_x = r.origin.x;
|
||||
vo->xinerama_y = r.origin.y;
|
||||
}
|
||||
|
||||
static void resize_window(struct vo *vo)
|
||||
@@ -262,7 +260,7 @@ static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height,
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
struct mp_vo_opts *opts = vo->opts;
|
||||
|
||||
const NSRect contentRect = NSMakeRect(0, 0, d_width, d_height);
|
||||
const NSRect contentRect = NSMakeRect(vo->dx, vo->dy, d_width, d_height);
|
||||
|
||||
int window_mask = 0;
|
||||
if (opts->border) {
|
||||
@@ -276,7 +274,8 @@ static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height,
|
||||
[[MpvVideoWindow alloc] initWithContentRect:contentRect
|
||||
styleMask:window_mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
defer:NO
|
||||
screen:s->current_screen];
|
||||
s->view = [[[MpvVideoView alloc] initWithFrame:contentRect] autorelease];
|
||||
|
||||
[s->view setWantsBestResolutionOpenGLSurface:YES];
|
||||
@@ -299,7 +298,6 @@ static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height,
|
||||
[s->window setDelegate:s->window];
|
||||
[s->window makeMainWindow];
|
||||
|
||||
[s->window setFrameOrigin:NSMakePoint(vo->dx, vo->dy)];
|
||||
[s->window makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
|
||||
+9
-3
@@ -157,14 +157,20 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log,
|
||||
if (!profile)
|
||||
goto error_exit;
|
||||
|
||||
cmsCIExyY d65;
|
||||
cmsWhitePointFromTemp(&d65, 6504);
|
||||
cmsCIExyY d65 = {0.3127, 0.3290, 1.0};
|
||||
static const cmsCIExyYTRIPLE bt709prim = {
|
||||
.Red = {0.64, 0.33, 1.0},
|
||||
.Green = {0.30, 0.60, 1.0},
|
||||
.Blue = {0.15, 0.06, 1.0},
|
||||
};
|
||||
cmsToneCurve *tonecurve = cmsBuildGamma(NULL, 1.0/0.45);
|
||||
|
||||
/* Rec BT.709 defines the tone curve as:
|
||||
V = 1.099 * L^0.45 - 0.099 for L >= 0.018
|
||||
V = 4.500 * L for L < 0.018
|
||||
|
||||
The 0.081 parameter comes from inserting 0.018 into the function */
|
||||
cmsToneCurve *tonecurve = cmsBuildParametricToneCurve(NULL, 4,
|
||||
(cmsFloat64Number[5]){1/0.45, 1/1.099, 0.099/1.099, 1/4.5, 0.081});
|
||||
cmsHPROFILE vid_profile = cmsCreateRGBProfile(&d65, &bt709prim,
|
||||
(cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
|
||||
cmsFreeToneCurve(tonecurve);
|
||||
|
||||
@@ -45,8 +45,14 @@
|
||||
#if __VERSION__ >= 130
|
||||
vec3 srgb_compand(vec3 v)
|
||||
{
|
||||
return mix(1.055 * pow(v, vec3(1.0/2.4)) - vec3(0.055), v * 12.92,
|
||||
lessThanEqual(v, vec3(0.0031308)));
|
||||
return mix(v * 12.92, 1.055 * pow(v, vec3(1.0/2.4)) - vec3(0.055),
|
||||
lessThanEqual(vec3(0.0031308), v));
|
||||
}
|
||||
|
||||
vec3 bt709_expand(vec3 v)
|
||||
{
|
||||
return mix(v / 4.5, pow((v + vec3(0.099))/1.099, vec3(1/0.45)),
|
||||
lessThanEqual(vec3(0.0812), v));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -384,6 +390,13 @@ void main() {
|
||||
color = texture3D(lut_3d, color).rgb;
|
||||
#endif
|
||||
#ifdef USE_SRGB
|
||||
// Go from "linear" (0.45) to BT.709 to true linear to sRGB
|
||||
#ifndef USE_3DLUT
|
||||
// Unless we are using a 3DLUT, in which case USE_LINEAR_CONV_INV
|
||||
// already triggered earlier.
|
||||
color = pow(color, vec3(0.45));
|
||||
#endif
|
||||
color = bt709_expand(color.rgb);
|
||||
color.rgb = srgb_compand(color.rgb);
|
||||
#endif
|
||||
#ifdef USE_DITHER
|
||||
|
||||
@@ -188,11 +188,12 @@ static void releaseGlContext_wayland(MPGLContext *ctx)
|
||||
struct vo_wayland_state *wl = ctx->vo->wayland;
|
||||
|
||||
gl->Finish();
|
||||
eglReleaseThread();
|
||||
wl_egl_window_destroy(wl->egl_context.egl_window);
|
||||
eglDestroySurface(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
|
||||
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(wl->egl_context.egl.dpy, wl->egl_context.egl.ctx);
|
||||
eglTerminate(wl->egl_context.egl.dpy);
|
||||
eglReleaseThread();
|
||||
wl_egl_window_destroy(wl->egl_context.egl_window);
|
||||
wl->egl_context.egl.ctx = NULL;
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -44,6 +44,11 @@ static bool create_context_x11_old(struct MPGLContext *ctx)
|
||||
if (glx_ctx->context)
|
||||
return true;
|
||||
|
||||
if (!glx_ctx->vinfo) {
|
||||
MP_FATAL(vo, "Can't create a legacy GLX context without X visual\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLXContext new_context = glXCreateContext(display, glx_ctx->vinfo, NULL,
|
||||
True);
|
||||
if (!new_context) {
|
||||
@@ -219,6 +224,7 @@ static bool config_window_x11(struct MPGLContext *ctx, uint32_t d_width,
|
||||
int glx_attribs[] = {
|
||||
GLX_STEREO, False,
|
||||
GLX_X_RENDERABLE, True,
|
||||
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
||||
GLX_RED_SIZE, 1,
|
||||
GLX_GREEN_SIZE, 1,
|
||||
GLX_BLUE_SIZE, 1,
|
||||
@@ -254,12 +260,13 @@ static bool config_window_x11(struct MPGLContext *ctx, uint32_t d_width,
|
||||
|
||||
glx_ctx->fbc = fbc;
|
||||
glx_ctx->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
|
||||
if (!glx_ctx->vinfo) {
|
||||
MP_ERR(vo, "Selected GLX FB config has no associated X visual\n");
|
||||
return false;
|
||||
if (glx_ctx->vinfo) {
|
||||
MP_VERBOSE(vo, "GLX chose visual with ID 0x%x\n",
|
||||
(int)glx_ctx->vinfo->visualid);
|
||||
} else {
|
||||
MP_WARN(vo, "Selected GLX FB config has no associated X visual\n");
|
||||
}
|
||||
|
||||
MP_VERBOSE(vo, "GLX chose visual with ID 0x%x\n", (int)glx_ctx->vinfo->visualid);
|
||||
|
||||
glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r);
|
||||
glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->depth_g);
|
||||
|
||||
+2
-1
@@ -267,7 +267,8 @@ static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
|
||||
frame->pts * (double) vc->stream->codec->time_base.num /
|
||||
(double) vc->stream->codec->time_base.den, size);
|
||||
|
||||
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
|
||||
if (got_packet)
|
||||
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
+51
-68
@@ -50,6 +50,8 @@
|
||||
#include "osdep/timer.h"
|
||||
#include "bitmap_packer.h"
|
||||
|
||||
// Returns x + a, but wrapped around to the range [0, m)
|
||||
// a must be within [-m, m], x within [0, m)
|
||||
#define WRAP_ADD(x, a, m) ((a) < 0 \
|
||||
? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
|
||||
: ((x)+(a) < (m) ? (x)+(a) : (x)+(a)-(m)))
|
||||
@@ -76,8 +78,6 @@ struct vdpctx {
|
||||
|
||||
VdpPresentationQueueTarget flip_target;
|
||||
VdpPresentationQueue flip_queue;
|
||||
uint64_t last_vdp_time;
|
||||
uint64_t last_sync_update;
|
||||
|
||||
VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
|
||||
VdpOutputSurface screenshot_surface;
|
||||
@@ -160,50 +160,6 @@ struct vdpctx {
|
||||
|
||||
static bool status_ok(struct vo *vo);
|
||||
|
||||
static int change_vdptime_sync(struct vo *vo, int64_t *t)
|
||||
{
|
||||
struct vdpctx *vc = vo->priv;
|
||||
struct vdp_functions *vdp = vc->vdp;
|
||||
VdpStatus vdp_st;
|
||||
VdpTime vdp_time;
|
||||
vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
|
||||
CHECK_VDP_ERROR(vo, "Error when calling vdp_presentation_queue_get_time");
|
||||
uint64_t t1 = *t;
|
||||
uint64_t t2 = mp_time_us();
|
||||
uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL;
|
||||
if (vdp_time > old) {
|
||||
if (vdp_time > old + (t2 - t1) * 1000ULL)
|
||||
vdp_time -= (t2 - t1) * 1000ULL;
|
||||
else
|
||||
vdp_time = old;
|
||||
}
|
||||
MP_DBG(vo, "adjusting VdpTime offset by %f µs\n",
|
||||
(int64_t)(vdp_time - old) / 1000.);
|
||||
vc->last_vdp_time = vdp_time;
|
||||
vc->last_sync_update = t1;
|
||||
*t = t2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t sync_vdptime(struct vo *vo)
|
||||
{
|
||||
struct vdpctx *vc = vo->priv;
|
||||
|
||||
uint64_t t = mp_time_us();
|
||||
if (t - vc->last_sync_update > 5000000)
|
||||
change_vdptime_sync(vo, &t);
|
||||
uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time;
|
||||
// Make sure nanosecond inaccuracies don't make things inconsistent
|
||||
now = FFMAX(now, vc->recent_vsync_time);
|
||||
return now;
|
||||
}
|
||||
|
||||
static uint64_t convert_to_vdptime(struct vo *vo, uint64_t t)
|
||||
{
|
||||
struct vdpctx *vc = vo->priv;
|
||||
return (t - vc->last_sync_update) * 1000LL + vc->last_vdp_time;
|
||||
}
|
||||
|
||||
static int render_video_to_output_surface(struct vo *vo,
|
||||
VdpOutputSurface output_surface,
|
||||
VdpRect *output_rect,
|
||||
@@ -482,12 +438,6 @@ static int win_x11_init_vdpau_flip_queue(struct vo *vo)
|
||||
CHECK_VDP_WARNING(vo, "Error setting colorkey");
|
||||
}
|
||||
|
||||
VdpTime vdp_time;
|
||||
vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
|
||||
CHECK_VDP_ERROR(vo, "Error when calling vdp_presentation_queue_get_time");
|
||||
vc->last_vdp_time = vdp_time;
|
||||
vc->last_sync_update = mp_time_us();
|
||||
|
||||
vc->vsync_interval = 1;
|
||||
if (vc->composite_detect && vo_x11_screen_is_composited(vo)) {
|
||||
MP_INFO(vo, "Compositing window manager detected. Assuming timing info "
|
||||
@@ -1016,6 +966,15 @@ static int update_presentation_queue_status(struct vo *vo)
|
||||
&status, &vtime);
|
||||
CHECK_VDP_WARNING(vo, "Error calling "
|
||||
"presentation_queue_query_surface_status");
|
||||
if (mp_msg_test(vo->log, MSGL_TRACE)) {
|
||||
VdpTime current;
|
||||
vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, ¤t);
|
||||
CHECK_VDP_WARNING(vo, "Error when calling "
|
||||
"vdp_presentation_queue_get_time");
|
||||
MP_TRACE(vo, "Vdpau time: %"PRIu64"\n", (uint64_t)current);
|
||||
MP_TRACE(vo, "Surface %d status: %d time: %"PRIu64"\n",
|
||||
(int)surface, (int)status, (uint64_t)vtime);
|
||||
}
|
||||
if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
|
||||
break;
|
||||
if (vc->vsync_interval > 1) {
|
||||
@@ -1037,12 +996,13 @@ static int update_presentation_queue_status(struct vo *vo)
|
||||
return num_queued;
|
||||
}
|
||||
|
||||
static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
|
||||
// Return the timestamp of the vsync that must have happened before ts.
|
||||
static inline uint64_t prev_vsync(struct vdpctx *vc, uint64_t ts)
|
||||
{
|
||||
uint64_t offset = ts - vc->recent_vsync_time;
|
||||
// Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
|
||||
offset += (uint64_t)vc->vsync_interval << shift;
|
||||
offset %= vc->vsync_interval;
|
||||
int64_t diff = (int64_t)(ts - vc->recent_vsync_time);
|
||||
int64_t offset = diff % vc->vsync_interval;
|
||||
if (offset < 0)
|
||||
offset += vc->vsync_interval;
|
||||
return ts - offset;
|
||||
}
|
||||
|
||||
@@ -1064,14 +1024,36 @@ static void flip_page_timed(struct vo *vo, int64_t pts_us, int duration)
|
||||
if (vc->vsync_interval == 1)
|
||||
duration = -1; // Make sure drop logic is disabled
|
||||
|
||||
uint64_t now = sync_vdptime(vo);
|
||||
uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
|
||||
VdpTime vdp_time = 0;
|
||||
vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
|
||||
CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_get_time");
|
||||
|
||||
int64_t rel_pts_ns = (pts_us - mp_time_us()) * 1000;
|
||||
if (!pts_us || rel_pts_ns < 0)
|
||||
rel_pts_ns = 0;
|
||||
|
||||
uint64_t now = vdp_time;
|
||||
uint64_t pts = now + rel_pts_ns;
|
||||
uint64_t ideal_pts = pts;
|
||||
uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
|
||||
|
||||
#define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
|
||||
// Only gives accurate results for ts >= vc->recent_vsync_time
|
||||
#define PREV_VSYNC(ts) PREV_VS2(ts, 0)
|
||||
/* This should normally never happen.
|
||||
* - The last queued frame can't have a PTS that goes more than 50ms in the
|
||||
* future. This is guaranteed by the playloop, which currently actually
|
||||
* roughly queues 50ms ahead, plus the flip queue offset. Just to be sure
|
||||
* give some additional room by doubling the time.
|
||||
* - The last vsync can never be in the future.
|
||||
*/
|
||||
int64_t max_pts_ahead = (vo->flip_queue_offset + 0.050) * 2 * 1e9;
|
||||
if (vc->last_queue_time > now + max_pts_ahead ||
|
||||
vc->recent_vsync_time > now)
|
||||
{
|
||||
vc->last_queue_time = 0;
|
||||
vc->recent_vsync_time = 0;
|
||||
MP_WARN(vo, "Inconsistent timing detected.\n");
|
||||
}
|
||||
|
||||
#define PREV_VSYNC(ts) prev_vsync(vc, ts)
|
||||
|
||||
/* We hope to be here at least one vsync before the frame should be shown.
|
||||
* If we are running late then don't drop the frame unless there is
|
||||
@@ -1100,7 +1082,7 @@ static void flip_page_timed(struct vo *vo, int64_t pts_us, int duration)
|
||||
*/
|
||||
uint64_t vsync = PREV_VSYNC(pts);
|
||||
if (pts < vsync + vsync_interval / 4
|
||||
&& (vsync - PREV_VS2(vc->last_queue_time, 16)
|
||||
&& (vsync - PREV_VSYNC(vc->last_queue_time)
|
||||
> pts - vc->last_ideal_time + vsync_interval / 2
|
||||
|| vc->dropped_frame && vsync > vc->dropped_time))
|
||||
pts -= vsync_interval / 2;
|
||||
@@ -1115,19 +1097,20 @@ static void flip_page_timed(struct vo *vo, int64_t pts_us, int duration)
|
||||
|
||||
int num_flips = update_presentation_queue_status(vo);
|
||||
vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
|
||||
now = sync_vdptime(vo);
|
||||
pts = FFMAX(pts, now);
|
||||
pts = FFMAX(pts, vsync + (vsync_interval >> 2));
|
||||
vsync = PREV_VSYNC(pts);
|
||||
if (npts < vsync + vsync_interval)
|
||||
return;
|
||||
pts = vsync + (vsync_interval >> 2);
|
||||
vdp_st =
|
||||
vdp->presentation_queue_display(vc->flip_queue,
|
||||
vc->output_surfaces[vc->surface_num],
|
||||
vo->dwidth, vo->dheight, pts);
|
||||
VdpOutputSurface frame = vc->output_surfaces[vc->surface_num];
|
||||
vdp_st = vdp->presentation_queue_display(vc->flip_queue, frame,
|
||||
vo->dwidth, vo->dheight, pts);
|
||||
CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_display");
|
||||
|
||||
MP_TRACE(vo, "Queue new surface %d: Vdpau time: %"PRIu64" "
|
||||
"pts: %"PRIu64"\n", (int)frame, now, pts);
|
||||
|
||||
vc->last_queue_time = pts;
|
||||
vc->queue_time[vc->surface_num] = pts;
|
||||
vc->last_ideal_time = ideal_pts;
|
||||
|
||||
+47
-52
@@ -41,13 +41,6 @@
|
||||
|
||||
#define MAX_BUFFERS 2
|
||||
|
||||
// minium target version for the new formats (1.2.90)
|
||||
#define MINIMUM_VERSION (1 << 16 | 2 << 8 | 90)
|
||||
|
||||
#define CURRENT_VERSION (WAYLAND_VERSION_MAJOR << 16 | \
|
||||
WAYLAND_VERSION_MINOR << 8 | \
|
||||
WAYLAND_VERSION_MICRO)
|
||||
|
||||
static void draw_image(struct vo *vo, mp_image_t *mpi);
|
||||
|
||||
static const struct wl_callback_listener frame_listener;
|
||||
@@ -65,7 +58,6 @@ struct fmtentry {
|
||||
static const struct fmtentry fmttable[] = {
|
||||
{WL_SHM_FORMAT_ARGB8888, IMGFMT_BGRA}, // 8b 8g 8r 8a
|
||||
{WL_SHM_FORMAT_XRGB8888, IMGFMT_BGR0},
|
||||
#if CURRENT_VERSION >= MINIMUM_VERSION
|
||||
{WL_SHM_FORMAT_RGB332, IMGFMT_BGR8}, // 3b 3g 2r
|
||||
{WL_SHM_FORMAT_BGR233, IMGFMT_RGB8}, // 3r 3g 3b,
|
||||
{WL_SHM_FORMAT_XRGB4444, IMGFMT_BGR12_LE}, // 4b 4g 4r 4a
|
||||
@@ -94,7 +86,6 @@ static const struct fmtentry fmttable[] = {
|
||||
{WL_SHM_FORMAT_ABGR8888, IMGFMT_RGBA},
|
||||
{WL_SHM_FORMAT_RGBA8888, IMGFMT_ABGR},
|
||||
{WL_SHM_FORMAT_BGRA8888, IMGFMT_ARGB},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MAX_FORMAT_ENTRIES (sizeof(fmttable) / sizeof(fmttable[0]))
|
||||
@@ -149,7 +140,7 @@ struct priv {
|
||||
|
||||
// options
|
||||
int enable_alpha;
|
||||
int use_default;
|
||||
int use_rgb565;
|
||||
};
|
||||
|
||||
/* copied from weston clients */
|
||||
@@ -232,6 +223,21 @@ static bool is_alpha_format(const struct fmtentry *fmt)
|
||||
return !!(mp_imgfmt_get_desc(fmt->mp_fmt).flags & MP_IMGFLAG_ALPHA);
|
||||
}
|
||||
|
||||
static const struct fmtentry * is_wayland_format_supported(struct priv *p,
|
||||
enum wl_shm_format fmt)
|
||||
{
|
||||
struct supported_format *sf;
|
||||
|
||||
// find the matching format first
|
||||
wl_list_for_each(sf, &p->format_list, link) {
|
||||
if (sf->fmt->wl_fmt == fmt) {
|
||||
return sf->fmt;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void buffer_swap(struct priv *p)
|
||||
{
|
||||
if (!p->back_buffer->is_new)
|
||||
@@ -466,27 +472,7 @@ static void frame_handle_redraw(void *data,
|
||||
struct buffer *buf = buffer_get_front(p);
|
||||
|
||||
if (buf) {
|
||||
if (p->resize_attach) {
|
||||
wl_surface_attach(wl->window.surface, buf->wlbuf, p->x, p->y);
|
||||
wl_surface_damage(wl->window.surface, 0, 0, p->dst_w, p->dst_h);
|
||||
wl_surface_commit(wl->window.surface);
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
p->redraw_callback = NULL;
|
||||
buffer_finalise_front(buf);
|
||||
p->resize_attach = false;
|
||||
|
||||
destroy_shm_buffer(&p->tmp_buffer);
|
||||
|
||||
// I have to destroy the callback and return early to avoid black flickers
|
||||
// I don't exactly know why this, but I guess the back buffer is still
|
||||
// empty. The callback loop will be restored on the next flip_page call
|
||||
return;
|
||||
}
|
||||
|
||||
wl_surface_attach(wl->window.surface, buf->wlbuf, 0, 0);
|
||||
wl_surface_attach(wl->window.surface, buf->wlbuf, p->x, p->y);
|
||||
wl_surface_damage(wl->window.surface, 0, 0, p->dst_w, p->dst_h);
|
||||
|
||||
if (callback)
|
||||
@@ -496,6 +482,14 @@ static void frame_handle_redraw(void *data,
|
||||
wl_callback_add_listener(p->redraw_callback, &frame_listener, p);
|
||||
wl_surface_commit(wl->window.surface);
|
||||
buffer_finalise_front(buf);
|
||||
|
||||
// to avoid multiple resizes of non-shown frames
|
||||
if (p->resize_attach) {
|
||||
destroy_shm_buffer(&p->tmp_buffer);
|
||||
p->resize_attach = false;
|
||||
p->x = 0;
|
||||
p->y = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (callback)
|
||||
@@ -539,7 +533,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
|
||||
struct buffer *buf = buffer_get_back(p);
|
||||
|
||||
if (!buf) {
|
||||
MP_WARN(p->wl, "can't draw, back buffer is busy\n");
|
||||
MP_VERBOSE(p->wl, "can't draw, back buffer is busy\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -570,7 +564,7 @@ static void flip_page(struct vo *vo)
|
||||
buffer_swap(p);
|
||||
|
||||
if (!p->redraw_callback) {
|
||||
MP_INFO(p->wl, "restart frame callback\n");
|
||||
MP_DBG(p->wl, "restart frame callback\n");
|
||||
frame_handle_redraw(p, NULL, 0);
|
||||
}
|
||||
}
|
||||
@@ -595,13 +589,13 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags)
|
||||
struct priv *p = vo->priv;
|
||||
mp_image_unrefp(&p->original_image);
|
||||
|
||||
p->width = vo->dwidth;
|
||||
p->height = vo->dheight;
|
||||
p->width = fmt->w;
|
||||
p->height = fmt->h;
|
||||
p->in_format = *fmt;
|
||||
|
||||
struct supported_format *sf;
|
||||
|
||||
// find the same format first
|
||||
// find the matching format first
|
||||
wl_list_for_each(sf, &p->format_list, link) {
|
||||
if (sf->fmt->mp_fmt == fmt->imgfmt && (p->enable_alpha == sf->is_alpha)) {
|
||||
p->pref_format = sf->fmt;
|
||||
@@ -609,25 +603,22 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
// if the format is not supported choose one of the fancy formats next
|
||||
// the default formats are always last
|
||||
if (!p->pref_format) {
|
||||
wl_list_for_each(sf, &p->format_list, link) {
|
||||
if (p->enable_alpha == sf->is_alpha) {
|
||||
p->pref_format = sf->fmt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if use default is enable overwrite the auto selected one
|
||||
if (p->use_default) {
|
||||
// if use default is enable overwrite the auto selected one
|
||||
if (p->enable_alpha)
|
||||
p->pref_format = &fmttable[DEFAULT_ALPHA_FORMAT_ENTRY];
|
||||
else
|
||||
p->pref_format = &fmttable[DEFAULT_FORMAT_ENTRY];
|
||||
}
|
||||
|
||||
// overides alpha
|
||||
// use rgb565 if performance is your main concern
|
||||
if (p->use_rgb565) {
|
||||
const struct fmtentry *mp_fmt =
|
||||
is_wayland_format_supported(p, WL_SHM_FORMAT_RGB565);
|
||||
if (mp_fmt)
|
||||
p->pref_format = mp_fmt;
|
||||
}
|
||||
|
||||
p->bytes_per_pixel = mp_imgfmt_get_desc(p->pref_format->mp_fmt).bytes[0];
|
||||
MP_VERBOSE(p->wl, "bytes per pixel: %d\n", p->bytes_per_pixel);
|
||||
@@ -639,6 +630,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt, int flags)
|
||||
|
||||
vo_wayland_config(vo, vo->dwidth, vo->dheight, flags);
|
||||
|
||||
if (p->wl->window.events & VO_EVENT_RESIZE)
|
||||
resize(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -648,6 +642,9 @@ static void uninit(struct vo *vo)
|
||||
for (int i = 0; i < MAX_BUFFERS; ++i)
|
||||
destroy_shm_buffer(&p->buffers[i]);
|
||||
|
||||
if (p->redraw_callback)
|
||||
wl_callback_destroy(p->redraw_callback);
|
||||
|
||||
talloc_free(p->original_image);
|
||||
|
||||
vo_wayland_uninit(vo);
|
||||
@@ -657,10 +654,8 @@ static int preinit(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (!vo_wayland_init(vo)) {
|
||||
MP_ERR(p->wl, "could not initalise backend\n");
|
||||
if (!vo_wayland_init(vo))
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->vo = vo;
|
||||
p->wl = vo->wayland;
|
||||
@@ -728,7 +723,7 @@ const struct vo_driver video_out_wayland = {
|
||||
.uninit = uninit,
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLAG("alpha", enable_alpha, 0),
|
||||
OPT_FLAG("default-format", use_default, 0),
|
||||
OPT_FLAG("rgb565", use_rgb565, 0),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -330,9 +330,16 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||
// Window dragging hack
|
||||
ReleaseCapture();
|
||||
SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
||||
mp_input_put_key(vo->input_ctx, MP_MOUSE_BTN0 |
|
||||
MP_KEY_STATE_UP);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouse_button & MP_KEY_STATE_DOWN)
|
||||
SetCapture(w32->window);
|
||||
else
|
||||
ReleaseCapture();
|
||||
}
|
||||
|
||||
return DefWindowProcW(hWnd, message, wParam, lParam);
|
||||
@@ -647,17 +654,14 @@ int vo_w32_init(struct vo *vo)
|
||||
|
||||
HINSTANCE hInstance = GetModuleHandleW(NULL);
|
||||
|
||||
HICON mplayerIcon = LoadIconW(hInstance, L"IDI_ICON1");
|
||||
|
||||
WNDCLASSEXW wcex = {
|
||||
.cbSize = sizeof wcex,
|
||||
.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
|
||||
.lpfnWndProc = WndProc,
|
||||
.hInstance = hInstance,
|
||||
.hIcon = mplayerIcon,
|
||||
.hIcon = LoadIconW(hInstance, L"IDI_ICON1"),
|
||||
.hCursor = LoadCursor(NULL, IDC_ARROW),
|
||||
.lpszClassName = classname,
|
||||
.hIconSm = mplayerIcon,
|
||||
};
|
||||
|
||||
if (!RegisterClassExW(&wcex)) {
|
||||
|
||||
+66
-65
@@ -112,43 +112,6 @@ static const struct mp_keymap keymap[] = {
|
||||
|
||||
/** Wayland listeners **/
|
||||
|
||||
static void display_handle_error(void *data,
|
||||
struct wl_display *display,
|
||||
void *object_id,
|
||||
uint32_t code,
|
||||
const char *message)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
const char * error_type_msg = "";
|
||||
|
||||
switch (code) {
|
||||
case WL_DISPLAY_ERROR_INVALID_OBJECT:
|
||||
error_type_msg = "Invalid object";
|
||||
break;
|
||||
case WL_DISPLAY_ERROR_INVALID_METHOD:
|
||||
error_type_msg = "Invalid method";
|
||||
break;
|
||||
case WL_DISPLAY_ERROR_NO_MEMORY:
|
||||
error_type_msg = "No memory";
|
||||
break;
|
||||
}
|
||||
|
||||
MP_ERR(wl, "%s: %s\n", error_type_msg, message);
|
||||
}
|
||||
|
||||
static void display_handle_delete_id(void *data,
|
||||
struct wl_display *display,
|
||||
uint32_t id)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
MP_DBG(wl, "Object %u deleted\n", id);
|
||||
}
|
||||
|
||||
const struct wl_display_listener display_listener = {
|
||||
display_handle_error,
|
||||
display_handle_delete_id
|
||||
};
|
||||
|
||||
static void ssurface_handle_ping(void *data,
|
||||
struct wl_shell_surface *shell_surface,
|
||||
uint32_t serial)
|
||||
@@ -171,7 +134,7 @@ static void ssurface_handle_popup_done(void *data,
|
||||
{
|
||||
}
|
||||
|
||||
const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
ssurface_handle_ping,
|
||||
ssurface_handle_configure,
|
||||
ssurface_handle_popup_done
|
||||
@@ -220,7 +183,7 @@ static void output_handle_mode(void *data,
|
||||
output->flags = flags;
|
||||
}
|
||||
|
||||
const struct wl_output_listener output_listener = {
|
||||
static const struct wl_output_listener output_listener = {
|
||||
output_handle_geometry,
|
||||
output_handle_mode
|
||||
};
|
||||
@@ -329,7 +292,7 @@ static void keyboard_handle_modifiers(void *data,
|
||||
0, 0, group);
|
||||
}
|
||||
|
||||
const struct wl_keyboard_listener keyboard_listener = {
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
keyboard_handle_keymap,
|
||||
keyboard_handle_enter,
|
||||
keyboard_handle_leave,
|
||||
@@ -443,7 +406,6 @@ static void seat_handle_capabilities(void *data,
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->input.keyboard) {
|
||||
wl->input.keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_set_user_data(wl->input.keyboard, wl);
|
||||
wl_keyboard_add_listener(wl->input.keyboard, &keyboard_listener, wl);
|
||||
}
|
||||
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->input.keyboard) {
|
||||
@@ -452,7 +414,6 @@ static void seat_handle_capabilities(void *data,
|
||||
}
|
||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->input.pointer) {
|
||||
wl->input.pointer = wl_seat_get_pointer(seat);
|
||||
wl_pointer_set_user_data(wl->input.pointer, wl);
|
||||
wl_pointer_add_listener(wl->input.pointer, &pointer_listener, wl);
|
||||
}
|
||||
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->input.pointer) {
|
||||
@@ -466,13 +427,12 @@ static const struct wl_seat_listener seat_listener = {
|
||||
};
|
||||
|
||||
static void registry_handle_global (void *data,
|
||||
struct wl_registry *registry,
|
||||
struct wl_registry *reg,
|
||||
uint32_t id,
|
||||
const char *interface,
|
||||
uint32_t version)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
struct wl_registry *reg = wl->display.registry;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
|
||||
@@ -578,6 +538,8 @@ static void shedule_resize(struct vo_wayland_state *wl,
|
||||
int32_t x, y;
|
||||
float temp_aspect = width / (float) MPMAX(height, 1);
|
||||
|
||||
MP_DBG(wl, "shedule resize: %dx%d\n", width, height);
|
||||
|
||||
if (width < minimum_size)
|
||||
width = minimum_size;
|
||||
|
||||
@@ -632,6 +594,9 @@ static void shedule_resize(struct vo_wayland_state *wl,
|
||||
|
||||
static bool create_display (struct vo_wayland_state *wl)
|
||||
{
|
||||
if (wl->vo->probing && !getenv("XDG_RUNTIME_DIR"))
|
||||
return false;
|
||||
|
||||
wl->display.display = wl_display_connect(NULL);
|
||||
|
||||
if (!wl->display.display) {
|
||||
@@ -642,9 +607,6 @@ static bool create_display (struct vo_wayland_state *wl)
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_display_add_listener(wl->display.display, &display_listener, wl);
|
||||
|
||||
wl_list_init(&wl->display.output_list);
|
||||
wl->display.registry = wl_display_get_registry(wl->display.display);
|
||||
wl_registry_add_listener(wl->display.registry, ®istry_listener, wl);
|
||||
|
||||
@@ -657,15 +619,33 @@ static bool create_display (struct vo_wayland_state *wl)
|
||||
|
||||
static void destroy_display (struct vo_wayland_state *wl)
|
||||
{
|
||||
struct vo_wayland_output *output = NULL;
|
||||
struct vo_wayland_output *tmp = NULL;
|
||||
|
||||
wl_list_for_each_safe(output, tmp, &wl->display.output_list, link) {
|
||||
if (output && output->output) {
|
||||
wl_output_destroy(output->output);
|
||||
output->output = NULL;
|
||||
wl_list_remove(&output->link);
|
||||
}
|
||||
}
|
||||
|
||||
if (wl->display.shm)
|
||||
wl_shm_destroy(wl->display.shm);
|
||||
|
||||
if (wl->display.shell)
|
||||
wl_shell_destroy(wl->display.shell);
|
||||
|
||||
if (wl->display.compositor)
|
||||
wl_compositor_destroy(wl->display.compositor);
|
||||
|
||||
wl_registry_destroy(wl->display.registry);
|
||||
wl_display_flush(wl->display.display);
|
||||
wl_display_disconnect(wl->display.display);
|
||||
if (wl->display.registry)
|
||||
wl_registry_destroy(wl->display.registry);
|
||||
|
||||
if (wl->display.display) {
|
||||
wl_display_flush(wl->display.display);
|
||||
wl_display_disconnect(wl->display.display);
|
||||
}
|
||||
}
|
||||
|
||||
static bool create_window (struct vo_wayland_state *wl)
|
||||
@@ -690,8 +670,11 @@ static bool create_window (struct vo_wayland_state *wl)
|
||||
|
||||
static void destroy_window (struct vo_wayland_state *wl)
|
||||
{
|
||||
wl_shell_surface_destroy(wl->window.shell_surface);
|
||||
wl_surface_destroy(wl->window.surface);
|
||||
if (wl->window.shell_surface)
|
||||
wl_shell_surface_destroy(wl->window.shell_surface);
|
||||
|
||||
if (wl->window.surface)
|
||||
wl_surface_destroy(wl->window.surface);
|
||||
}
|
||||
|
||||
static bool create_cursor (struct vo_wayland_state *wl)
|
||||
@@ -740,9 +723,11 @@ static void destroy_input (struct vo_wayland_state *wl)
|
||||
wl_keyboard_destroy(wl->input.keyboard);
|
||||
xkb_map_unref(wl->input.xkb.keymap);
|
||||
xkb_state_unref(wl->input.xkb.state);
|
||||
xkb_context_unref(wl->input.xkb.context);
|
||||
}
|
||||
|
||||
if (wl->input.xkb.context)
|
||||
xkb_context_unref(wl->input.xkb.context);
|
||||
|
||||
if (wl->input.pointer)
|
||||
wl_pointer_destroy(wl->input.pointer);
|
||||
|
||||
@@ -760,11 +745,14 @@ int vo_wayland_init (struct vo *vo)
|
||||
wl->vo = vo;
|
||||
wl->log = mp_log_new(wl, vo->log, "wayland");
|
||||
|
||||
wl_list_init(&wl->display.output_list);
|
||||
|
||||
if (!create_input(wl)
|
||||
|| !create_display(wl)
|
||||
|| !create_window(wl)
|
||||
|| !create_cursor(wl))
|
||||
{
|
||||
vo_wayland_uninit(vo);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -786,13 +774,11 @@ void vo_wayland_uninit (struct vo *vo)
|
||||
|
||||
static void vo_wayland_ontop (struct vo *vo)
|
||||
{
|
||||
MP_DBG(vo->wayland, "going ontop\n");
|
||||
vo->opts->ontop = 0;
|
||||
vo->opts->fullscreen = 1;
|
||||
|
||||
/* use the already existing code to leave fullscreen mode and go into
|
||||
* toplevel mode */
|
||||
vo_wayland_fullscreen(vo);
|
||||
struct vo_wayland_state *wl = vo->wayland;
|
||||
MP_DBG(wl, "going ontop\n");
|
||||
vo->opts->ontop = 1;
|
||||
wl_shell_surface_set_toplevel(wl->window.shell_surface);
|
||||
shedule_resize(wl, 0, wl->window.width, wl->window.height);
|
||||
}
|
||||
|
||||
static void vo_wayland_border (struct vo *vo)
|
||||
@@ -918,6 +904,9 @@ static void vo_wayland_update_screeninfo (struct vo *vo)
|
||||
}
|
||||
}
|
||||
|
||||
wl->window.fs_width = opts->screenwidth;
|
||||
wl->window.fs_height = opts->screenheight;
|
||||
|
||||
aspect_save_screenres(vo, opts->screenwidth, opts->screenheight);
|
||||
}
|
||||
|
||||
@@ -932,7 +921,6 @@ int vo_wayland_control (struct vo *vo, int *events, int request, void *arg)
|
||||
return VO_TRUE;
|
||||
case VOCTRL_FULLSCREEN:
|
||||
vo_wayland_fullscreen(vo);
|
||||
*events |= VO_EVENT_RESIZE;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_ONTOP:
|
||||
vo_wayland_ontop(vo);
|
||||
@@ -979,13 +967,26 @@ bool vo_wayland_config (struct vo *vo, uint32_t d_width,
|
||||
{
|
||||
struct vo_wayland_state *wl = vo->wayland;
|
||||
|
||||
wl->window.width = d_width;
|
||||
wl->window.height = d_height;
|
||||
wl->window.p_width = d_width;
|
||||
wl->window.p_height = d_height;
|
||||
wl->window.aspect = wl->window.width / (float) MPMAX(wl->window.height, 1);
|
||||
wl->window.aspect = d_width / (float) MPMAX(d_height, 1);
|
||||
|
||||
vo_wayland_fullscreen(vo);
|
||||
if (!(flags & VOFLAG_HIDDEN)) {
|
||||
if (!wl->window.is_init) {
|
||||
wl->window.width = d_width;
|
||||
wl->window.height = d_height;
|
||||
}
|
||||
|
||||
if (vo->opts->fullscreen) {
|
||||
if (wl->window.is_fullscreen)
|
||||
shedule_resize(wl, 0, wl->window.fs_width, wl->window.fs_height);
|
||||
else
|
||||
vo_wayland_fullscreen(vo);
|
||||
}
|
||||
else
|
||||
vo_wayland_ontop(vo);
|
||||
wl->window.is_init = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ struct vo_wayland_state {
|
||||
} display;
|
||||
|
||||
struct {
|
||||
int32_t width;
|
||||
int32_t width; // current size of the window
|
||||
int32_t height;
|
||||
int32_t p_width; // previous sizes for leaving fullscreen
|
||||
int32_t p_height;
|
||||
@@ -90,7 +90,10 @@ struct vo_wayland_state {
|
||||
int32_t sh_y;
|
||||
float aspect;
|
||||
|
||||
bool is_init; // true if the window has a valid size
|
||||
bool is_fullscreen; // don't keep aspect ratio in fullscreen mode
|
||||
int32_t fs_width; // fullscreen sizes
|
||||
int32_t fs_height;
|
||||
|
||||
struct wl_surface *surface;
|
||||
int32_t mouse_x; // mouse position inside the surface
|
||||
|
||||
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user