Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6236e22db |
No files matched your search
+8
-17
@@ -2,7 +2,6 @@
|
||||
*.a
|
||||
*.d
|
||||
*.exe
|
||||
*.pyc
|
||||
.depend
|
||||
|
||||
/config.h
|
||||
@@ -11,7 +10,7 @@
|
||||
/mpv
|
||||
/mpv.app
|
||||
/version.h
|
||||
/input/input_conf.h
|
||||
/mpvcore/input/input_conf.h
|
||||
/tags
|
||||
/TAGS
|
||||
/video/out/gl_video_shaders.h
|
||||
@@ -19,18 +18,10 @@
|
||||
/demux/ebml_defs.c
|
||||
/demux/ebml_types.h
|
||||
/sub/osd_font.h
|
||||
/player/lua/*.inc
|
||||
/DOCS/man/mpv.1
|
||||
/DOCS/man/mpv.aux
|
||||
/DOCS/man/mpv.log
|
||||
/DOCS/man/mpv.out
|
||||
/DOCS/man/mpv.pdf
|
||||
/DOCS/man/mpv.toc
|
||||
|
||||
/waf
|
||||
/build
|
||||
/.waf*
|
||||
/.lock-waf_*
|
||||
|
||||
/old_build
|
||||
/Makefile
|
||||
/mpvcore/lua/*.inc
|
||||
/DOCS/man/*/mpv.1
|
||||
/DOCS/man/*/mpv.aux
|
||||
/DOCS/man/*/mpv.log
|
||||
/DOCS/man/*/mpv.out
|
||||
/DOCS/man/*/mpv.pdf
|
||||
/DOCS/man/*/mpv.toc
|
||||
+14
-46
@@ -1,62 +1,30 @@
|
||||
language: c
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
env:
|
||||
matrix:
|
||||
- LIBAV=libav-stable
|
||||
- LIBAV=libav-git
|
||||
- LIBAV=ffmpeg-stable
|
||||
- LIBAV=ffmpeg-git
|
||||
global:
|
||||
# Coverity token
|
||||
- secure: "H21mSRlMhk4BKS0xHZvCFGJxteCP0hRVUxTuNfM2Z9HBsyutuLEYMtViLO86VtM+Tqla3xXPzUdS4ozLwI72Ax/5ZUDXACROj73yW6QhFB5D6rLut12+FjqC7M33Qv2hl0xwgNBmR5dsm1ToP37+Wn+ecJQNvN8fkTXF+HVzOEw="
|
||||
- LIBAV=libav-stable
|
||||
- LIBAV=libav-git
|
||||
- LIBAV=ffmpeg-stable
|
||||
- LIBAV=ffmpeg-git
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- ci
|
||||
- coverity_scan
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: LIBAV=libav-stable
|
||||
- os: osx
|
||||
env: LIBAV=libav-git
|
||||
- os: osx
|
||||
env: LIBAV=ffmpeg-git
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
env: LIBAV=ffmpeg-stable
|
||||
- os: linux
|
||||
compiler: clang
|
||||
|
||||
before_install: ./travis-deps libass-stable $LIBAV
|
||||
script:
|
||||
- ./bootstrap.py
|
||||
- ./waf configure
|
||||
- ./waf build
|
||||
script: ./configure && make
|
||||
|
||||
notifications-policy: ¬ifications-policy
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
email:
|
||||
recipients:
|
||||
- mpv-team@googlegroups.com
|
||||
<<: *notifications-policy
|
||||
irc:
|
||||
channels:
|
||||
- "irc.freenode.org#mpv-player-dev"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "mpv-player/mpv"
|
||||
description: "Build submitted via Travis CI"
|
||||
notification_email: mpv-team@googlegroups.com
|
||||
build_command_prepend: "./bootstrap.py && ./waf configure"
|
||||
build_command: "./waf build"
|
||||
branch_pattern: coverity_scan
|
||||
<<: *notifications-policy
|
||||
@@ -0,0 +1,160 @@
|
||||
1. Input layer, supported devices, methods:
|
||||
- plain file, with seeking
|
||||
- STDIN, without seeking backward
|
||||
- network streaming (currently plain wget-like HTTP and MMS (.asx))
|
||||
- VCD (Video CD) track, by direct CDROM device access (not requires mounting disc)
|
||||
- DVD titles using .IFO structure, by direct DVD device access (not requires mounting disc)
|
||||
- DVD titles using menu navigation (experimental/alpha, not yet finished!!!)
|
||||
- CDDA - raw audio from audio CD-ROM discs (using cdparanoia libs)
|
||||
- RTP streaming (mpeg-ps over multicast only)
|
||||
- LIVE555 streaming - support SDP/RTSP (using the LIVE555 libs)
|
||||
- SMB - file access over samba (experimental)
|
||||
|
||||
2. Demuxer/parser layer, supported file/media formats:
|
||||
|
||||
- MPEG streams (ES,PES,PS. no TS support yet)
|
||||
note: mpeg demuxer silently ignore non-mpeg content, and find mpeg packets
|
||||
in arbitrary streams. it means you can play directly VCD images (for example
|
||||
CDRwin's .BIN files) without extracting mpeg files first (with tools like vcdgear)
|
||||
It accepts all PES variants, including files created by VDR.
|
||||
Note: VOB (video object) is simple mpeg stream, but it usually has 01BD
|
||||
packets which may contain subtitles and non-mpeg audio. Usually found on DVD discs.
|
||||
|
||||
Headers: mpeg streams has no global header. each frame sequence (also called GOP,
|
||||
group of pictures) contains an sequence header, it describes that block.
|
||||
In normal mpeg 1/2 content there are groups of 12-15 frames (24/30 fps).
|
||||
It means you can freely seek in mpeg streams, and even can cut it to
|
||||
small parts with standard file tools (dd, cut) without destroying it.
|
||||
|
||||
Codecs: video is always mpeg video (mpeg1, mpeg2 or mpeg4).
|
||||
audio is usually mpeg audio (any layer allowed, but it's layer 2 in most files)
|
||||
but 01BD packets may contain AC3, DTS or LPCM too.
|
||||
|
||||
FPS: mpeg2 content allows variable framerate, in form of delayed frames.
|
||||
It's mostly used for playback 24fps content at 29.97/30 fps (NTSC) rate.
|
||||
(so called Telecine or 3:2 pulldown effect)
|
||||
It means you see 30 frames per second, but there are only 24 different
|
||||
pictures and some of them are shown longer to fill 30 frame time.
|
||||
If you encode such files with mencoder, using -ofps 24 or -ofps 23.976
|
||||
is recommended.
|
||||
|
||||
- AVI streams.
|
||||
Two kind of RIFF AVI files exists:
|
||||
1. interleaved: audio and video content is interleaved. it's faster and
|
||||
requires only 1 reading thread, so it's recommended (and mostly used).
|
||||
2. non-interleaved: audio and video aren't interleaved, i mean first come
|
||||
whole video followed by whole audio. it requires 2 reading process or
|
||||
1 reading with lots of seeking. very bad for network or cdrom.
|
||||
3. badly interleaved streams: mplayer detects interleaving at startup and
|
||||
enables -ni option if it finds non-interleaved content. but sometimes
|
||||
the stream seems to be interleaved, but with bad sync so it should be
|
||||
played as non-interleaved otherwise you get a-v desync or buffer overflow.
|
||||
|
||||
MPlayer supports 2 kind of timing for AVI files:
|
||||
- bps-based: it is based on bitrate/samplerate of video/audio stream.
|
||||
this method is used by most players, including avifile and wmp.
|
||||
files with broken headers, and files created with VBR audio but not
|
||||
vbr-compliant encoder will result a-v desync with this method.
|
||||
(mostly at seeking).
|
||||
- interleaving-based: note: it can't be used togethwer with -ni
|
||||
it doesn't use bitrate stuff of header, it uses the relative position
|
||||
of interleaved audio and video chunks. makes some badly encoded file
|
||||
with vbr audio playable.
|
||||
|
||||
Headers: AVI files has a mandatory header at the begin of the file,
|
||||
describing video parameters (resolution, fps) and codecs. Optionally
|
||||
they have an INDEX block at the end of the file. It's optional, but
|
||||
most files has such block, because it's REQUIRED for seeking.
|
||||
Btw usually it can be rebuilt from file content, mplayer does it with
|
||||
the -idx switch. MPlayer can recreate broken index blocks using -forceidx.
|
||||
As AVI files needs index for random access, broken files with no index
|
||||
are usually unplayable.
|
||||
Of course, cutting/joining AVI files needs special programs.
|
||||
|
||||
Codecs: any audio and video codecs allowed, but I note that VBR audio is
|
||||
not well supported by most players. The file format makes it possible to
|
||||
use VBR audio, but most players expect CBR audio and fails with VBR,
|
||||
as VBR is unusual, and Microsoft's AVI specs only describe CBR audio.
|
||||
I also note, that most AVI encoders/multiplexers create bad files if
|
||||
using VBR audio. only 2 exception (known by me): NaNDub and MEncoder.
|
||||
|
||||
FPS: only constant framerate allowed, but it's possible to skip frames.
|
||||
|
||||
- ASF streams:
|
||||
ASF (active streaming format) comes from Microsoft. they developed two
|
||||
variant of ASF, v1.0 and v2.0. v1.0 is used by their media tools (wmp and
|
||||
wme) and v2.0 is published and patented :). of course, they differ,
|
||||
no compatibility at all. (it's just a legality game)
|
||||
MPlayer supports only v1.0, as nobody ever seen v2.0 files :)
|
||||
Note, that .ASF files are nowdays come with extension .WMA or .WMV.
|
||||
UPDATE: MS recently released the ASF v1.0 specs too, but it has some
|
||||
restrictions making it illegal to read by us :)
|
||||
|
||||
Headers: Stream headers (codecs parameters) can be everywhere (in theory),
|
||||
but all files i've seen had it at the beginning of the file.
|
||||
Asf uses fixed packet size, so it is seekable without any INDEX block,
|
||||
and broken files are playable well.
|
||||
|
||||
Codecs: video is mostly microsoft's mpeg4 variants: MP42, MP43 (aka DivX),
|
||||
WMV1 and WMV2. but any codecs allowed.
|
||||
audio is usually wma or voxware, sometimes mp3, but any codecs allowed.
|
||||
|
||||
FPS: no fixed fps, every video frame has an exact timestamp instead.
|
||||
I've got stream with up to 3 sec frame display times.
|
||||
|
||||
- QuickTime / MOV files:
|
||||
They come from Mac users, usually with .mov or .qt extension, but as
|
||||
MPEG Group chose quicktime as recommended file format for MPEG4,
|
||||
sometimes you meet quicktime files with .mpg or .mp4 extension.
|
||||
|
||||
At first look, it's a mixture of ASF and AVI.
|
||||
It requires INDEX block for random access and seeking, and even for
|
||||
playback, like AVI, but uses timestamps instead of constant framerate
|
||||
and has more flexible stream options (including network stuff) like ASF.
|
||||
|
||||
Headers: header can be placed at the beginning or at the end of file.
|
||||
About half of my files have it at the beginning, others have it at the end.
|
||||
Broken files are only playable if they have header at the beginning!
|
||||
|
||||
Codecs: any codecs allowed, both CBR and VBR.
|
||||
Note: most new mov files use Sorenson video and QDesign Music audio,
|
||||
they are patented, closed, secret, (TM)-ed etc formats, only Apple's
|
||||
quicktime player is able to playback these files (on win/mac only).
|
||||
|
||||
- VIVO files:
|
||||
They are funny streams. They have a human-readable ascii header at
|
||||
the beginning, followed by interleaved audio and video chunks.
|
||||
It has no index block, has no fixed packetsize or sync bytes, and most
|
||||
files even has no keyframes, so forget seeking!
|
||||
Video is standard h.263 (in vivo/2.0 files it's modified, non-standard
|
||||
h.263), audio is either standard g.723 or Vivo Siren codec.
|
||||
|
||||
Note, that microsoft licensed vivo stuff, and included in their netshow
|
||||
v2.0 program, so there are VfW/ACM codecs for vivo video and audio.
|
||||
|
||||
- RealMedia files:
|
||||
A mixture of AVI and ASF features. It has mandatory headers at the
|
||||
beginning and an optional INDEX (missing in most files).
|
||||
The file is constructed of variable size chunks, with small header
|
||||
telling the stream ID, timestamp, flags (keyframe...) and size.
|
||||
But it has some features found in ASF files:
|
||||
The video is actually double-muxed, the video chunks are really
|
||||
appended fragments of the video frame. RV30+ supports B frames, so
|
||||
you have to parse some bits of the first fragment to get the real PTS.
|
||||
The audio frames are fixed size (CBR) but using the same scrambling
|
||||
(out-of-order interleaving) as in the ASF files.
|
||||
|
||||
Codecs: Audio is either COOK(er), SIPR(o), ATRAC3 or DNET.
|
||||
The DNET is actually a byte-swapped low-bitrate Dolby AC3 variant :)
|
||||
Video is RV10 (h263 variant), RV20 (rp G2), RV30 (rp v8) or RV40 (rp v9).
|
||||
|
||||
FPS: variable, just like in ASF.
|
||||
|
||||
Note, that similarity of real and asf has some background - they worked
|
||||
together on the (never finished/used) ASF v2 spec for some time.
|
||||
|
||||
- GIF files:
|
||||
The GIF format is a common format for web graphics that supports
|
||||
animation. These are read through libungif or compatible library.
|
||||
Variable frame delays are supported, but seeking is not supported.
|
||||
Seeking will be supported once an index of gif frames can be built.
|
||||
@@ -0,0 +1,228 @@
|
||||
So, I'll describe how this stuff works.
|
||||
|
||||
The main modules:
|
||||
|
||||
1. stream.c: this is the input layer, this reads the input media (file, stdin,
|
||||
vcd, dvd, network etc). what it has to know: appropriate buffering by
|
||||
sector, seek, skip functions, reading by bytes, or blocks with any size.
|
||||
The stream_t (stream.h) structure describes the input stream, file/device.
|
||||
|
||||
There is a stream cache layer (cache2.c), it's a wrapper for the stream
|
||||
API. It does fork(), then emulates stream driver in the parent process,
|
||||
and stream user in the child process, while proxying between them using
|
||||
preallocated big memory chunk for FIFO buffer.
|
||||
|
||||
2. demuxer.c: this does the demultiplexing (separating) of the input to
|
||||
audio, video or dvdsub channels, and their reading by buffered packages.
|
||||
The demuxer.c is basically a framework, which is the same for all the
|
||||
input formats, and there are parsers for each of them (mpeg-es,
|
||||
mpeg-ps, avi, avi-ni, asf), these are in the demux_*.c files.
|
||||
The structure is the demuxer_t. There is only one demuxer.
|
||||
|
||||
2.a. demux_packet_t, that is DP.
|
||||
Contains one chunk (avi) or packet (asf,mpg). They are stored in memory as
|
||||
in linked list, cause of their different size.
|
||||
|
||||
2.b. demuxer stream, that is DS.
|
||||
Struct: demux_stream_t
|
||||
Every channel (a/v/s) has one. This contains the packets for the stream
|
||||
(see 2.a). For now, there can be 3 for each demuxer :
|
||||
- audio (d_audio)
|
||||
- video (d_video)
|
||||
- DVD subtitle (d_dvdsub)
|
||||
|
||||
2.c. stream header. There are 2 types (for now): sh_audio_t and sh_video_t
|
||||
This contains every parameter essential for decoding, such as input/output
|
||||
buffers, chosen codec, fps, etc. There are each for every stream in
|
||||
the file. At least one for video, if sound is present then another,
|
||||
but if there are more, then there'll be one structure for each.
|
||||
These are filled according to the header (avi/asf), or demux_mpg.c
|
||||
does it (mpg) if it founds a new stream. If a new stream is found,
|
||||
the ====> Found audio/video stream: <id> messages is displayed.
|
||||
|
||||
The chosen stream header and its demuxer are connected together
|
||||
(ds->sh and sh->ds) to simplify the usage. So it's enough to pass the
|
||||
ds or the sh, depending on the function.
|
||||
|
||||
For example: we have an asf file, 6 streams inside it, 1 audio, 5
|
||||
video. During the reading of the header, 6 sh structs are created, 1
|
||||
audio and 5 video. When it starts reading the packet, it chooses the
|
||||
stream for the first found audio & video packet, and sets the sh
|
||||
pointers of d_audio and d_video according to them. So later it reads
|
||||
only these streams. Of course the user can force choosing a specific
|
||||
stream with
|
||||
-vid and -aid switches.
|
||||
A good example for this is the DVD, where the english stream is not
|
||||
always the first, so every VOB has different language :)
|
||||
That's when we have to use for example the -aid 128 switch.
|
||||
|
||||
Now, how this reading works?
|
||||
- demuxer.c/demux_read_data() is called, it gets how many bytes,
|
||||
and where (memory address), would we like to read, and from which
|
||||
DS. The codecs call this.
|
||||
- this checks if the given DS's buffer contains something, if so, it
|
||||
reads from there as much as needed. If there isn't enough, it calls
|
||||
ds_fill_buffer(), which:
|
||||
- checks if the given DS has buffered packages (DP's), if so, it moves
|
||||
the oldest to the buffer, and reads on. If the list is empty, it
|
||||
calls demux_fill_buffer() :
|
||||
- this calls the parser for the input format, which reads the file
|
||||
onward, and moves the found packages to their buffers.
|
||||
Well it we'd like an audio package, but only a bunch of video
|
||||
packages are available, then sooner or later the:
|
||||
DEMUXER: Too many (%d in %d bytes) audio packets in the buffer
|
||||
error shows up.
|
||||
|
||||
2.d. video.c: this file/function handle the reading and assembling of the
|
||||
video frames. each call to video_read_frame() should read and return a
|
||||
single video frame, and it's duration in seconds (float).
|
||||
The implementation is splitted to 2 big parts - reading from mpeg-like
|
||||
streams and reading from one-frame-per-chunk files (avi, asf, mov).
|
||||
Then it calculates duration, either from fixed FPS value, or from the
|
||||
PTS difference between and after reading the frame.
|
||||
|
||||
2.e. other utility functions: there are some useful code there, like
|
||||
AVI muxer, or mp3 header parser, but leave them for now.
|
||||
|
||||
So everything is ok 'till now. It can be found in libmpdemux/ library.
|
||||
It should compile outside of mplayer tree, you just have to implement few
|
||||
simple functions, like mp_msg() to print messages, etc.
|
||||
See libmpdemux/test.c for example.
|
||||
|
||||
See also formats.txt, for description of common media file formats and their
|
||||
implementation details in libmpdemux.
|
||||
|
||||
Now, go on:
|
||||
|
||||
3. mplayer.c - ooh, he's the boss :)
|
||||
Its main purpose is connecting the other modules, and maintaining A/V
|
||||
sync.
|
||||
|
||||
The given stream's actual position is in the 'timer' field of the
|
||||
corresponding stream header (sh_audio / sh_video).
|
||||
|
||||
The structure of the playing loop :
|
||||
while(not EOF) {
|
||||
fill audio buffer (read & decode audio) + increase a_frame
|
||||
read & decode a single video frame + increase v_frame
|
||||
sleep (wait until a_frame>=v_frame)
|
||||
display the frame
|
||||
apply A-V PTS correction to a_frame
|
||||
handle events (keys,lirc etc) -> pause,seek,...
|
||||
}
|
||||
|
||||
When playing (a/v), it increases the variables by the duration of the
|
||||
played a/v.
|
||||
- with audio this is played bytes / sh_audio->o_bps
|
||||
Note: i_bps = number of compressed bytes for one second of audio
|
||||
o_bps = number of uncompressed bytes for one second of audio
|
||||
(this is = bps*samplerate*channels)
|
||||
- with video this is usually == 1.0/fps, but I have to note that
|
||||
fps doesn't really matters at video, for example asf doesn't have that,
|
||||
instead there is "duration" and it can change per frame.
|
||||
MPEG2 has "repeat_count" which delays the frame by 1-2.5 ...
|
||||
Maybe only AVI and MPEG1 has fixed fps.
|
||||
|
||||
So everything works right until the audio and video are in perfect
|
||||
synchronity, since the audio goes, it gives the timing, and if the
|
||||
time of a frame passed, the next frame is displayed.
|
||||
But what if these two aren't synchronized in the input file?
|
||||
PTS correction kicks in. The input demuxers read the PTS (presentation
|
||||
timestamp) of the packages, and with it we can see if the streams
|
||||
are synchronized. Then MPlayer can correct the a_frame, within
|
||||
a given maximal bounder (see -mc option). The summary of the
|
||||
corrections can be found in c_total .
|
||||
|
||||
Of course this is not everything, several things suck.
|
||||
For example the soundcards delay, which has to be corrected by
|
||||
MPlayer! The audio delay is the sum of all these:
|
||||
- bytes read since the last timestamp:
|
||||
t1 = d_audio->pts_bytes/sh_audio->i_bps
|
||||
- if Win32/ACM then the bytes stored in audio input buffer
|
||||
t2 = a_in_buffer_len/sh_audio->i_bps
|
||||
- uncompressed bytes in audio out buffer
|
||||
t3 = a_buffer_len/sh_audio->o_bps
|
||||
- not yet played bytes stored in the soundcard's (or DMA's) buffer
|
||||
t4 = get_audio_delay()/sh_audio->o_bps
|
||||
|
||||
From this we can calculate what PTS we need for the just played
|
||||
audio, then after we compare this with the video's PTS, we have
|
||||
the difference!
|
||||
|
||||
Life didn't get simpler with AVI. There's the "official" timing
|
||||
method, the BPS-based, so the header contains how many compressed
|
||||
audio bytes or chunks belong to one second of frames.
|
||||
In the AVI stream header there are 2 important fields, the
|
||||
dwSampleSize, and dwRate/dwScale pairs:
|
||||
- If the dwSampleSize is 0, then it's VBR stream, so its bitrate
|
||||
isn't constant. It means that 1 chunk stores 1 sample, and
|
||||
dwRate/dwScale gives the chunks/sec value.
|
||||
- If the dwSampleSize is >0, then it's constant bitrate, and the
|
||||
time can be measured this way: time = (bytepos/dwSampleSize) /
|
||||
(dwRate/dwScale) (so the sample's number is divided with the
|
||||
samplerate). Now the audio can be handled as a stream, which can
|
||||
be cut to chunks, but can be one chunk also.
|
||||
|
||||
The other method can be used only for interleaved files: from
|
||||
the order of the chunks, a timestamp (PTS) value can be calculated.
|
||||
The PTS of the video chunks are simple: chunk number * fps
|
||||
The audio is the same as the previous video chunk was.
|
||||
We have to pay attention to the so called "audio preload", that is,
|
||||
there is a delay between the audio and video streams. This is
|
||||
usually 0.5-1.0 sec, but can be totally different.
|
||||
The exact value was measured until now, but now the demux_avi.c
|
||||
handles it: at the audio chunk after the first video, it calculates
|
||||
the A/V difference, and take this as a measure for audio preload.
|
||||
|
||||
3.a. audio playback:
|
||||
Some words on audio playback:
|
||||
Not the playing is hard, but:
|
||||
1. knowing when to write into the buffer, without blocking
|
||||
2. knowing how much was played of what we wrote into
|
||||
The first is needed for audio decoding, and to keep the buffer
|
||||
full (so the audio will never skip). And the second is needed for
|
||||
correct timing, because some soundcards delay even 3-7 seconds,
|
||||
which can't be forgotten about.
|
||||
To solve this, the OSS gives several possibilities:
|
||||
- ioctl(SNDCTL_DSP_GETODELAY): tells how many unplayed bytes are in
|
||||
the soundcard's buffer -> perfect for timing, but not all drivers
|
||||
support it :(
|
||||
- ioctl(SNDCTL_DSP_GETOSPACE): tells how much can we write into the
|
||||
soundcard's buffer, without blocking. If the driver doesn't
|
||||
support GETODELAY, we can use this to know how much the delay is.
|
||||
- select(): should tell if we can write into the buffer without
|
||||
blocking. Unfortunately it doesn't say how much we could :((
|
||||
Also, doesn't/badly works with some drivers.
|
||||
Only used if none of the above works.
|
||||
|
||||
4. Codecs. Consists of libmpcodecs/* and separate files or libs,
|
||||
for example libmpeg2, loader, mp3lib.
|
||||
|
||||
mplayer.c doesn't call them directly, but through the dec_audio.c and
|
||||
dec_video.c files, so the mplayer.c doesn't have to know anything about
|
||||
the codecs.
|
||||
|
||||
libmpcodecs contains wrapper for every codecs, some of them include the
|
||||
codec function implementation, some calls functions from other files
|
||||
included with mplayer, some calls optional external libraries.
|
||||
file naming convention in libmpcodecs:
|
||||
ad_*.c - audio decoder (called through dec_audio.c)
|
||||
vd_*.c - video decoder (called through dec_video.c)
|
||||
ve_*.c - video encoder (used by mencoder)
|
||||
vf_*.c - video filter (see option -vf)
|
||||
|
||||
On this topic, see also:
|
||||
libmpcodecs.txt - The structure of the codec-filter path, with explanation
|
||||
dr-methods.txt - Direct rendering, MPI buffer management for video codecs
|
||||
codecs.conf.txt - How to write/edit codec configuration file (codecs.conf)
|
||||
codec-devel.txt - Mike's hints about codec development - a bit OUTDATED
|
||||
hwac3.txt - about SP/DIF audio passthrough
|
||||
|
||||
5. libvo: this displays the frame.
|
||||
|
||||
for details on this, read libvo.txt
|
||||
|
||||
6. libao2: this control audio playing
|
||||
6.a audio plugins
|
||||
|
||||
for details on this, read libao2.txt
|
||||
@@ -0,0 +1,145 @@
|
||||
mails by A'rpi and Marcus Blomenkamp <Marcus.Blomenkamp@epost.de>
|
||||
describing how this ac3-passtrough hack work under linux and mplayer...
|
||||
-----------------------------------------------------------------------
|
||||
Hi,
|
||||
|
||||
> I received the following patch from Steven Brookes <stevenjb@mda.co.uk>.
|
||||
> He is working on fixing the digital audio output of the dxr3 driver and
|
||||
> told me he fixed some bugs in mplayer along the way. I don't know shit
|
||||
> about hwac3 output so all I did was to make sure the patch applied
|
||||
> against latest cvs.
|
||||
> This is from his e-mail to me:
|
||||
>
|
||||
> "Secondly there is a patch to dec_audio.c and
|
||||
> ac3-iec958 to fix the -ac hwac3 codec stuff and to use liba52 to sync it.
|
||||
|
||||
> Seems to work for everything I've thrown at and maintains sync for all audio
|
||||
> types through the DXR3."
|
||||
|
||||
patch applied (with some comments added and an unwanted change (in software
|
||||
a52 decoder) removed)
|
||||
|
||||
now i understand how this whole hwac3 mess work.
|
||||
it's very very tricky. it virtually decodes ac3 to LPCM packets, but really
|
||||
it keeps the original compressed data padded by zeros. this way it's
|
||||
constant bitrate, and sync is calculated just like for stereo PCM.
|
||||
(so it bypass LPCM-capable media converters...)
|
||||
|
||||
so, every ac3 frame is translated to 6144 byte long tricky LPCM packet.
|
||||
6144 = 4*(6*256) = 4 * samples_per_ac3_frame = LPCM size of uncompressed ac3
|
||||
frame.
|
||||
|
||||
i wanna know if it works for sblive and other ac3-capable cards too?
|
||||
(i can't test it, lack of ac3 decoder)
|
||||
|
||||
A'rpi / Astral & ESP-team
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Hi folks.
|
||||
I spend some time fiddling with ac3 passthrough in mplayer. The
|
||||
traditional way of setting the output format to AFMT_AC3 was no ideal
|
||||
solution since not all digital io cards/drivers supported this format or
|
||||
honoured it to set the spdif non-audio bit. To make it short, it only
|
||||
worked with oss sblive driver IIRC.
|
||||
|
||||
Inspired by alsa's ac3dec program I found an alternative way by
|
||||
inspecting to which format the alsa device had been set. Suprise: it was
|
||||
simple 16bit_le 2_channel pcm. So setting the non-audio bit doesn't
|
||||
necessarily mean the point. The only important thing seems to be
|
||||
bit-identical output at the correct samplerate. Modern AV-Receivers seem
|
||||
to be quite tolerant/compatible.
|
||||
|
||||
So I changed the output format of hwac3 from
|
||||
|
||||
AFMT_AC3 channels=1
|
||||
to
|
||||
AFMT_S16_LE channels=2
|
||||
|
||||
and corrected the absolute time calculation. That was all to get it
|
||||
running for me.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Hi there.
|
||||
|
||||
Perhaps I can clear up some mystification about AC3 passthrough in
|
||||
general and mplayer in special:
|
||||
|
||||
To get the external decoder solution working, it must be fed with data
|
||||
which is bitidentical to the chunks in the source ac3 file (compressed
|
||||
data is very picky about bit errors). Additionally - or better to say
|
||||
'historically' - the non-audio bit should be set in the spdif status
|
||||
fields to prevent old spdif hardware from reproducing ugly scratchy
|
||||
noise. Note: for current decoders (probably those with DTS capability)
|
||||
this safety bit isn't needed anymore. At least I can state that for my
|
||||
Sherwood RVD-6095RDS. I think it is due to DTS because DTS sound can
|
||||
reside on a ordinary AudioCD and an ordinary AudioCD-Player will always
|
||||
have it's audio-bit set.
|
||||
|
||||
The sample format of the data must be 2channel 16bit (little endian
|
||||
IIRC). Samplerates are 48kHz - although my receiver also accepts
|
||||
44100Hz. I do not know if this is due to an over-compatability of my
|
||||
receiver or if 44100 is also possible in the ac3 specs. For safety's
|
||||
sake lets keep this at 48000Hz. AC3 data chunks are inserted into the
|
||||
stream every 0x1600 bytes (don't bite me on that, look into
|
||||
'ac3-iec958.c': 'ac3_iec958_build_burst').
|
||||
|
||||
To come back to the problem: data must be played bit-identically through
|
||||
the soundcard at the correct samplerate and should optionally have it's
|
||||
non-audio bit set. There are two ways to accomplish this:
|
||||
|
||||
1) Some OSS guy invented the format AFMT_AC3. Soundcard drivers
|
||||
implementing this format should therefore adjust it's mixers and
|
||||
switches to produce the desired output. Unfortunately some soundcard
|
||||
drivers do not support this format correctly and most do not even
|
||||
support it at all (including ALSA).
|
||||
|
||||
2) The alternative approach currently in mplayer CVS is to simply set
|
||||
the output format to 48kHz16bitLE and rely on the user to have the
|
||||
soundcard mixers adjusted properly.
|
||||
|
||||
I do have two soundcards with digital IO facilities (CMI8738 and
|
||||
Trident4DWaveNX based) plus the mentioned decoder. I'm currently running
|
||||
Linux-2.4.17. Following configurations are happily running here:
|
||||
|
||||
1. Trident with ALSA drivers (OSS does not support Hoontech's dig. IO)
|
||||
2. CMI with ALSA drivers
|
||||
3. CMI with OSS drivers
|
||||
|
||||
For Linux I'd suggest using ALSA because of it's cleaner architecture
|
||||
and more consitent user interface. Not to mention that it'll be the
|
||||
standard sound support in Linux soon.
|
||||
|
||||
For those who want to stick to OSS drivers: The CMI8738 drivers works
|
||||
out-of-the-box, if the PCM/Wave mixer is set to 100%.
|
||||
|
||||
For ALSA I'd suggest using its OSS emulation. More on that later.
|
||||
ALSA-0.9 invented the idea of cards, devices and dubdevices. You can
|
||||
reach the digital interface of all supported cards consitently by using
|
||||
the device 'hw:x,2' (x counting from 0 is the number of your soundcard).
|
||||
So most people would end up at 'hw:0,2'. This device can only be opened
|
||||
in sample formats and rates which are directly supported in hardware
|
||||
hence no samplerate conversion is done keeping the stream as-is. However
|
||||
most consumer soundcards do not support 44kHz so it would definitively
|
||||
be a bad idea to use this as your standard device if you wanted to
|
||||
listen to some mp3s (most of them are 44kHz due to CD source). Here the
|
||||
OSS comes to play again. You can configure which OSS device (/dev/dsp
|
||||
and /dev/adsp) uses which ALSA device. So I'd suggest pointing the
|
||||
standard '/dev/dsp' to standard 'hw:0,0' which suports mixing and
|
||||
samplerate conversion. No further reconfiguration would be needed for
|
||||
your sound apps. For movies I'd point '/dev/adsp' to 'hw:0,2' and
|
||||
configure mplayer to use adsp instead of dsp. The samplerate constrain
|
||||
is no big deal here since movies usually are in 48Khz anyway. The
|
||||
configuration in '/etc/modules.conf' is no big deal also:
|
||||
|
||||
alias snd-card-0 snd-card-cmipci # insert your card here
|
||||
alias snd-card-1 snd-pcm-oss # load OSS emulation
|
||||
options snd-pcm-oss snd_dsp_map=0 snd_adsp_map=2 # do the mapping
|
||||
|
||||
This works flawlessly in combination with alsa's native
|
||||
SysVrc-init-script 'alsasound'. Be sure to disable any distribution
|
||||
dependent script (e.g. Mandrake-8.1 has an 'alsa' script which depends
|
||||
on ALSA-0.5).
|
||||
|
||||
Sorry for you *BSD'lers out there. I have no grasp on sound support there.
|
||||
|
||||
HTH Marcus
|
||||
@@ -0,0 +1,56 @@
|
||||
6. libao2: this control audio playing
|
||||
|
||||
As in libvo (see 5.) also here are some drivers, based on the same API:
|
||||
|
||||
static int control(int cmd, int arg);
|
||||
This is for reading/setting driver-specific and other special parameters.
|
||||
Not really used for now.
|
||||
|
||||
static int init(int rate,int channels,int format,int flags);
|
||||
The init of driver, opens device, sets sample rate, channels, sample format
|
||||
parameters.
|
||||
Sample format: usually AFMT_S16_LE or AFMT_U8, for more definitions see
|
||||
dec_audio.c and linux/soundcards.h files!
|
||||
|
||||
static void uninit(void);
|
||||
Guess what.
|
||||
Ok I help: closes the device, not (yet) called when exit.
|
||||
|
||||
static void reset(void);
|
||||
Resets device. To be exact, it's for deleting buffers' contents,
|
||||
so after reset() the previously received stuff won't be output.
|
||||
(called if pause or seek)
|
||||
|
||||
static int get_space(void);
|
||||
Returns how many bytes can be written into the audio buffer without
|
||||
blocking (making caller process wait). MPlayer occasionally checks the
|
||||
remaining space and tries to fill the buffer with play() if there's free
|
||||
space. The buffer size used should be sane; a buffer that is too small
|
||||
could run empty before MPlayer tries filling it again (normally once per
|
||||
video frame), a buffer that is too big would force MPlayer decode the file
|
||||
far ahead trying to find enough audio data to fill it.
|
||||
|
||||
static int play(void* data,int len,int flags);
|
||||
Plays a bit of audio, which is received throught the "data" memory area, with
|
||||
a size of "len". It has to copy the data, because they can be overwritten
|
||||
after the call is made. Doesn't have to use all the bytes; it has to
|
||||
return the number of bytes used used (copied to buffer). If
|
||||
flags|AOPLAY_FINAL_CHUNK is true then this is the last audio in the file.
|
||||
The purpose of this flag is to tell aos that round down the audio played
|
||||
from "len" to a multiple of some chunksize that this "len" should not be
|
||||
rounded down to 0 or the data will never be played (as MPlayer will never
|
||||
call play() with a larger len).
|
||||
|
||||
static float get_delay(void);
|
||||
Returns how long time it will take to play the data currently in the
|
||||
output buffer. Be exact, if possible, since the whole timing depends
|
||||
on this! In the worst case, return the maximum delay.
|
||||
|
||||
!!! Because the video is synchronized to the audio (card), it's very important
|
||||
!!! that the get_delay function is correctly implemented!
|
||||
|
||||
static void audio_pause(void);
|
||||
Pause playing but do not delete buffered data if possible.
|
||||
|
||||
static void audio_resume(void);
|
||||
Continue playing after audio_pause().
|
||||
@@ -0,0 +1,173 @@
|
||||
libvo --- the library to handle video output by A'rpi, 2002.04
|
||||
============================================
|
||||
|
||||
Note: before start on this, read colorspaces.txt !
|
||||
|
||||
The constants for different pixelformats are defined in img_format.h,
|
||||
their usage is mandatory.
|
||||
|
||||
WARNING: Please keep in mind that some of this information may be out-dated,
|
||||
so if you are working on a new vo, consider submitting preliminary patches
|
||||
very early on. Currently vo_gl is one of the more up-to-date VOs to use
|
||||
as reference if you are unsure about something and do not want to ask on the
|
||||
list.
|
||||
vo_vdpau and vo_direct3d may be a good choice, too, they use different
|
||||
approaches closer to the sometimes convoluted way DirectX works.
|
||||
|
||||
Each vo driver _has_ to implement these:
|
||||
|
||||
preinit():
|
||||
init the video system (to support querying for supported formats)
|
||||
|
||||
uninit():
|
||||
Uninit the whole system, this is on the same "level" as preinit.
|
||||
|
||||
control():
|
||||
Current controls (VOCTRL_QUERY_FORMAT must be implemented,
|
||||
VOCTRL_DRAW_IMAGE, VOCTRL_FULLSCREEN, VOCTRL_UPDATE_SCREENINFO
|
||||
should be implemented):
|
||||
VOCTRL_QUERY_FORMAT - queries if a given pixelformat is supported.
|
||||
It also returns various flags decsirbing the capabilities
|
||||
of the driver with teh given mode. for the flags, see
|
||||
file vfcaps.h !
|
||||
the most important flags, every driver must properly report
|
||||
these:
|
||||
0x1 - supported (with or without conversion)
|
||||
0x2 - supported without conversion (define 0x1 too!)
|
||||
0x100 - driver/hardware handles timing (blocking)
|
||||
also SET sw/hw scaling and osd support flags, and flip,
|
||||
and accept_stride if you implement VOCTRL_DRAW_IMAGE (see bellow)
|
||||
NOTE: VOCTRL_QUERY_FORMAT may be called _before_ first config()
|
||||
but is always called between preinit() and uninit()
|
||||
VOCTRL_GET_IMAGE
|
||||
libmpcodecs Direct Rendering interface
|
||||
You need to update mpi (mp_image.h) structure, for example,
|
||||
look at vo_x11, vo_sdl, vo_xv or mga_common.
|
||||
VOCTRL_DRAW_IMAGE
|
||||
replacement for the current draw_slice/draw_frame way of
|
||||
passing video frames. by implementing SET_IMAGE, you'll get
|
||||
image in mp_image struct instead of by calling draw_*.
|
||||
unless you return VO_TRUE for VOCTRL_DRAW_IMAGE call, the
|
||||
old-style draw_* functils will be called!
|
||||
Note: draw_slice is still mandatory, for per-slice rendering!
|
||||
VOCTRL_RESET - reset the video device
|
||||
This is sent on seeking and similar and is useful if you are
|
||||
using a device which prebuffers frames that need to flush them
|
||||
before refilling audio/video buffers.
|
||||
VOCTRL_PAUSE
|
||||
VOCTRL_RESUME
|
||||
VOCTRL_GUISUPPORT
|
||||
return true only if driver supports co-operation with
|
||||
MPlayer's GUI (not yet used by GUI)
|
||||
VOCTRL_SET_EQUALIZER
|
||||
set the video equalizer to the given values
|
||||
two arguments are provided: item and value
|
||||
item is a string, the possible values are (currently):
|
||||
brightness, contrast, saturation, hue
|
||||
VOCTRL_GET_EQUALIZER
|
||||
get the current video equalizer values
|
||||
two arguments are provided: item and value
|
||||
item is a string, the possible values are (currently):
|
||||
brightness, contrast, saturation, hue
|
||||
VOCTRL_ONTOP
|
||||
Makes the player window stay-on-top. Only supported (currently)
|
||||
by drivers which use X11, except SDL, as well as directx and
|
||||
gl2 under Windows.
|
||||
VOCTRL_BORDER
|
||||
Makes the player window borderless.
|
||||
VOCTRL_FULLSCREEN
|
||||
Switch from and to fullscreen mode
|
||||
VOCTRL_GET_PANSCAN
|
||||
VOCTRL_SET_PANSCAN
|
||||
Needed to implement pan-scan support ('w' and 'e' keys during
|
||||
playback in fullscreen mode)
|
||||
VOCTRL_START_SLICE
|
||||
Called before the first draw_slice of each frame, useful if
|
||||
you need to do some set-up work.
|
||||
VOCTRL_DRAW_EOSD
|
||||
Required for EOSD (ASS subtitle) support. Provides all
|
||||
information necessary to draw the EOSD for the current video
|
||||
frame.
|
||||
VOCTRL_GET_EOSD_RES
|
||||
Required for EOSD (ASS subtitle) support. Informs the ASS
|
||||
renderer about the properties of the drawing area (size,
|
||||
borders).
|
||||
VOCTRL_SET_DEINTERLACE
|
||||
VOCTRL_GET_DEINTERLACE
|
||||
Get or set deinterlacing status for VOs that support some kind
|
||||
of deinterlacing.
|
||||
VOCTRL_UPDATE_SCREENINFO
|
||||
Should set the xinerama_x, xinerama_y, vo_screenwidth and
|
||||
vo_screenheight appropriately for the currently used
|
||||
monitor and -xineramascreen option.
|
||||
Usually should simply call the w32_update_xinerama_info or
|
||||
update_xinerama_info function.
|
||||
By supporting this, the VO also requests the newer API
|
||||
that sets vo_dx, vo_dy etc. appropriately before config()
|
||||
is called.
|
||||
|
||||
config():
|
||||
Set up the video system. You get the dimensions and flags.
|
||||
width, height: size of the source image
|
||||
d_width, d_height: wanted scaled/display size (it's a hint)
|
||||
Flags:
|
||||
0x01 - force fullscreen (-fs)
|
||||
0x02 - allow mode switching (-vm)
|
||||
0x04 - allow software scaling (-zoom)
|
||||
0x08 - flipping (-flip)
|
||||
They're defined as VOFLAG_* (see libvo/video_out.h)
|
||||
|
||||
IMPORTANT NOTE: config() may be called 0 (zero), 1 or more (2,3...)
|
||||
times between preinit() and uninit() calls. You MUST handle it, and
|
||||
you shouldn't crash at second config() call or at uninit() without
|
||||
any config() call! To make your life easier, vo_config_count is
|
||||
set to the number of previous config() call, counted from preinit().
|
||||
It's set by the caller (vf_vo.c), you don't have to increase it!
|
||||
So, you can check for vo_config_count>0 in uninit() when freeing
|
||||
resources allocated in config() to avoid crash!
|
||||
|
||||
You should implement VOCTRL_UPDATE_SCREENINFO so that vo_dx, vo_dy,
|
||||
vo_dwidth and vo_dheight are already pre-set to values that take
|
||||
aspect and -geometry into account. It is also necessary to properly
|
||||
support multi-monitor setups (if based on x11_common, w32_common).
|
||||
|
||||
draw_slice(): this displays YV12 pictures (3 planes, one full sized that
|
||||
contains brightness (Y), and 2 quarter-sized which the colour-info
|
||||
(U,V). MPEG codecs (libmpeg2, opendivx) use this. This doesn't have
|
||||
to display the whole frame, only update small parts of it.
|
||||
If this is not supported, it must be signaled in QUERY_FORMAT with
|
||||
VOCAP_NOSLICES.
|
||||
|
||||
draw_frame(): this is the older interface, this displays only complete
|
||||
frames, and can do only packed format (YUY2, RGB/BGR).
|
||||
Win32 codecs use this (DivX, Indeo, etc).
|
||||
If you implement VOCTRL_DRAW_IMAGE, you do not need to implement draw_frame.
|
||||
|
||||
draw_osd(): this displays subtitles and OSD.
|
||||
It's a bit tricky to use it, since it's a callback-style stuff.
|
||||
It should call vo_draw_text() with screen dimension and your
|
||||
draw_alpha implementation for the pixelformat (function pointer).
|
||||
The vo_draw_text() checks the characters to draw, and calls
|
||||
draw_alpha() for each. As a help, osd.c contains draw_alpha for
|
||||
each pixelformats, use this if possible!
|
||||
Note that if you do not draw directly onto the video you should
|
||||
use vo_draw_text_ext() which allows you to specify the border
|
||||
values etc. needed to draw DVD menu highlights at the correct place.
|
||||
If you do not want to implement this, you can still use -vf
|
||||
expand=osd=1 to draw the OSD, or even implement code to insert
|
||||
this filter automatically.
|
||||
Make sure you set VFCAP_OSD depending on whether you implemented it
|
||||
or not.
|
||||
|
||||
NOTE: This one will be obsolete soon! But it's still useful when
|
||||
you want to do tricks, like rendering osd _after_ hardware scaling
|
||||
(tdfxfb) or render subtitles under of the image (vo_mpegpes, sdl)
|
||||
|
||||
NOTE2: above NOTE is probably wrong, there are currently no plans to
|
||||
obsolete draw_osd, though there is the more advanced EOSD support for
|
||||
ASS subtitles.
|
||||
|
||||
flip_page(): this is called after each frame, this displays the buffer for
|
||||
real. This is 'swapbuffers' when doublebuffering.
|
||||
Try to do as little work here as possible, since that affect jitter/
|
||||
A-V sync.
|
||||
@@ -0,0 +1,100 @@
|
||||
TITLE=Weird Al Yankovic - The Saga Begins (clip)
|
||||
# just to be informative (maybe future
|
||||
# use?)
|
||||
FILE=14233792,7972c637f619e01854ecb453b7339e3f # size,md5
|
||||
AUTHOR=Gabucino
|
||||
TYPE=VIDEO # maybe AUDIO in future (for displaying
|
||||
# mp3 lyrics while playing or whatever)
|
||||
FORMAT=TIME # this file contains measures based on
|
||||
# TIME, not frames.
|
||||
|
||||
# if FORMAT is a number, then it's frame based, i.e.:
|
||||
# FORMAT=25
|
||||
|
||||
NOTE=MPlayerSub test file v0.1
|
||||
|
||||
# first number : wait this much after previous subtitle disappeared
|
||||
# second number : display the current subtitle for this many seconds
|
||||
|
||||
15 3
|
||||
A long, long time ago...
|
||||
|
||||
0 3
|
||||
in a galaxy far away...
|
||||
|
||||
0 3
|
||||
Naboo was under an attack.
|
||||
|
||||
1 2.5
|
||||
And I thought me and
|
||||
Qui-Gon Jinn could
|
||||
|
||||
0 2.5
|
||||
talk the Federation into
|
||||
|
||||
0 4
|
||||
...maybe cutting them a
|
||||
little slack.
|
||||
|
||||
2 3
|
||||
But their response, it
|
||||
didn't thrill us,
|
||||
|
||||
0 3
|
||||
They locked the doors,
|
||||
and tried to kill us.
|
||||
|
||||
0 2.5
|
||||
We escaped from that gas,
|
||||
|
||||
0 3.5
|
||||
then met Jar-jar and
|
||||
Boss-Nass.
|
||||
|
||||
1 6
|
||||
We took a bongo from the
|
||||
scene and we went to
|
||||
Theed to see the Queen.
|
||||
|
||||
0 5
|
||||
We all wound' up on
|
||||
Tatooine.
|
||||
|
||||
0 6
|
||||
That's where, we've found
|
||||
this boy.
|
||||
|
||||
0 4
|
||||
Oh my, my this here
|
||||
Anakin guy,
|
||||
|
||||
0 5
|
||||
maybe Vader someday
|
||||
later now he's just
|
||||
a small fry.
|
||||
|
||||
0 4
|
||||
And he left his home and
|
||||
kissed his mommy goodbye,
|
||||
|
||||
0 5
|
||||
singing "Soon I'm gonna be
|
||||
a Jedi!"
|
||||
|
||||
6 6
|
||||
Did you know this junkyard
|
||||
slave isn't even old enough
|
||||
to shave,
|
||||
|
||||
0 3
|
||||
but he can use the Force,
|
||||
they say.
|
||||
|
||||
1 6
|
||||
Ahh, do you see him hitting
|
||||
on the queen though he's
|
||||
just nine and she's fourteen
|
||||
|
||||
0 6
|
||||
yeah, he's probably gonna
|
||||
marry her, someday!
|
||||
@@ -0,0 +1,19 @@
|
||||
software scaler filter docs
|
||||
|
||||
-ssf cgb=<float> chrominance gaussian blur
|
||||
useful for videos with some chroma (color) noise (TV captures, VHS, ...)
|
||||
|
||||
-ssf lgb=<float> -> luminance gaussian blur
|
||||
hmm, mostly special effect, could be used as lame noise reducer though
|
||||
|
||||
-ssf chs=<int> -> shift chrominance horizontally
|
||||
-ssf cvs=<int> -> shift chrominance vertically
|
||||
useful if the luma & chroma are not aligned exactly
|
||||
|
||||
-ssf cs=<float> -> chrominance sharpen
|
||||
-ssf ls=<float> -> luminance sharpen
|
||||
simple sharpening filter
|
||||
|
||||
note: these will only work if the swscaler is used at all (use -vf scale)
|
||||
|
||||
example: mplayer foobar.avi -vf scale -ssf lgb=3.0
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
artifact types:
|
||||
---------------------------------------------------------------------
|
||||
ringing
|
||||
wave or noise like patterns around sharp edges
|
||||
bad: sinc, lanczos (with high filter length)
|
||||
acceptable: lanczos (with low filter length), cubic, spline
|
||||
ok: area, (fast)blinear, gauss, point
|
||||
|
||||
blur
|
||||
loss of detail / high frequency
|
||||
bad: gauss with high variance
|
||||
acceptable: (fast)bilinear, area
|
||||
ok: others
|
||||
|
||||
aliasing (only downscale)
|
||||
straight lines look like staircases
|
||||
areas of high detail look worse
|
||||
regular patterns turn into a funny looking mess (moire patterns)
|
||||
bad: fast bilinear, point, gauss with low variance
|
||||
acceptable: area
|
||||
ok: others
|
||||
|
||||
blocky (upscale only)
|
||||
looks like the image is made of rectangular blocks like a mosaic
|
||||
bad: point
|
||||
ok: others
|
||||
|
||||
recommendations: (based upon my personal opinion many ppl might disagree ...)
|
||||
---------------------------------------------------------------------
|
||||
|
||||
the recommended scalers for upscaling:
|
||||
fast_bilinear, point if speed is important
|
||||
cubic, spline, lanczos if quality is important
|
||||
|
||||
the recommended scalers for downscaling:
|
||||
fast_bilinear, point if speed is important
|
||||
gauss, bilinear if quality is important
|
||||
cubic, spline, lanczos if a sharper picture is important
|
||||
|
||||
note: when encoding at a limited amount of bits (not constant quantizer)
|
||||
then a slightly blurred input might look better after decoding than a slightly
|
||||
sharpened one, especially for lower bitrates
|
||||
|
||||
sws numbers / names / artifact types
|
||||
-------------------------------------------------------------------------
|
||||
r ringing
|
||||
b blurry
|
||||
a aliasing (downscale only)
|
||||
m mosaic (blocky) (upscale only)
|
||||
|
||||
-sws 0 fast bilinear bA
|
||||
-sws 1 bilinear b
|
||||
-sws 2 bicubic high sharpness r
|
||||
low sharpness b
|
||||
-sws 3 experimental ????
|
||||
-sws 4 nearest neighbour AM
|
||||
-sws 5 area ba
|
||||
-sws 6 luma bicubic / chroma bilinear r
|
||||
-sws 7 gauss low sharpness B
|
||||
high sharpness AM
|
||||
-sws 8 sinc R
|
||||
-sws 9 lanczos long filter R
|
||||
short filter b
|
||||
-sws 10 bicubic spline r
|
||||
|
||||
Notes:
|
||||
area upscale is identical to bilinear
|
||||
@@ -1,74 +0,0 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
This file lists all changes that can cause compatibility issues when using
|
||||
mpv through the client API (libmpv and ``client.h``). Since the client API
|
||||
interfaces to input handling (commands, properties) as well as command line
|
||||
options, this list is interesting for other uses of mpv, such as the Lua
|
||||
scripting interface, key bindings in ``input.rst``, or plain command line
|
||||
usage.
|
||||
|
||||
Normally, changes to the C API that are incompatible to previous iterations
|
||||
receive a major version bump (i.e. the first version number is increased),
|
||||
while C API additions bump the minor version (i.e. the second number is
|
||||
increased). Changes to properties/commands/options may also lead to a minor
|
||||
version bump, in particular if they are incompatible.
|
||||
|
||||
The version number is the same as used for MPV_CLIENT_API_VERSION (see
|
||||
``client.h`` how to convert between major/minor version numbers and the flat
|
||||
32 bit integer).
|
||||
|
||||
Also, read the section ``Compatibility`` in ``client.h``.
|
||||
|
||||
API changes
|
||||
===========
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.7.0 is released ---
|
||||
1.10 - deprecate/disable everything directly related to script_dispatch
|
||||
(most likely affects nobody)
|
||||
1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason
|
||||
- add MPV_END_FILE_REASON_ERROR and the mpv_event_end_file.error field
|
||||
for slightly better error reporting on playback failure
|
||||
- add --stop-playback-on-init-failure option, and make it the default
|
||||
behavior for libmpv only
|
||||
- add qthelper.hpp set_option_variant()
|
||||
- mark the following events as deprecated:
|
||||
MPV_EVENT_TRACKS_CHANGED
|
||||
MPV_EVENT_TRACK_SWITCHED
|
||||
MPV_EVENT_PAUSE
|
||||
MPV_EVENT_UNPAUSE
|
||||
MPV_EVENT_METADATA_UPDATE
|
||||
MPV_EVENT_CHAPTER_CHANGE
|
||||
They are handled better with mpv_observe_property() as mentioned in
|
||||
the documentation comments. They are not removed and still work.
|
||||
1.8 - add qthelper.hpp
|
||||
1.7 - add mpv_command_node(), mpv_command_node_async()
|
||||
1.6 - modify "core-idle" property behavior
|
||||
- MPV_EVENT_LOG_MESSAGE now always sends complete lines
|
||||
- introduce numeric log levels (mpv_log_level)
|
||||
--- mpv 0.6.0 is released ---
|
||||
1.5 - change in X11 and "--wid" behavior again. The previous change didn't
|
||||
work as expected, and now the behavior can be explicitly controlled
|
||||
with the "input-x11-keyboard" option. This is only a temporary
|
||||
measure until XEmbed is implemented and confirmed working.
|
||||
Note: in 1.6, "input-x11-keyboard" was renamed to "input-vo-keyboard",
|
||||
although the old option name still works.
|
||||
1.4 - subtle change in X11 and "--wid" behavior
|
||||
(this change was added to 0.5.2, and broke some things, see #1090)
|
||||
--- mpv 0.5.0 is released ---
|
||||
1.3 - add MPV_MAKE_VERSION()
|
||||
1.2 - remove "stream-time-pos" property (no replacement)
|
||||
1.1 - remap dvdnav:// to dvd://
|
||||
- add "--cache-file", "--cache-file-size"
|
||||
- add "--colormatrix-primaries" (and property)
|
||||
- add "primaries" sub-field to image format properties
|
||||
- add "playback-time" property
|
||||
- extend the "--start" option; a leading "+", which was previously
|
||||
insignificant is now significant
|
||||
- add "cache-free" and "cache-used" properties
|
||||
- OSX: the "coreaudio" AO spdif code is split into a separate AO
|
||||
--- mpv 0.4.0 is released ---
|
||||
1.0 - the API is declared stable
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
// Plays a video from the command line in a window provided by mpv.
|
||||
// You likely want to play the video in your own window instead,
|
||||
// but that's not quite ready yet.
|
||||
// You may need a basic Info.plist and MainMenu.xib to make this work.
|
||||
|
||||
#include "../../libmpv/client.h"
|
||||
#include "shared.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface CocoaWindow : NSWindow
|
||||
@end
|
||||
|
||||
@implementation CocoaWindow
|
||||
- (BOOL)canBecomeMainWindow { return YES; }
|
||||
- (BOOL)canBecomeKeyWindow { return YES; }
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
mpv_handle *mpv;
|
||||
dispatch_queue_t queue;
|
||||
NSWindow *w;
|
||||
}
|
||||
@end
|
||||
|
||||
static void wakeup(void *);
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)createWindow {
|
||||
|
||||
int mask = NSTitledWindowMask|NSClosableWindowMask|
|
||||
NSMiniaturizableWindowMask|NSResizableWindowMask;
|
||||
|
||||
self->w = [[CocoaWindow alloc]
|
||||
initWithContentRect:NSMakeRect(0,0, 1280, 720)
|
||||
styleMask:mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
[self->w setTitle:@"cocoabasic example"];
|
||||
[self->w makeMainWindow];
|
||||
[self->w makeKeyAndOrderFront:nil];
|
||||
|
||||
NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"];
|
||||
NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""];
|
||||
NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"];
|
||||
[m setSubmenu:sm forItem:item];
|
||||
[sm addItemWithTitle: @"Shutdown mpv" action:@selector(shutdown) keyEquivalent:@"s"];
|
||||
[NSApp setMenu:m];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
atexit_b(^{
|
||||
// Because activation policy has just been set to behave like a real
|
||||
// application, that policy must be reset on exit to prevent, among
|
||||
// other things, the menubar created here from remaining on screen.
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||
});
|
||||
|
||||
// Read filename
|
||||
NSArray *args = [NSProcessInfo processInfo].arguments;
|
||||
if (args.count < 2) {
|
||||
NSLog(@"Expected filename on command line");
|
||||
exit(1);
|
||||
}
|
||||
NSString *filename = args[1];
|
||||
|
||||
[self createWindow];
|
||||
|
||||
// Deal with MPV in the background.
|
||||
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv) {
|
||||
printf("failed creating context\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int64_t wid = (intptr_t) [self->w contentView];
|
||||
check_error(mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid));
|
||||
|
||||
// Maybe set some options here, like default key bindings.
|
||||
// NOTE: Interaction with the window seems to be broken for now.
|
||||
check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes"));
|
||||
|
||||
// for testing!
|
||||
check_error(mpv_set_option_string(mpv, "input-media-keys", "yes"));
|
||||
check_error(mpv_set_option_string(mpv, "input-cursor", "no"));
|
||||
check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes"));
|
||||
|
||||
// request important errors
|
||||
check_error(mpv_request_log_messages(mpv, "warn"));
|
||||
|
||||
check_error(mpv_initialize(mpv));
|
||||
|
||||
// Register to be woken up whenever mpv generates new events.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self);
|
||||
|
||||
// Load the indicated file
|
||||
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
|
||||
check_error(mpv_command(mpv, cmd));
|
||||
});
|
||||
}
|
||||
|
||||
- (void) handleEvent:(mpv_event *)event
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_SHUTDOWN:
|
||||
// Clean up and shut down.
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSApplication sharedApplication] terminate:nil];
|
||||
});
|
||||
break;
|
||||
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
|
||||
}
|
||||
|
||||
case MPV_EVENT_VIDEO_RECONFIG:
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self->w selectNextKeyView:nil];
|
||||
});
|
||||
|
||||
default:
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
}
|
||||
}
|
||||
|
||||
- (void) readEvents
|
||||
{
|
||||
dispatch_async(queue, ^{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
[self handleEvent:event];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void wakeup(void *context) {
|
||||
AppDelegate *a = (__bridge AppDelegate *) context;
|
||||
[a readEvents];
|
||||
}
|
||||
|
||||
// Ostensibly, mpv's window would be hooked up to this.
|
||||
- (BOOL) windowShouldClose:(id)sender
|
||||
{
|
||||
[self shutdown];
|
||||
if (self->w)
|
||||
[self->w release];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
if (mpv) {
|
||||
const char *args[] = {"quit", NULL};
|
||||
mpv_command(mpv, args);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
// Delete this if you already have a main.m.
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate *delegate = [AppDelegate new];
|
||||
app.delegate = delegate;
|
||||
[app run];
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
// License: pick one of: public domain, WTFPL, ISC, Ms-PL, AGPLv3
|
||||
|
||||
// This example can be built with: qmake && make
|
||||
|
||||
#include <clocale>
|
||||
#include <sstream>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFileDialog>
|
||||
#include <QStatusBar>
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QGridLayout>
|
||||
#include <QApplication>
|
||||
#include <QTextEdit>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QJsonDocument>
|
||||
#endif
|
||||
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
#include "qtexample.h"
|
||||
|
||||
static void wakeup(void *ctx)
|
||||
{
|
||||
// This callback is invoked from any mpv thread (but possibly also
|
||||
// recursively from a thread that is calling the mpv API). Just notify
|
||||
// the Qt GUI thread to wake up (so that it can process events with
|
||||
// mpv_wait_event()), and return as quickly as possible.
|
||||
MainWindow *mainwindow = (MainWindow *)ctx;
|
||||
QCoreApplication::postEvent(mainwindow, new QEvent(QEvent::User));
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
setWindowTitle("Qt embedding demo");
|
||||
setMinimumSize(640, 480);
|
||||
|
||||
QMenu *menu = menuBar()->addMenu(tr("&File"));
|
||||
QAction *on_open = new QAction(tr("&Open"), this);
|
||||
on_open->setShortcuts(QKeySequence::Open);
|
||||
on_open->setStatusTip(tr("Open a file"));
|
||||
connect(on_open, SIGNAL(triggered()), this, SLOT(on_file_open()));
|
||||
menu->addAction(on_open);
|
||||
|
||||
statusBar();
|
||||
|
||||
QMainWindow *log_window = new QMainWindow(this);
|
||||
log = new QTextEdit(log_window);
|
||||
log->setReadOnly(true);
|
||||
log_window->setCentralWidget(log);
|
||||
log_window->setWindowTitle("mpv log window");
|
||||
log_window->setMinimumSize(500, 50);
|
||||
log_window->show();
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv)
|
||||
throw "can't create mpv instance";
|
||||
|
||||
// Create a video child window. Force Qt to create a native window, and
|
||||
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa
|
||||
mpv_container = new QWidget(this);
|
||||
setCentralWidget(mpv_container);
|
||||
mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
mpv_container->setAttribute(Qt::WA_NativeWindow);
|
||||
// If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
|
||||
int64_t wid = mpv_container->winId();
|
||||
mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
|
||||
|
||||
// Enable default bindings, because we're lazy. Normally, a player using
|
||||
// mpv as backend would implement its own key bindings.
|
||||
mpv_set_option_string(mpv, "input-default-bindings", "yes");
|
||||
|
||||
// Enable keyboard input on the X11 window. For the messy details, see
|
||||
// --input-vo-keyboard on the manpage.
|
||||
mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
|
||||
|
||||
// Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
|
||||
// this property changes.
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
|
||||
mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE);
|
||||
|
||||
// Request log messages with level "info" or higher.
|
||||
// They are received as MPV_EVENT_LOG_MESSAGE.
|
||||
mpv_request_log_messages(mpv, "info");
|
||||
|
||||
// From this point on, the wakeup function will be called. The callback
|
||||
// can come from any thread, so we use the Qt QEvent mechanism to relay
|
||||
// the wakeup in a thread-safe way.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, this);
|
||||
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw "mpv failed to initialize";
|
||||
}
|
||||
|
||||
void MainWindow::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
std::stringstream ss;
|
||||
ss << "At: " << time;
|
||||
statusBar()->showMessage(QString::fromStdString(ss.str()));
|
||||
} else if (prop->format == MPV_FORMAT_NONE) {
|
||||
// The property is unavailable, which probably means playback
|
||||
// was stopped.
|
||||
statusBar()->showMessage("");
|
||||
}
|
||||
} else if (strcmp(prop->name, "chapter-list") == 0 ||
|
||||
strcmp(prop->name, "track-list") == 0)
|
||||
{
|
||||
// Dump the properties as JSON for demo purposes.
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (prop->format == MPV_FORMAT_NODE) {
|
||||
QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data);
|
||||
// Abuse JSON support for easily printing the mpv_node contents.
|
||||
QJsonDocument d = QJsonDocument::fromVariant(v);
|
||||
append_log("Change property " + QString(prop->name) + ":\n");
|
||||
append_log(d.toJson().data());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_VIDEO_RECONFIG: {
|
||||
// Retrieve the new video size.
|
||||
int64_t w, h;
|
||||
if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
|
||||
mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 &&
|
||||
w > 0 && h > 0)
|
||||
{
|
||||
// Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily
|
||||
// imply a resize, and you should check yourself if the video
|
||||
// dimensions really changed.
|
||||
// mpv itself will scale/letter box the video to the container size
|
||||
// if the video doesn't fit.
|
||||
std::stringstream ss;
|
||||
ss << "Reconfig: " << w << " " << h;
|
||||
statusBar()->showMessage(QString::fromStdString(ss.str()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
std::stringstream ss;
|
||||
ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
|
||||
append_log(QString::fromStdString(ss.str()));
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
break;
|
||||
}
|
||||
default: ;
|
||||
// Ignore uninteresting or unknown events.
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::event(QEvent *event)
|
||||
{
|
||||
// QEvent::User is sent by wakeup().
|
||||
if (event->type() == QEvent::User) {
|
||||
// Process all events, until the event queue is empty.
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
handle_mpv_event(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return QMainWindow::event(event);
|
||||
}
|
||||
|
||||
void MainWindow::on_file_open()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Open file");
|
||||
if (mpv) {
|
||||
const QByteArray c_filename = filename.toUtf8();
|
||||
const char *args[] = {"loadfile", c_filename.data(), NULL};
|
||||
mpv_command_async(mpv, 0, args);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::append_log(const QString &text)
|
||||
{
|
||||
QTextCursor cursor = log->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(text);
|
||||
log->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if (mpv)
|
||||
mpv_terminate_destroy(mpv);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// Qt sets the locale in the QApplication constructor, but libmpv requires
|
||||
// the LC_NUMERIC category to be set to "C", so change it back.
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef QTEXAMPLE_H
|
||||
#define QTEXAMPLE_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
class QTextEdit;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
protected:
|
||||
virtual bool event(QEvent *event);
|
||||
|
||||
private slots:
|
||||
void on_file_open();
|
||||
|
||||
private:
|
||||
QWidget *mpv_container;
|
||||
mpv_handle *mpv;
|
||||
QTextEdit *log;
|
||||
|
||||
void append_log(const QString &text);
|
||||
|
||||
void create_player();
|
||||
void handle_mpv_event(mpv_event *event);
|
||||
};
|
||||
|
||||
#endif // QTEXAMPLE_H
|
||||
@@ -1,12 +0,0 @@
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = qtexample
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG = mpv
|
||||
|
||||
SOURCES += qtexample.cpp
|
||||
HEADERS += qtexample.h
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline void check_error(int status)
|
||||
{
|
||||
if (status < 0) {
|
||||
printf("mpv API error: %s\n", mpv_error_string(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "libmpv/client.h"
|
||||
#include "shared.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("pass a single media file as argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mpv_handle *ctx = mpv_create();
|
||||
if (!ctx) {
|
||||
printf("failed creating context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Enable default key bindings, so the user can actually interact with
|
||||
// the player (and e.g. close the window).
|
||||
check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));
|
||||
mpv_set_option_string(ctx, "input-x11-keyboard", "yes");
|
||||
int val = 1;
|
||||
check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));
|
||||
|
||||
// Done setting up options.
|
||||
check_error(mpv_initialize(ctx));
|
||||
|
||||
// Play this file.
|
||||
const char *cmd[] = {"loadfile", argv[1], NULL};
|
||||
check_error(mpv_command(ctx, cmd));
|
||||
|
||||
// Let it play, and wait until the user quits.
|
||||
while (1) {
|
||||
mpv_event *event = mpv_wait_event(ctx, 10000);
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
if (event->event_id == MPV_EVENT_SHUTDOWN)
|
||||
break;
|
||||
}
|
||||
|
||||
mpv_terminate_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
How to contribute
|
||||
=================
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
The main contact for mpv development is IRC, specifically #mpv-player and
|
||||
#mpv-player-dev on Freenode.
|
||||
|
||||
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.
|
||||
- If you add a new command line option, document it in options.rst. If you
|
||||
add a new input property, document it in input.rst.
|
||||
|
||||
Code formatting
|
||||
---------------
|
||||
|
||||
mpv uses C99 with K&R formatting, with some exceptions.
|
||||
|
||||
- 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.
|
||||
@@ -1,83 +0,0 @@
|
||||
Cross Compiling to Windows
|
||||
==========================
|
||||
|
||||
Cross compiling mpv to Windows is supported with MinGW-w64. This can be used to
|
||||
produce both 32 bit and 64 bit executables. MinGW-w64 is available from
|
||||
http://mingw-w64.sourceforge.net.
|
||||
|
||||
You have to run mpv's configure with these arguments:
|
||||
|
||||
```bash
|
||||
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
|
||||
```
|
||||
|
||||
While building a complete MinGW-w64 toolchain yourself is possible, people have
|
||||
created scripts to help ease the process. These are the two recommended methods:
|
||||
|
||||
- Using [mingw-w64-cmake](https://github.com/lachs0r/mingw-w64-cmake) to setup
|
||||
a MinGW-w64 environment. We recommend you to try this first before MXE.
|
||||
mingw-w64-cmake will also build mpv and its dependencies.
|
||||
- Alternatively, you can use MXE: http://mxe.cc. With MXE, you have to manually
|
||||
set the target to MinGW-w64 (even if you compile to 32 bit). A working example
|
||||
below.
|
||||
|
||||
**Warning**: the original MinGW (http://www.mingw.org) is unsupported.
|
||||
|
||||
Note that MinGW environments included in Linux distributions are often broken,
|
||||
outdated and useless, and usually don't use MinGW-w64.
|
||||
|
||||
Additional dependencies
|
||||
-----------------------
|
||||
|
||||
You need a pthread wrapper. It must be interoperable with native Windows
|
||||
threads. pthreads-win32 or MinGW pthreads might work.
|
||||
|
||||
If you want ``--input-file=...`` to work, you need libwaio. It's available
|
||||
from: git://midipix.org/waio
|
||||
|
||||
Example with MXE
|
||||
----------------
|
||||
|
||||
```bash
|
||||
# Download MXE. Note that compiling the required packages requires about 1.4 GB
|
||||
# or more!
|
||||
|
||||
cd /opt
|
||||
git clone https://github.com/mxe/mxe mingw
|
||||
cd mingw
|
||||
|
||||
# Set build options.
|
||||
|
||||
# The JOBS environment variable controls threads to use when building. DO NOT
|
||||
# use the regular `make -j4` option with MXE as it will slow down the build.
|
||||
# Alternatively, you can set this in the make command by appending "JOBS=4"
|
||||
# to the end of command:
|
||||
echo "JOBS := 4" >> settings.mk
|
||||
|
||||
# The MXE_TARGET environment variable builds MinGW-w64 for 32 bit targets.
|
||||
# Alternatively, you can specify this in the make command by appending
|
||||
# "MXE_TARGETS=i686-w64-mingw32" to the end of command:
|
||||
echo "MXE_TARGETS := i686-w64-mingw32" >> settings.mk
|
||||
|
||||
# If you want to build 64 bit version, use this:
|
||||
# echo "MXE_TARGETS := x86_64-w64-mingw32" >> settings.mk
|
||||
|
||||
# Build required packages. The following provide a minimum required to build
|
||||
# mpv.
|
||||
|
||||
make gcc ffmpeg libass jpeg pthreads
|
||||
|
||||
# Add MXE binaries to $PATH
|
||||
export PATH=/opt/mingw/usr/bin/:$PATH
|
||||
|
||||
# Build mpv. The target will be used to automatically select the name of the
|
||||
# build tools involved (e.g. it will use i686-w64-mingw32-gcc).
|
||||
|
||||
cd ..
|
||||
git clone https://github.com/mpv-player/mpv.git
|
||||
cd mpv
|
||||
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
|
||||
# Or, if 64 bit version,
|
||||
# DEST_OS=win32 TARGET=x86_64-w64-mingw32 ./waf configure
|
||||
./waf build
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
Cross compiling mpv to Windows is supported with MinGW-w64. This can be used to
|
||||
produce both 32 bit and 64 bit executables. MinGW-w64 is available from
|
||||
http://mingw-w64.sourceforge.net.
|
||||
|
||||
You have to run mpv's configure with these arguments:
|
||||
./configure --enable-cross-compile --target=i686-w64-mingw32
|
||||
|
||||
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
|
||||
|
||||
Warning: the original MinGW (http://www.mingw.org) is unsupported.
|
||||
|
||||
Note that MinGW environments included in Linux distributions are often broken,
|
||||
outdated and useless, and usually don't use MinGW-w64.
|
||||
|
||||
Example with MXE
|
||||
================
|
||||
|
||||
# Download MXE. Note that compiling the required packages requires about 1 GB
|
||||
# or more!
|
||||
|
||||
cd /opt
|
||||
git clone https://github.com/mxe/mxe
|
||||
cd mingw
|
||||
|
||||
# Edit the MXE target, so that MinGW-w64 for 32 bit targets is built.
|
||||
|
||||
echo "MXE_TARGETS := i686-w64-mingw32" > settings.mk
|
||||
|
||||
# Build required packages. The following provide a minimum required to build
|
||||
# mpv. (Not all of the following packages are strictly required.)
|
||||
|
||||
make gcc
|
||||
make ffmpeg
|
||||
make libass
|
||||
make jpeg
|
||||
make pthreads
|
||||
|
||||
# Build mpv. The target will be used to automatically select the name of the
|
||||
# build tools involved (e.g. it will use i686-w64-mingw32-gcc).
|
||||
|
||||
git clone https://github.com/mpv-player/mpv.git
|
||||
cd mpv
|
||||
export PATH=/opt/mingw/usr/bin/:$PATH
|
||||
./configure --enable-cross-compile --target=i686-w64-mingw32
|
||||
make
|
||||
|
||||
# This should work. Note however that MXE’s ffmpeg package might be very old
|
||||
# in order to avoid breaking e.g. xine-lib, so you might want to update that
|
||||
# or build it manually as well.
|
||||
@@ -1,121 +0,0 @@
|
||||
EDL files
|
||||
=========
|
||||
|
||||
EDL files basically concatenate ranges of video/audio from multiple source
|
||||
files into a single continuous virtual file. Each such range is called a
|
||||
segment, and consists of source file, source offset, and segment length.
|
||||
|
||||
For example::
|
||||
|
||||
# mpv EDL v0
|
||||
f1.mkv,10,20
|
||||
f2.mkv
|
||||
f1.mkv,40,10
|
||||
|
||||
This would skip the first 10 seconds of the file f1.mkv, then play the next
|
||||
20 seconds, then switch to the file f2.mkv and play all of it, then switch
|
||||
back to f1.mkv, skip to the 40 second mark, and play 10 seconds, and then
|
||||
stop playback. The difference to specifying the files directly on command
|
||||
line (and using ``--{ --start=10 --length=20 f1.mkv --}`` etc.) is that the
|
||||
virtual EDL file appears as a virtual timeline (like a single file), instead
|
||||
as a playlist.
|
||||
|
||||
The general simplified syntax is::
|
||||
|
||||
# mpv EDL v0
|
||||
<filename>
|
||||
<filename>,<start in seconds>,<length in seconds>
|
||||
|
||||
If the start time is omitted, 0 is used. If the length is omitted, the
|
||||
estimated remaining duration of the source file is used.
|
||||
|
||||
Note::
|
||||
|
||||
mpv can't use ordered chapter files in EDL entries. Usage of relative or
|
||||
absolute paths as well as any protocol prefixes is prevented for security
|
||||
reasons.
|
||||
|
||||
|
||||
Syntax of mpv EDL files
|
||||
=======================
|
||||
|
||||
Generally, the format is relatively strict. No superfluous whitespace (except
|
||||
empty lines and commented lines) are allowed. You must use UNIX line breaks.
|
||||
|
||||
The first line in the file must be ``# mpv EDL v0``. This designates that the
|
||||
file uses format version 0, which is not frozen yet and may change any time.
|
||||
(If you need a stable EDL file format, make a feature request. Likewise, if
|
||||
you have suggestions for improvements, it's not too late yet.)
|
||||
|
||||
The rest of the lines belong to one of these classes:
|
||||
|
||||
1) An empty or commented line. A comment starts with ``#``, which must be the
|
||||
first character in the line. The rest of the line (up until the next line
|
||||
break) is ignored. An empty line has 0 bytes between two line feed bytes.
|
||||
2) A segment entry in all other cases.
|
||||
|
||||
Each segment entry consists of a list of named or unnamed parameters.
|
||||
Parameters are separated with ``,``. Named parameters consist of a name,
|
||||
followed by ``=``, followed by the value. Unnamed parameters have only a
|
||||
value, and the name is implicit from the parameter position.
|
||||
|
||||
Syntax::
|
||||
|
||||
segment_entry ::= <param> ( <param> ',' )*
|
||||
param ::= [ <name> '=' ] ( <value> | '%' <number> '%' <valuebytes> )
|
||||
|
||||
The ``name`` string can consist of any characters, except ``=%,;\n``. The
|
||||
``value`` string can consist of any characters except of ``,;\n``.
|
||||
|
||||
The construct starting with ``%`` allows defining any value with arbitrary
|
||||
contents inline, where ``number`` is an integer giving the number of bytes in
|
||||
``valuebytes``. If a parameter value contains disallowed characters, it has to
|
||||
be guarded by a length specifier using this syntax.
|
||||
|
||||
The parameter name defines the meaning of the parameter:
|
||||
|
||||
1) ``file``, the source file to use for this segment.
|
||||
2) ``start``, a time value that specifies the start offset into the source file.
|
||||
3) ``length``, a time value that specifies the length of the segment.
|
||||
|
||||
See the section below for the format of timestamps.
|
||||
|
||||
Unnamed parameters carry implicit names. The parameter position determines
|
||||
which of the parameters listed above is set. For example, the second parameter
|
||||
implicitly uses the name ``start``.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
%18%filename,with,.mkv,10,length=20,param3=%13%value,escaped,param4=value2
|
||||
|
||||
this sets ``file`` to ``filename,with,.mkv``, ``start`` to ``10``, ``length``
|
||||
to ``20``, ``param3`` to ``value,escaped``, ``param4`` to ``value2``.
|
||||
|
||||
Instead of line breaks, the character ``;`` can be used. Line feed bytes and
|
||||
``;`` are treated equally.
|
||||
|
||||
Timestamp format
|
||||
================
|
||||
|
||||
Currently, time values are floating point values in seconds.
|
||||
|
||||
As an extension, you can set the ``timestamps=chapters`` option. If this option
|
||||
is set, timestamps have to be integers, and refer to chapter numbers, starting
|
||||
with 0.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
file.mkv,2,4,timestamps=chapters
|
||||
|
||||
Plays chapter 3 and ends with the start of chapter 7 (4 chapters later).
|
||||
|
||||
Syntax of EDL URIs
|
||||
==================
|
||||
|
||||
mpv accepts inline EDL data in form of ``edl://`` URIs. Other than the
|
||||
header, the syntax is exactly the same. It's far more convenient to use ``;``
|
||||
instead of line breaks, but that is orthogonal.
|
||||
|
||||
Example: ``edl://f1.mkv,length=5,start=10;f2.mkv,30,20;f3.mkv``
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
Format of mplayer2 EDL files
|
||||
============================
|
||||
|
||||
The first line in the file must be ``mplayer EDL file, version 2``.
|
||||
The rest of the lines belong to one of these classes:
|
||||
|
||||
1) lines specifying source files
|
||||
2) empty lines
|
||||
3) lines specifying timeline segments.
|
||||
|
||||
Lines beginning with ``<`` specify source files. These lines first
|
||||
contain an identifier used to refer to the source file later, then the
|
||||
filename separated by whitespace. The identifier must start with a
|
||||
letter. Filenames that start or end with whitespace or contain
|
||||
newlines are not supported.
|
||||
|
||||
On other lines ``#`` characters delimit comments. Lines that contain
|
||||
only whitespace after comments have been removed are ignored.
|
||||
|
||||
Timeline segments must appear in the file in chronological order. Each
|
||||
segment has the following information associated with it:
|
||||
|
||||
- duration
|
||||
- output start time
|
||||
- output end time (= output start time + duration)
|
||||
- source id (specifies the file the content of the segment comes from)
|
||||
- source start time (timestamp in the source file)
|
||||
- source end time (= source start time + duration)
|
||||
|
||||
The output timestamps must form a continuous timeline from 0 to the
|
||||
end of the last segment, such that each new segment starts from the
|
||||
time the previous one ends at. Source files and times may change
|
||||
arbitrarily between segments.
|
||||
|
||||
The general format for lines specifying timeline segments is
|
||||
[output time info] source_id [source time info]
|
||||
source_id must be an identifier defined on a ``<`` line. Both the time
|
||||
info parts consists of zero or more of the following elements:
|
||||
|
||||
1) ``timestamp``
|
||||
2) ``-timestamp``
|
||||
3) ``+duration``
|
||||
4) ``*``
|
||||
5) ``-*``
|
||||
|
||||
, where ``timestamp`` and ``duration`` are decimal numbers (computations
|
||||
are done with nanosecond precision). Whitespace around ``+`` and ``-`` is
|
||||
optional. 1) and 2) specify start and end time of the segment on
|
||||
output or source side. 3) specifies duration; the semantics are the
|
||||
same whether this appears on output or source side. 4) and 5) are
|
||||
ignored on the output side (they're always implicitly assumed). On the
|
||||
source side 4) specifies that the segment starts where the previous
|
||||
segment *using this source* ended; if there was no previous segment
|
||||
time 0 is used. 5) specifies that the segment ends where the next
|
||||
segment using this source starts.
|
||||
|
||||
Redundant information may be omitted. It will be filled in using the
|
||||
following rules:
|
||||
|
||||
- output start for first segment is 0
|
||||
- two of [output start, output end, duration] imply third
|
||||
- two of [source start, source end, duration] imply third
|
||||
- output start = output end of previous segment
|
||||
- output end = output start of next segment
|
||||
- if ``*``, source start = source end of earlier segment
|
||||
- if ``-*``, source end = source start of a later segment
|
||||
|
||||
As a special rule, a last zero-duration segment without a source
|
||||
specification may appear. This will produce no corresponding segment
|
||||
in the resulting timeline, but can be used as syntax to specify the
|
||||
end time of the timeline (with effect equal to adding -time on the
|
||||
previous line).
|
||||
|
||||
Examples::
|
||||
|
||||
mplayer EDL file, version 2
|
||||
< id1 filename
|
||||
|
||||
0 id1 123
|
||||
100 id1 456
|
||||
200 id1 789
|
||||
300
|
||||
|
||||
All segments come from the source file ``filename``. First segment
|
||||
(output time 0-100) comes from time 123-223, second 456-556, third
|
||||
789-889.
|
||||
|
||||
::
|
||||
|
||||
mplayer EDL file, version 2
|
||||
< f filename
|
||||
f 60-120
|
||||
f 600-660
|
||||
f 30- 90
|
||||
|
||||
Play first seconds 60-120 from the file, then 600-660, then 30-90.
|
||||
|
||||
::
|
||||
|
||||
mplayer EDL file, version 2
|
||||
< id1 filename1
|
||||
< id2 filename2
|
||||
|
||||
+10 id1 *
|
||||
+10 id2 *
|
||||
+10 id1 *
|
||||
+10 id2 *
|
||||
+10 id1 *
|
||||
+10 id2 *
|
||||
|
||||
This plays time 0-10 from filename1, then 0-10 from filename1, then
|
||||
10-20 from filename1, then 10-20 from filename2, then 20-30 from
|
||||
filename1, then 20-30 from filename2.
|
||||
|
||||
::
|
||||
|
||||
mplayer EDL file, version 2
|
||||
< t1 filename1
|
||||
< t2 filename2
|
||||
|
||||
t1 * +2 # segment 1
|
||||
+2 t2 100 # segment 2
|
||||
t1 * # segment 3
|
||||
t2 *-* # segment 4
|
||||
t1 3 -* # segment 5
|
||||
+0.111111 t2 102.5 # segment 6
|
||||
7.37 t1 5 +1 # segment 7
|
||||
|
||||
This rather pathological example illustrates the rules for filling in
|
||||
implied data. All the values can be determined by recursively applying
|
||||
the rules given above, and the full end result is this::
|
||||
|
||||
+2 0-2 t1 0-2 # segment 1
|
||||
+2 2-4 t2 100-102 # segment 2
|
||||
+0.758889 4-4.758889 t1 2-2.758889 # segment 3
|
||||
+0.5 4.4758889-5.258889 t2 102-102.5 # segment 4
|
||||
+2 5.258889-7.258889 t1 3-5 # segment 5
|
||||
+0.111111 7.258889-7.37 t2 102.5-102.611111 # segment 6
|
||||
+1 7.37-8.37 t1 5-6 # segment 7
|
||||
+8
-12
@@ -25,7 +25,7 @@ from the input video. Note that not all codecs and not all formats support VFR
|
||||
encoding, and some which do have bugs when a target bitrate is specified - use
|
||||
-ofps or -oautofps to force CFR encoding in these cases.
|
||||
|
||||
Of course, the options can be stored in a profile, like this .config/mpv/mpv.conf
|
||||
Of course, the options can be stored in a profile, like this .mpv/config
|
||||
section::
|
||||
|
||||
[myencprofile]
|
||||
@@ -38,17 +38,12 @@ section::
|
||||
oac = aac
|
||||
oacopts-add = b=96k
|
||||
|
||||
It's also possible to define default encoding options by putting them into
|
||||
the section named ``[encoding]``. (This behavior changed after mpv 0.3.x. In
|
||||
mpv 0.3.x, config options in the default section / no section were applied
|
||||
to encoding. This is not the case anymore.)
|
||||
|
||||
One can then encode using this profile using the command::
|
||||
|
||||
mpv infile -o outfile.mp4 -profile myencprofile
|
||||
|
||||
Some example profiles are provided in a file
|
||||
etc/encoding-profiles.conf; as for this, see below.
|
||||
etc/encoding-example-profiles.conf; as for this, see below.
|
||||
|
||||
|
||||
Encoding examples
|
||||
@@ -94,12 +89,13 @@ Device targets
|
||||
As the options for various devices can get complex, profiles can be used.
|
||||
|
||||
An example profile file for encoding is provided in
|
||||
etc/encoding-profiles.conf in the source tree. This file is installed and loaded
|
||||
by default (if libavfilter is enabled at compilation). If you want to modify
|
||||
it, you can replace and it with your own copy by doing::
|
||||
etc/encoding-example-profiles.conf in the source tree. You can include it into
|
||||
your configuration by doing::
|
||||
|
||||
mkdir -p ~/.mpv
|
||||
cp /etc/mpv/encoding-profiles.conf ~/.mpv/encoding-profiles.conf
|
||||
curl https://raw.github.com/mpv-player/mpv/master/etc/encoding-example-profiles.conf \
|
||||
> ~/.mpv/encoding-profiles.conf
|
||||
echo "include = $HOME/.mpv/encoding-profiles.conf" >> ~/.mpv/config
|
||||
|
||||
Refer to the top of that file for more comments - in a nutshell, the following
|
||||
options are added by it::
|
||||
@@ -139,9 +135,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")
|
||||
|
||||
@@ -1,438 +0,0 @@
|
||||
CHANGES FROM OTHER VERSIONS OF MPLAYER
|
||||
======================================
|
||||
|
||||
**mpv** is based on mplayer2, which in turn is based on the original MPlayer
|
||||
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
|
||||
large part of which is incompatible or completely changes how the player
|
||||
behaves. Although there are still many similarities to its ancestors, **mpv**
|
||||
should generally be treated as a completely different program.
|
||||
|
||||
.. note::
|
||||
These lists are incomplete.
|
||||
|
||||
General Changes from MPlayer to mpv
|
||||
-----------------------------------
|
||||
|
||||
This listing is about changes introduced by mplayer2 and mpv relatively to
|
||||
MPlayer.
|
||||
|
||||
Player
|
||||
~~~~~~
|
||||
|
||||
* New name for the binary (``mpv``). New location for config files (either
|
||||
``~/.config/mpv/mpv.conf``, or if you want, ``~/.mpv/config``).
|
||||
* Encoding functionality (replacement for MEncoder, see the `ENCODING`_ section).
|
||||
* Support for Lua scripting (see the `LUA SCRIPTING`_ section).
|
||||
* Better pause handling (e.g. do not unpause on a command).
|
||||
* Precise seeking support.
|
||||
* Improvements in audio/video sync handling.
|
||||
* Do not lose settings when playing a new file in the same player instance.
|
||||
* Slave mode compatibility broken (see below).
|
||||
* Re-enable screensaver while the player is paused.
|
||||
* Allow resuming playback at a later point with ``Shift+q``, also see the
|
||||
``quit_watch_later`` input command.
|
||||
* ``--keep-open`` option to stop the player from closing the window and
|
||||
exiting after playback ends.
|
||||
* A client API, that allows embedding **mpv** into applications
|
||||
(see ``libmpv/client.h`` in the sources).
|
||||
|
||||
Input
|
||||
~~~~~
|
||||
|
||||
* Improved default keybindings. MPlayer bindings are also available (see
|
||||
``etc/mplayer-input.conf`` in the source tree).
|
||||
* Improved responsiveness on user input.
|
||||
* Support for modifier keys (alt, shift, ctrl) in input.conf.
|
||||
* Allow customizing whether a key binding for seeking shows the video time, the
|
||||
OSD bar, or nothing (see the `Input Command Prefixes`_ section).
|
||||
* Support mapping multiple commands to one key.
|
||||
|
||||
Audio
|
||||
~~~~~
|
||||
|
||||
* Support for gapless audio (see the ``--gapless-audio`` option).
|
||||
* Support for OSS4 volume control.
|
||||
* Improved support for PulseAudio.
|
||||
* Make ``--softvol`` default (**mpv** is not a mixer control panel).
|
||||
* By default, do pitch correction if playback speed is increased.
|
||||
* Improved downmixing and output of surround audio:
|
||||
|
||||
- Instead of using hardcoded pan filters to do remixing, libavresample is used
|
||||
- Channel maps are used to identify the channel layout, so e.g. ``3.0`` and
|
||||
``2.1`` audio can be distinguished.
|
||||
|
||||
Video
|
||||
~~~~~
|
||||
|
||||
* Wayland support.
|
||||
* Native support for VAAPI and VDA. Improved VDPAU video output.
|
||||
* Improved OpenGL output (see the ``opengl-hq`` video output).
|
||||
* Make hardware decoding work with the ``opengl`` video output.
|
||||
* Support for libavfilter (for video->video and audio->audio). This allows
|
||||
using most of FFmpeg's filters, which improve greatly on the old MPlayer
|
||||
filters in features, performance, and correctness.
|
||||
* More correct color reproduction (color matrix generation).
|
||||
* Better subtitle rendering using libass by default.
|
||||
* Improvements when playing multiple files (``--fixed-vo`` is default, do not
|
||||
reset settings by default when playing a new file).
|
||||
* Replace image video outputs (``--vo=jpeg`` etc.) with ``--vo=image``.
|
||||
* Removal of ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding
|
||||
can handle these use cases. For yuv4mpeg, for example, use::
|
||||
|
||||
mpv input.mkv -o output.y4m --no-audio --oautofps --oneverdrop
|
||||
|
||||
* Image subtitles (DVDs etc.) are rendered in color and use more correct
|
||||
positioning (color for image subs can be disabled with ``--sub-gray``).
|
||||
|
||||
OSD and terminal
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* Cleaned up terminal output: nicer status line, less useless noise.
|
||||
* Improved OSD rendering using libass, with full Unicode support.
|
||||
* New OSD bar with chapter marks. Not positioned in the middle of the video
|
||||
(this can be customized with the ``--osd-bar-align-y`` option).
|
||||
* Display list of chapters and audio/subtitle tracks on OSD (see the
|
||||
`Properties`_ section).
|
||||
|
||||
Screenshots
|
||||
~~~~~~~~~~~
|
||||
|
||||
* Instant screenshots without 1-frame delay.
|
||||
* Support for taking screenshots even with hardware decoding.
|
||||
* Support for saving screenshots as JPEG or PNG.
|
||||
* Support for configurable file names.
|
||||
* Support for taking screenshots with or without subtitles.
|
||||
|
||||
Note that the ``screenshot`` video filter is not needed anymore, and should not
|
||||
be put into the mpv config file.
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* Better MKV support (e.g. ordered chapters, 3D metadata).
|
||||
* Matroska edition switching at runtime.
|
||||
* Support for playing URLs of popular streaming sites directly.
|
||||
(e.g. ``mpv https://www.youtube.com/watch?v=...``).
|
||||
Requires a recent version of ``youtube-dl`` to be installed. Can be
|
||||
disabled with ``ytdl=no`` in the mpv config file.
|
||||
* Support for precise scrolling which scales the parameter of commands. If the
|
||||
input doesn't support precise scrolling the scale factor stays 1.
|
||||
* Allow changing/adjusting video filters at runtime. (This is also used to make
|
||||
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise).
|
||||
* Improved support for .cue files.
|
||||
|
||||
Mac OS X
|
||||
~~~~~~~~
|
||||
|
||||
* Native OpenGL backend.
|
||||
* Cocoa event loop is independent from MPlayer's event loop, so user
|
||||
actions like accessing menus and live resizing do not block the playback.
|
||||
* Apple Remote support.
|
||||
* Media Keys support.
|
||||
* VDA support using libavcodec hwaccel API instead of FFmpeg's decoder with up
|
||||
to 2-2.5x reduction in CPU usage.
|
||||
|
||||
Windows
|
||||
~~~~~~~
|
||||
|
||||
* Improved support for Unicode file names.
|
||||
* Improved window handling.
|
||||
* Do not block playback when moving the window.
|
||||
* Improved Direct3D video output.
|
||||
* Added WASAPI audio output.
|
||||
|
||||
Internal changes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* Switch to GPLv2+ (see ``Copyright`` file for details).
|
||||
* Removal of lots of cruft:
|
||||
|
||||
- Internal GUI (replaced by the OSC, see the `ON SCREEN CONTROLLER`_ section).
|
||||
- MEncoder (replaced by native encoding, see the `ENCODING`_ section).
|
||||
- OSD menu.
|
||||
- Kernel video drivers for Linux 2.4 (including VIDIX).
|
||||
- Teletext support.
|
||||
- Support for dead platforms.
|
||||
- Most built-in demuxers have been replaced by their libavformat counterparts.
|
||||
- Built-in network support has been replaced by libavformat's (which also
|
||||
supports https URLs).
|
||||
- Embedded copies of libraries (such as FFmpeg).
|
||||
|
||||
* General code cleanups (including refactoring or rewrites of many parts).
|
||||
* New build system.
|
||||
* Many bug fixes and removal of long-standing issues.
|
||||
* Generally preferring FFmpeg/Libav over internal demuxers, decoders, and
|
||||
filters.
|
||||
|
||||
Detailed Listing of User-visible Changes
|
||||
----------------------------------------
|
||||
|
||||
This listing is about changed command line switches, slave commands, and similar
|
||||
things. Completely removed features are not listed.
|
||||
|
||||
Command Line Switches
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* There is a new command line syntax, which is generally preferred over the old
|
||||
syntax. ``-optname optvalue`` becomes ``--optname=optvalue``.
|
||||
|
||||
The old syntax will not be removed. However, the new syntax is mentioned in
|
||||
all documentation and so on, and unlike the old syntax is not ambiguous,
|
||||
so it is a good thing to know about this change.
|
||||
* In general, negating switches like ``-noopt`` now have to be written as
|
||||
``-no-opt`` or ``--no-opt``.
|
||||
* Per-file options are not the default anymore. You can explicitly specify
|
||||
file-local options. See ``Usage`` section.
|
||||
* Many options have been renamed, removed or changed semantics. Some options
|
||||
that are required for a good playback experience with MPlayer are now
|
||||
superfluous or even worse than the defaults, so make sure to read the manual
|
||||
before trying to use your existing configuration with **mpv**.
|
||||
* Table of renamed/replaced switches:
|
||||
|
||||
=========================== ========================================
|
||||
Old New
|
||||
=========================== ========================================
|
||||
``-no<opt>`` ``--no-<opt>`` (add a dash)
|
||||
``-a52drc level`` ``--ad-lavc-ac3drc=level``
|
||||
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
|
||||
``-af volnorm`` ``--af=drc`` (renamed)
|
||||
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
|
||||
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
|
||||
``-aspect`` ``--video-aspect``
|
||||
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
|
||||
``-ass`` ``--sub-ass``
|
||||
``-audiofile-cache`` (removed; the main cache settings are used)
|
||||
``-audiofile`` ``--audio-file``
|
||||
``-benchmark`` ``--untimed`` (no stats)
|
||||
``-capture`` ``--stream-capture=<filename>``
|
||||
``-channels`` ``--audio-channels`` (changed semantics)
|
||||
``-cursor-autohide-delay`` ``--cursor-autohide``
|
||||
``-delay`` ``--audio-delay``
|
||||
``-dumpstream`` ``--stream-dump=<filename>``
|
||||
``-dvdangle`` ``--dvd-angle``
|
||||
``-endpos`` ``--length``
|
||||
``-font`` ``--osd-font``
|
||||
``-forcedsubsonly`` ``--sub-forced-only``
|
||||
``-forceidx`` ``--index``
|
||||
``-format`` ``--audio-format``
|
||||
``-fsmode-dontuse`` (removed)
|
||||
``-fstype`` ``--x11-netwm`` (changed semantics)
|
||||
``-hardframedrop`` ``--framedrop=hard``
|
||||
``-identify`` (removed; use TOOLS/mpv_identify.sh)
|
||||
``-idx`` ``--index``
|
||||
``-lavdopts ...`` ``--vd-lavc-...``
|
||||
``-lavfdopts`` ``--demuxer-lavf-...``
|
||||
``-lircconf`` ``--input-lirc-conf``
|
||||
``-loop 0`` ``--loop=inf``
|
||||
``-mixer-channel`` AO suboptions (``alsa``, ``oss``)
|
||||
``-mixer`` AO suboptions (``alsa``, ``oss``)
|
||||
``-mouse-movements`` ``--input-cursor``
|
||||
``-msgcolor`` ``--msg-color``
|
||||
``-msglevel`` ``--msg-level`` (changed semantics)
|
||||
``-msgmodule`` ``--msg-module``
|
||||
``-name`` ``--x11-name``
|
||||
``-noar`` ``--no-input-appleremote``
|
||||
``-noautosub`` ``--no-sub-auto``
|
||||
``-noconsolecontrols`` ``--no-input-terminal``
|
||||
``-nojoystick`` ``--no-input-joystick``
|
||||
``-nosound`` ``--no-audio``
|
||||
``-osdlevel`` ``--osd-level``
|
||||
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
|
||||
``-playing-msg`` ``--term-playing-msg``
|
||||
``-pp ...`` ``'--vf=pp=[...]'``
|
||||
``-pphelp`` ``--vf=pp:help``
|
||||
``-rawaudio ...`` ``--demuxer-rawaudio-...``
|
||||
``-rawvideo ...`` ``--demuxer-rawvideo-...``
|
||||
``-spugauss`` ``--sub-gauss``
|
||||
``-srate`` ``--audio-samplerate``
|
||||
``-ss`` ``--start``
|
||||
``-ssf <sub>`` ``--sws-...``
|
||||
``-stop-xscreensaver`` ``--stop-screensaver``
|
||||
``-sub-fuzziness`` ``--sub-auto``
|
||||
``-sub`` ``--sub-file``
|
||||
``-subcp`` ``--sub-codepage``
|
||||
``-subdelay`` ``--sub-delay``
|
||||
``-subfile`` ``--sub-file``
|
||||
``-subfont-*`` ``--sub-text-*``, ``--osd-*``
|
||||
``-subfont-text-scale`` ``--sub-scale``
|
||||
``-subfont`` ``--sub-text-font``
|
||||
``-subfps`` ``--sub-fps``
|
||||
``-subpos`` ``--sub-pos``
|
||||
``-sws`` ``--sws-scaler``
|
||||
``-tvscan`` ``--tv-scan``
|
||||
``-use-filename-title`` ``--title='${filename}'``
|
||||
``-vc ffh264vdpau`` (etc.) ``--hwdec=vdpau``
|
||||
``-vobsub`` ``--sub-file`` (pass the .idx file)
|
||||
``-x W``, ``-y H`` ``--geometry=WxH`` + ``--no-keepaspect``
|
||||
``-xineramascreen`` ``--screen`` (different values)
|
||||
``-xy W`` ``--autofit=W``
|
||||
``-zoom`` Inverse available as ``--video-unscaled``
|
||||
``dvdnav://`` ``dvdnav://menu``
|
||||
``dvd://1`` ``dvd://0`` (0-based offset)
|
||||
=========================== ========================================
|
||||
|
||||
.. note::
|
||||
|
||||
``-opt val`` becomes ``--opt=val``.
|
||||
|
||||
.. note::
|
||||
|
||||
Quite some video filters, video outputs, audio filters, audio outputs, had
|
||||
changes in their option parsing. These aren't mentioned in the table above.
|
||||
|
||||
Also, some video and audio filters have been removed, and you have to use
|
||||
libavfilter (using ``--vf=lavfi=[...]`` or ``--af=lavfi=[...]``) to get
|
||||
them back.
|
||||
|
||||
input.conf and Slave Commands
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Table of renamed input commands:
|
||||
|
||||
This lists only commands that are not always gracefully handled by the
|
||||
internal legacy translation layer. If an input.conf contains any legacy
|
||||
commands, a warning will be printed when starting the player. The warnings
|
||||
also show the replacement commands.
|
||||
|
||||
Properties containing ``_`` to separate words use ``-`` instead.
|
||||
|
||||
+--------------------------------+----------------------------------------+
|
||||
| Old | New |
|
||||
+================================+========================================+
|
||||
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
|
||||
| | (translation layer cannot deal with |
|
||||
| | whitespace) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
|
||||
| | |
|
||||
| | ``set video-aspect 0`` (reset aspect) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
|
||||
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
|
||||
| | ``<dir>`` parameter unsupported. Use |
|
||||
| | a negative ``<step>`` instead. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``step_property <prop>`` | Prefix ``cycle`` or ``add`` with |
|
||||
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
|
||||
| | ``<step>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``osd_show_property_text`` | ``show_text <text>`` |
|
||||
| ``<text>`` | The property expansion format string |
|
||||
| | syntax slightly changed. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``osd_show_text`` | Now does the same as |
|
||||
| | ``osd_show_property_text``. Use the |
|
||||
| | ``raw`` prefix to disable property |
|
||||
| | expansion. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_tracks`` | ``show_text ${track-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_chapters`` | ``show_text ${chapter-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_start_scan`` | ``set tv-scan yes`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_set_channel <val>`` | ``set tv-channel <val>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_step_channel`` | ``cycle tv-channel`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``dvb_set_channel <v1> <v2>`` | ``set dvb-channel <v1>-<v2>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``dvb_step_channel`` | ``cycle dvb-channel`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_set_freq <val>`` | ``set tv-freq <val>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_step_freq`` | ``cycle tv-freq`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_set_norm <norm>`` | ``set tv-norm <norm>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``tv_step_norm`` | ``cycle tv-norm`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
|
||||
.. note::
|
||||
|
||||
Due to lack of hardware and users using the TV/DVB/PVR features, and
|
||||
due to the need to cleanup the related command code, it's possible
|
||||
that the new commands are buggy or behave worse. This can be improved
|
||||
if testers are available. Otherwise, some of the TV code will be
|
||||
removed at some point.
|
||||
|
||||
Slave mode
|
||||
~~~~~~~~~~
|
||||
|
||||
* Slave mode was removed. A proper slave mode application needed tons of code
|
||||
and hacks to get
|
||||
it right. The main problem is that slave mode is a bad and incomplete
|
||||
interface, and to get around that, applications parsed output messages
|
||||
intended for users. It is hard to know which messages exactly are parsed by
|
||||
slave mode applications. This makes it virtually impossible to improve
|
||||
terminal output intended for users without possibly breaking something.
|
||||
|
||||
This is absolutely insane, and since initial improvements to **mpv** quickly
|
||||
made slave mode incompatible to most applications, it was removed as useless
|
||||
cruft. The client API (see below) is provided instead.
|
||||
|
||||
``--identify`` was replaced by the ``TOOLS/mpv_identify.sh`` wrapper script.
|
||||
|
||||
* For some time (until including release 0.4.x), mpv supported a
|
||||
``--slave-broken`` option. The following options are equivalent:
|
||||
|
||||
::
|
||||
|
||||
--input-file=/dev/stdin --input-terminal=no
|
||||
|
||||
|
||||
Assuming the system supports ``/dev/stdin``.
|
||||
|
||||
(The option was readded in 0.5.1 and sets exactly these options.)
|
||||
|
||||
* A JSON RPC protocol giving access to the client API is also supported. See
|
||||
`JSON IPC`_ for more information.
|
||||
|
||||
* **mpv** also provides a client API, which can be used to embed the player
|
||||
by loading it as shared library. (See ``libmpv/client.h`` in the sources.)
|
||||
It might also be possible to implement a custom slave mode-like protocol
|
||||
using Lua scripting.
|
||||
|
||||
Policy for Removed Features
|
||||
---------------------------
|
||||
|
||||
**mpv** is in active development. If something is in the way of more important
|
||||
development (such as fixing bugs or implementing new features), we sometimes
|
||||
remove features. Usually this happens only with old features that either seem
|
||||
to be useless, or are not used by anyone. Often these are obscure, or
|
||||
"inherited", or were marked experimental, but never received any particular
|
||||
praise by any users.
|
||||
|
||||
Sometimes, features are replaced by something new. The new code will be either
|
||||
simpler or more powerful, but doesn't necessarily provide everything the old
|
||||
feature did.
|
||||
|
||||
We can not exclude that we accidentally remove features that are actually
|
||||
popular. Generally, we do not know how much a specific functionality is used.
|
||||
If you miss a feature and think it should be re-added, please open an issue
|
||||
on the mpv bug tracker. Hopefully, a solution can be found. Often, it turns
|
||||
out that re-adding something is not much of a problem, or that there are
|
||||
better alternatives.
|
||||
|
||||
Why this Fork?
|
||||
--------------
|
||||
|
||||
mplayer2 is practically dead, and mpv started out as a branch containing
|
||||
new/experimental development. (Some of it was merged right *after* the fork
|
||||
was made public, seemingly as an acknowledgment that development, or at
|
||||
least merging, should have been more active.)
|
||||
|
||||
MPlayer is focused on not breaking anything, but is stuck with a horrible
|
||||
codebase resistant to cleanup. (Unless you do what mpv did - merciless and
|
||||
consequent pruning of bad, old code.) Cleanup and keeping broken things
|
||||
conflict, so the kind of development mpv strives for can't be done within
|
||||
MPlayer due to clashing development policies.
|
||||
|
||||
Additionally, mplayer2 already had lots of changes over MPlayer, which would
|
||||
have needed to be backported to the MPlayer codebase. This would not only
|
||||
have been hard (several years of diverging development), but also would have
|
||||
been impossible due to the aforementioned MPlayer development policy.
|
||||
@@ -0,0 +1,20 @@
|
||||
[latex2e writer]
|
||||
use-latex-docinfo: yes
|
||||
use-latex-toc: yes
|
||||
hyperlink-color: Blue!30!RoyalPurple!70!Black
|
||||
latex-preamble: \usepackage[usenames,dvipsnames]{xcolor}
|
||||
\usepackage{fullpage}
|
||||
\usepackage{graphicx}
|
||||
\renewcommand{\familydefault}{\sfdefault}
|
||||
|
||||
\makeatletter
|
||||
\renewcommand{\maketitle}{
|
||||
\begin{center}
|
||||
\includegraphics{etc/mpv-icon-8bit-64x64.png}\\
|
||||
\vspace*{-1.5em}
|
||||
\begin{Huge}
|
||||
\@title
|
||||
\end{Huge}
|
||||
\end{center}
|
||||
}
|
||||
\makeatother
|
||||
@@ -11,12 +11,6 @@ syntax is:
|
||||
|
||||
To get a full list of available audio filters, see ``--af=help``.
|
||||
|
||||
You can also set defaults for each filter. The defaults are applied before the
|
||||
normal filter parameters.
|
||||
|
||||
``--af-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
|
||||
Set defaults for each filter.
|
||||
|
||||
Audio filters are managed in lists. There are a few commands to manage the
|
||||
filter list:
|
||||
|
||||
@@ -41,7 +35,7 @@ Available filters are:
|
||||
This filter is automatically enabled if the audio output does not support
|
||||
the audio configuration of the file being played.
|
||||
|
||||
It supports only the following sample formats: u8, s16, s32, float.
|
||||
It supports only the following sample formats: u8, s16ne, s32ne, floatne.
|
||||
|
||||
``filter-size=<length>``
|
||||
Length of the filter with respect to the lower sampling rate. (default:
|
||||
@@ -56,9 +50,9 @@ Available filters are:
|
||||
entries. (default: no)
|
||||
``no-detach``
|
||||
Do not detach if input and output audio format/rate/channels match.
|
||||
(If you just want to set defaults for this filter that will be used
|
||||
even by automatically inserted lavrresample instances, you should
|
||||
prefer setting them with ``--af-defaults=lavrresample:...``.)
|
||||
You should add this option if you specify additional parameters, as
|
||||
automatically inserted lavrresample instances will use the default
|
||||
settings.
|
||||
``o=<string>``
|
||||
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
|
||||
be documented by FFmpeg or Libav.
|
||||
@@ -70,20 +64,17 @@ Available filters are:
|
||||
outputting to S/PDIF. If the input sample rate is not 48 kHz, 44.1 kHz or
|
||||
32 kHz, it will be resampled to 48 kHz.
|
||||
|
||||
``tospdif=<yes|no>``
|
||||
Output raw AC-3 stream if ``no``, output to S/PDIF for
|
||||
pass-through if ``yes`` (default).
|
||||
|
||||
``bitrate=<rate>``
|
||||
The bitrate use for the AC-3 stream. Set it to 384 to get 384 kbps.
|
||||
|
||||
The default is 640. Some receivers might not be able to handle this.
|
||||
``<tospdif>``
|
||||
Output raw AC-3 stream if zero or not set, output to S/PDIF for
|
||||
passthrough when ``<tospdif>`` is set non-zero.
|
||||
``<bitrate>``
|
||||
The bitrate use for the AC-3 stream. Set it to either 384 or 384000
|
||||
to get 384 kbps.
|
||||
|
||||
Valid values: 32, 40, 48, 56, 64, 80, 96, 112, 128,
|
||||
160, 192, 224, 256, 320, 384, 448, 512, 576, 640.
|
||||
|
||||
The special value ``auto`` selects a default bitrate based on the
|
||||
input channel number:
|
||||
Default bitrate is based on the input channel number:
|
||||
|
||||
:1ch: 96
|
||||
:2ch: 192
|
||||
@@ -92,9 +83,9 @@ Available filters are:
|
||||
:5ch: 448
|
||||
:6ch: 448
|
||||
|
||||
``minchn=<n>``
|
||||
``<minchn>``
|
||||
If the input channel number is less than ``<minchn>``, the filter will
|
||||
detach itself (default: 3).
|
||||
detach itself (default: 5).
|
||||
|
||||
``sweep[=speed]``
|
||||
Produces a sine sweep.
|
||||
@@ -103,7 +94,7 @@ Available filters are:
|
||||
Sine function delta, use very low values to hear the sweep.
|
||||
|
||||
``sinesuppress[=freq:decay]``
|
||||
Remove a sine at the specified frequency. Useful to get rid of the 50/60 Hz
|
||||
Remove a sine at the specified frequency. Useful to get rid of the 50/60Hz
|
||||
noise on low quality audio equipment. It only works on mono input.
|
||||
|
||||
``<freq>``
|
||||
@@ -149,7 +140,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:
|
||||
@@ -184,9 +175,9 @@ Available filters are:
|
||||
|
||||
``mpv --af=equalizer=11:11:10:5:0:-12:0:5:12:12 media.avi``
|
||||
Would amplify the sound in the upper and lower frequency region
|
||||
while canceling it almost completely around 1 kHz.
|
||||
while canceling it almost completely around 1kHz.
|
||||
|
||||
``channels=nch[:routes]``
|
||||
``channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]``
|
||||
Can be used for adding, removing, routing and copying audio channels. If
|
||||
only ``<nch>`` is given, the default routing is used. It works as follows:
|
||||
If the number of output channels is greater than the number of input
|
||||
@@ -197,53 +188,46 @@ Available filters are:
|
||||
|
||||
``<nch>``
|
||||
number of output channels (1-8)
|
||||
``<routes>``
|
||||
List of ``,`` separated routes, in the form ``from1-to1,from2-to2,...``.
|
||||
Each pair defines where to route each channel. There can be at most
|
||||
8 routes. Without this argument, the default routing is used. Since
|
||||
``,`` is also used to separate filters, you must quote this argument
|
||||
with ``[...]`` or similar.
|
||||
``<nr>``
|
||||
number of routes (1-8)
|
||||
``<from1:to1:from2:to2:from3:to3:...>``
|
||||
Pairs of numbers between 0 and 7 that define where to route each
|
||||
channel.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=channels=4:[0-1,1-0,0-2,1-3] media.avi``
|
||||
``mpv --af=channels=4:4:0:1:1:0:2:2:3:3 media.avi``
|
||||
Would change the number of channels to 4 and set up 4 routes that
|
||||
swap channel 0 and channel 1 and leave channel 2 and 3 intact.
|
||||
Observe that if media containing two channels were played back,
|
||||
channels 2 and 3 would contain silence but 0 and 1 would still be
|
||||
swapped.
|
||||
|
||||
``mpv --af=channels=6:[0-0,0-1,0-2,0-3] media.avi``
|
||||
``mpv --af=channels=6:4:0:0:0:1:0:2:0:3 media.avi``
|
||||
Would change the number of channels to 6 and set up 4 routes that
|
||||
copy channel 0 to channels 0 to 3. Channel 4 and 5 will contain
|
||||
silence.
|
||||
|
||||
.. note::
|
||||
|
||||
You should probably not use this filter. If you want to change the
|
||||
output channel layout, try the ``format`` filter, which can make mpv
|
||||
automatically up- and downmix standard channel layouts.
|
||||
|
||||
``format=format:srate:channels:out-format:out-srate:out-channels``
|
||||
``force=in-format:in-srate:in-channels:out-format:out-srate:out-channels``
|
||||
Force a specific audio format/configuration without actually changing the
|
||||
audio data. Keep in mind that the filter system might auto-insert actual
|
||||
conversion filters before or after this filter if needed.
|
||||
|
||||
All parameters are optional. The first 3 parameters restrict what the filter
|
||||
accepts as input. The ``out-`` parameters change the audio format, without
|
||||
All parameters are optional. The ``in-`` variants restrict what the filter
|
||||
accepts as input. The ``out-`` variants change the audio format, without
|
||||
actually doing a conversion. The data will be 'reinterpreted' by the
|
||||
filters or audio outputs following this filter.
|
||||
|
||||
``<format>``
|
||||
Force conversion to this format. Use ``--af=format=format=help`` to get
|
||||
a list of valid formats.
|
||||
``<in-format>``
|
||||
Force conversion to this format. See ``format`` filter for valid audio
|
||||
format values.
|
||||
|
||||
``<srate>``
|
||||
``<in-srate>``
|
||||
Force conversion to a specific sample rate. The rate is an integer,
|
||||
48000 for example.
|
||||
|
||||
``<channels>``
|
||||
Force mixing to a specific channel layout. See ``--audio-channels`` option
|
||||
``<in-channels>``
|
||||
Force mixing to a specific channel layout. See ``--channels`` option
|
||||
for possible values.
|
||||
|
||||
``<out-format>``
|
||||
@@ -252,70 +236,55 @@ Available filters are:
|
||||
|
||||
``<out-channels>``
|
||||
|
||||
See also ``--audio-format``, ``--audio-samplerate``, and
|
||||
``--audio-channels`` for related options. Keep in mind that
|
||||
``--audio-channels`` does not actually force the number of
|
||||
channels in most cases, while this filter can do this.
|
||||
``format[=format]``
|
||||
Convert between different sample formats. Automatically enabled when
|
||||
needed by the audio output or another filter. See also ``--format``.
|
||||
|
||||
*NOTE*: this filter used to be named ``force``. Also, unlike the old
|
||||
``format`` filter, this does not do any actual conversion anymore.
|
||||
Conversion is done by other, automatically inserted filters.
|
||||
``<format>``
|
||||
Sets the desired format. The general form is 'sbe', where 's' denotes
|
||||
the sign (either 's' for signed or 'u' for unsigned), 'b' denotes the
|
||||
number of bits per sample (16, 24 or 32) and 'e' denotes the
|
||||
endianness ('le' means little-endian, 'be' big-endian and 'ne' the
|
||||
endianness of the computer mpv is running on). Valid values (amongst
|
||||
others) are: 's16le', 'u32be' and 'u24ne'. Exceptions to this rule that
|
||||
are also valid format specifiers: u8, s8, floatle, floatbe, floatne,
|
||||
mpeg2, and ac3.
|
||||
|
||||
``convert24``
|
||||
Filter for internal use only. Converts between 24-bit and 32-bit sample
|
||||
formats.
|
||||
|
||||
``convertsign``
|
||||
Filter for internal use only. Converts between signed/unsigned formats.
|
||||
|
||||
``volume[=<volumedb>[:...]]``
|
||||
``volume[=v[:sc[:fast]]]``
|
||||
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
|
||||
knob on your amplifier.
|
||||
|
||||
This filter has a second feature: It measures the overall maximum sound
|
||||
level and prints out that level when mpv exits. This feature currently
|
||||
only works with floating-point data.
|
||||
|
||||
*NOTE*: This filter is not reentrant and can therefore only be enabled
|
||||
once for every audio stream.
|
||||
|
||||
``<volumedb>``
|
||||
Sets the desired gain in dB for all channels in the stream from -200 dB
|
||||
to +60 dB, where -200 dB mutes the sound completely and +60 dB equals a
|
||||
``<v>``
|
||||
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).
|
||||
``replaygain-track``
|
||||
Adjust volume gain according to the track-gain replaygain value stored
|
||||
in the file metadata.
|
||||
``replaygain-album``
|
||||
Like replaygain-track, but using the album-gain value instead.
|
||||
``replaygain-preamp``
|
||||
Pre-amplification gain in dB to apply to the selected replaygain gain
|
||||
(default: 0).
|
||||
``replaygain-clip=yes|no``
|
||||
Prevent clipping caused by replaygain by automatically lowering the
|
||||
gain (default). Use ``replaygain-clip=no`` to disable this.
|
||||
``softclip``
|
||||
Turns soft clipping on. Soft-clipping can make the
|
||||
``<sc>``
|
||||
Turns soft clipping on (1) or off (0). 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``
|
||||
Force S16 sample format if set. Lower quality, but might be faster
|
||||
``<fast>``
|
||||
Force S16 sample format if set to 1. Lower quality, but might be faster
|
||||
in some situations.
|
||||
``detach``
|
||||
Remove the filter if the volume is not changed at audio filter config
|
||||
time. Useful with replaygain: if the current file has no replaygain
|
||||
tags, then the filter will be removed if this option is enabled.
|
||||
(If ``--softvol=yes`` is used and the player volume controls are used
|
||||
during playback, a different volume filter will be inserted.)
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=volume=10.1 media.avi``
|
||||
Would amplify the sound by 10.1 dB and hard-clip if the sound level
|
||||
``mpv --af=volume=10.1:0 media.avi``
|
||||
Would amplify the sound by 10.1dB and hard-clip if the sound level
|
||||
is too high.
|
||||
|
||||
``pan=n:[<matrix>]``
|
||||
``pan=n[:L00:L01:L02:...L10:L11:L12:...Ln0:Ln1:Ln2:...]``
|
||||
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
|
||||
@@ -327,23 +296,19 @@ Available filters are:
|
||||
|
||||
``<n>``
|
||||
Number of output channels (1-8).
|
||||
``<matrix>``
|
||||
A list of values ``[L00,L01,L02,...,L10,L11,L12,...,Ln0,Ln1,Ln2,...]``,
|
||||
where each element ``Lij`` means how much of input channel i is mixed
|
||||
into output channel j (range 0-1). So in principle you first have n
|
||||
numbers saying what to do with the first input channel, then n numbers
|
||||
that act on the second input channel etc. If you do not specify any
|
||||
numbers for some input channels, 0 is assumed.
|
||||
Note that the values are separated by ``,``, which is already used
|
||||
by the option parser to separate filters. This is why you must quote
|
||||
the value list with ``[...]`` or similar.
|
||||
``<Lij>``
|
||||
How much of input channel i is mixed into output channel j (0-1). So
|
||||
in principle you first have n numbers saying what to do with the first
|
||||
input channel, then n numbers that act on the second input channel
|
||||
etc. If you do not specify any numbers for some input channels, 0 is
|
||||
assumed.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=pan=1:[0.5,0.5] media.avi``
|
||||
``mpv --af=pan=1:0.5:0.5 media.avi``
|
||||
Would downmix from stereo to mono.
|
||||
|
||||
``mpv --af=pan=3:[1,0,0.5,0,1,0.5] media.avi``
|
||||
``mpv --af=pan=3:1:0:0.5:0:1:0.5 media.avi``
|
||||
Would give 3 channel output leaving channels 0 and 1 intact, and mix
|
||||
channels 0 and 1 into output channel 2 (which could be sent to a
|
||||
subwoofer for example).
|
||||
@@ -351,8 +316,8 @@ Available filters are:
|
||||
.. note::
|
||||
|
||||
If you just want to force remixing to a certain output channel layout,
|
||||
it is easier to use the ``format`` filter. For example,
|
||||
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
|
||||
it is easier to use the ``force`` filter. For example,
|
||||
``mpv '--af=force=channels=5.1' '--channels=5.1'`` would always force
|
||||
remixing audio to 5.1 and output it like this.
|
||||
|
||||
``sub[=fc:ch]``
|
||||
@@ -369,8 +334,8 @@ Available filters are:
|
||||
to the subwoofer.
|
||||
|
||||
``<fc>``
|
||||
cutoff frequency in Hz for the low-pass filter (20 Hz to 300 Hz)
|
||||
(default: 60 Hz) For the best result try setting the cutoff frequency
|
||||
cutoff frequency in Hz for the low-pass filter (20Hz to 300Hz)
|
||||
(default: 60Hz) For the best result try setting the cutoff frequency
|
||||
as low as possible. This will improve the stereo or surround sound
|
||||
experience.
|
||||
``<ch>``
|
||||
@@ -381,8 +346,8 @@ Available filters are:
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=sub=100:4 --audio-channels=5 media.avi``
|
||||
Would add a subwoofer channel with a cutoff frequency of 100 Hz to
|
||||
``mpv --af=sub=100:4 --channels=5 media.avi``
|
||||
Would add a subwoofer channel with a cutoff frequency of 100Hz to
|
||||
output channel 4.
|
||||
|
||||
``center``
|
||||
@@ -409,16 +374,16 @@ Available filters are:
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=surround=15 --audio-channels=4 media.avi``
|
||||
Would add surround sound decoding with 15 ms delay for the sound to
|
||||
``mpv --af=surround=15 --channels=4 media.avi``
|
||||
Would add surround sound decoding with 15ms delay for the sound to
|
||||
the rear speakers.
|
||||
|
||||
``delay[=[ch1,ch2,...]]``
|
||||
``delay[=ch1:ch2:...]``
|
||||
Delays the sound to the loudspeakers such that the sound from the
|
||||
different channels arrives at the listening position simultaneously. It is
|
||||
only useful if you have more than 2 loudspeakers.
|
||||
|
||||
``[ch1,ch2,...]``
|
||||
``ch1,ch2,...``
|
||||
The delay in ms that should be imposed on each channel (floating point
|
||||
number between 0 and 1000).
|
||||
|
||||
@@ -437,11 +402,11 @@ Available filters are:
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=delay=[10.5,10.5,0,0,7,0] media.avi``
|
||||
Would delay front left and right by 10.5 ms, the two rear channels
|
||||
and the subwoofer by 0 ms and the center channel by 7 ms.
|
||||
``mpv --af=delay=10.5:10.5:0:0:7:0 media.avi``
|
||||
Would delay front left and right by 10.5ms, the two rear channels
|
||||
and the subwoofer by 0ms and the center channel by 7ms.
|
||||
|
||||
``export=mmapped_file:nsamples]``
|
||||
``export[=mmapped_file[:nsamples]]``
|
||||
Exports the incoming signal to other processes using memory mapping
|
||||
(``mmap()``). Memory mapped areas contain a header::
|
||||
|
||||
@@ -453,7 +418,7 @@ Available filters are:
|
||||
The rest is payload (non-interleaved) 16-bit data.
|
||||
|
||||
``<mmapped_file>``
|
||||
File to map data to (required)
|
||||
File to map data to (default: ``~/.mpv/mpv-af_export``).
|
||||
``<nsamples>``
|
||||
number of samples per channel (default: 512).
|
||||
|
||||
@@ -494,7 +459,7 @@ Available filters are:
|
||||
This filter can cause distortion with audio signals that have a very
|
||||
large dynamic range.
|
||||
|
||||
``ladspa=file:label:[<control0>,<control1>,...]``
|
||||
``ladspa=file:label[:controls...]``
|
||||
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
|
||||
filter is reentrant, so multiple LADSPA plugins can be used at once.
|
||||
|
||||
@@ -510,21 +475,12 @@ Available filters are:
|
||||
one filter, but others contain many of them. Entering 'help' here
|
||||
will list all available filters within the specified library, which
|
||||
eliminates the use of 'listplugins' from the LADSPA SDK.
|
||||
``[<control0>,<control1>,...]``
|
||||
Controls are zero or more ``,`` separated floating point values that
|
||||
determine the behavior of the loaded plugin (for example delay,
|
||||
threshold or gain).
|
||||
``<controls>``
|
||||
Controls are zero or more floating point values that determine the
|
||||
behavior of the loaded plugin (for example delay, threshold or gain).
|
||||
In verbose mode (add ``-v`` to the mpv command line), all
|
||||
available controls and their valid ranges are printed. This eliminates
|
||||
the use of 'analyseplugin' from the LADSPA SDK.
|
||||
Note that ``,`` is already used by the option parser to separate
|
||||
filters, so you must quote the list of values with ``[...]`` or
|
||||
similar.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=ladspa='/usr/lib/ladspa/delay.so':delay_5s:[0.5,0.2] media.avi``
|
||||
Does something.
|
||||
|
||||
``karaoke``
|
||||
Simple voice removal filter exploiting the fact that voice is usually
|
||||
@@ -589,9 +545,9 @@ Available filters are:
|
||||
tempo.
|
||||
|
||||
``mpv --af=scaletempo=stride=30:overlap=.50:search=10 media.ogg``
|
||||
Would tweak the quality and performance parameters.
|
||||
Would tweak the quality and performace parameters.
|
||||
|
||||
``mpv --af=format=float,scaletempo media.ogg``
|
||||
``mpv --af=format=floatne,scaletempo media.ogg``
|
||||
Would make scaletempo use float code. Maybe faster on some
|
||||
platforms.
|
||||
|
||||
@@ -601,7 +557,7 @@ Available filters are:
|
||||
1.2x.
|
||||
|
||||
``lavfi=graph``
|
||||
Filter audio using FFmpeg's libavfilter.
|
||||
Filter audio using ffmpeg's libavfilter.
|
||||
|
||||
``<graph>``
|
||||
Libavfilter graph. See ``lavfi`` video filter for details - the graph
|
||||
@@ -10,20 +10,9 @@ syntax is:
|
||||
If the list has a trailing ',', mpv will fall back on drivers not contained
|
||||
in the list. Suboptions are optional and can mostly be omitted.
|
||||
|
||||
You can also set defaults for each driver. The defaults are applied before the
|
||||
normal driver parameters.
|
||||
|
||||
``--ao-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
|
||||
Set defaults for each driver.
|
||||
|
||||
.. note::
|
||||
|
||||
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 OS X systems, use ``--ao=coreaudio``.
|
||||
See ``--ao=help`` for a list of compiled-in audio output drivers.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
@@ -34,16 +23,13 @@ normal driver parameters.
|
||||
Available audio output drivers are:
|
||||
|
||||
``alsa`` (Linux only)
|
||||
ALSA audio output driver
|
||||
ALSA 0.9/1.x audio output driver
|
||||
|
||||
``no-block``
|
||||
Sets noblock-mode.
|
||||
``device=<device>``
|
||||
Sets the device name. For ac3 output via S/PDIF, use an "iec958" or
|
||||
"spdif" device, unless you really know how to set it correctly.
|
||||
``no-block``
|
||||
Sets noblock-mode.
|
||||
``resample=yes``
|
||||
Enable ALSA resampling plugin. (This is disabled by default, because
|
||||
some drivers report incorrect audio delay in some cases.)
|
||||
``mixer-device=<device>``
|
||||
Set the mixer device used with ``--no-softvol`` (default: ``default``).
|
||||
``mixer-name=<name>``
|
||||
@@ -53,10 +39,6 @@ Available audio output drivers are:
|
||||
Set the index of the mixer channel (default: 0). Consider the output of
|
||||
"``amixer scontrols``", then the index is the number that follows the
|
||||
name of the element.
|
||||
``non-interleaved``
|
||||
Allow output of non-interleaved formats (if the audio decoder uses
|
||||
this format). Currently disabled by default, because some popular
|
||||
ALSA plugins are utterly broken with non-interleaved formats.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -90,6 +72,9 @@ Available audio output drivers are:
|
||||
``name=<client>``
|
||||
Client name that is passed to JACK (default: ``mpv``). Useful
|
||||
if you want to have certain connections established automatically.
|
||||
``(no-)estimate``
|
||||
Estimate the audio delay, supposed to make the video playback smoother
|
||||
(default: enabled).
|
||||
``(no-)autostart``
|
||||
Automatically start jackd if necessary (default: disabled). Note that
|
||||
this tends to be unreliable and will flood stdout with server messages.
|
||||
@@ -114,24 +99,16 @@ Available audio output drivers are:
|
||||
with fixed setups.
|
||||
|
||||
``coreaudio`` (Mac OS X only)
|
||||
Native Mac OS X audio output driver using AudioUnits and the CoreAudio
|
||||
sound server.
|
||||
Native Mac OS X audio output driver
|
||||
|
||||
Automatically redirects to ``coreaudio_exclusive`` when playing compressed
|
||||
formats.
|
||||
|
||||
``coreaudio_exclusive`` (Mac OS X only)
|
||||
Native Mac OS X audio output driver using direct device access and
|
||||
exclusive mode (bypasses the sound server).
|
||||
|
||||
Supports only compressed formats (AC3 and DTS).
|
||||
``device_id=<id>``
|
||||
ID of output device to use (0 = default device)
|
||||
``help``
|
||||
List all available output devices with their IDs.
|
||||
|
||||
``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
|
||||
|
||||
@@ -140,26 +117,10 @@ Available audio output drivers are:
|
||||
string uses a local connection, "localhost" uses network transfer
|
||||
(most likely not what you want).
|
||||
|
||||
``buffer=<1-2000|native>``
|
||||
Set the audio buffer size in milliseconds. A higher value buffers
|
||||
more data, and has a lower probability of buffer underruns. A smaller
|
||||
value makes the audio stream react faster, e.g. to playback speed
|
||||
changes. Default: 250.
|
||||
|
||||
``latency-hacks=<yes|no>``
|
||||
Enable hacks to workaround PulseAudio timing bugs (default: yes). If
|
||||
enabled, mpv will do elaborate latency calculations on its own. If
|
||||
disabled, it will use PulseAudio automatically updated timing
|
||||
information. Disabling this might help with e.g. networked audio.
|
||||
|
||||
``portaudio``
|
||||
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,
|
||||
@@ -168,24 +129,15 @@ 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.
|
||||
|
||||
``buffersize=<ms>``
|
||||
DirectSound buffer size in milliseconds (default: 200).
|
||||
|
||||
``sdl``
|
||||
SDL 1.2+ audio output driver. Should work on any platform supported by SDL
|
||||
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
|
||||
@@ -198,30 +150,7 @@ Available audio output drivers are:
|
||||
|
||||
``null``
|
||||
Produces no audio output but maintains video playback speed. Use
|
||||
``--ao=null:untimed`` for benchmarking.
|
||||
|
||||
``untimed``
|
||||
Do not simulate timing of a perfect audio device. This means audio
|
||||
decoding will go as fast as possible, instead of timing it to the
|
||||
system clock.
|
||||
|
||||
``buffer``
|
||||
Simulated buffer length in seconds.
|
||||
|
||||
``outburst``
|
||||
Simulated chunk size in samples.
|
||||
|
||||
``speed``
|
||||
Simulated audio playback speed as a multiplier. Usually, a real audio
|
||||
device will not go exactly as fast as the system clock. It will deviate
|
||||
just a little, and this option helps simulating this.
|
||||
|
||||
``latency``
|
||||
Simulated device latency. This is additional to EOF.
|
||||
|
||||
``broken-eof``
|
||||
Simulate broken audio drivers, which always add the fixed device
|
||||
latency to the reported audio playback position.
|
||||
``--no-audio`` for benchmarking.
|
||||
|
||||
``pcm``
|
||||
Raw PCM/WAVE file writer audio output
|
||||
@@ -237,10 +166,6 @@ 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
|
||||
@@ -252,8 +177,6 @@ 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.)
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
CHANGES FROM OTHER VERSIONS OF MPLAYER
|
||||
======================================
|
||||
|
||||
**mpv** is based on mplayer2, which in turn is based on the original
|
||||
MPlayer (also called mplayer, mplayer-svn, mplayer1). Many changes
|
||||
have been made. Some changes are incompatible, or completely change how the
|
||||
player behaves.
|
||||
|
||||
General Changes for MPlayer to mplayer2
|
||||
---------------------------------------
|
||||
|
||||
* Removal of the internal GUI, MEncoder, OSD menu
|
||||
* Better pause handling (do not unpause on a command)
|
||||
* Better MKV support (such as ordered chapters)
|
||||
* vo_vdpau improvements
|
||||
* Precise seeking support
|
||||
* No embedded copy of ffmpeg and other libraries
|
||||
* Native OpenGL backend for OSX
|
||||
* General OSX improvements
|
||||
* Improvements in audio/video sync handling
|
||||
* Cleaned up terminal output
|
||||
* Gapless audio support (``--gapless-audio``)
|
||||
* Improved responsiveness on user input
|
||||
* Support for modifier keys (alt, shift, ctrl) in input.conf
|
||||
* OSS4 volume control
|
||||
* More correct color reproduction (color matrix generation)
|
||||
* Use libass for subtitle rendering by default (better quality)
|
||||
* Generally preferring ffmpeg/libav over internal demuxers and decoders
|
||||
* Improvements when playing multiple files (``--fixed-vo``)
|
||||
* Screenshot improvements (instant screenshots without 1-frame delay, allow
|
||||
taking screenshots even with hardware decoding)
|
||||
* Improved support for PulseAudio
|
||||
* General code cleanups
|
||||
* Many more changes
|
||||
|
||||
General Changes for mplayer2 to mpv
|
||||
-----------------------------------
|
||||
|
||||
* Switch back to GPLv2+.
|
||||
* Removal of lots of unneeded code to encourage developer activity (less
|
||||
obscure scary zombie code that kills any desire for hacking the codebase)
|
||||
* Removal of dust and dead bodies (code-wise), such as kernel drivers for
|
||||
decades old hardware
|
||||
* Removal of support for dead platforms
|
||||
* Generally improved MS Windows support (dealing with unicode filenames,
|
||||
improved ``--vo=direct3d``, improved window handling)
|
||||
* Better OSD rendering (using libass). This has full unicode support, and
|
||||
languages like Arabic should be better supported.
|
||||
* Cleaned up terminal output (nicer status line, less useless noise)
|
||||
* Support for playing URLs of popular streaming sites directly
|
||||
(e.g. ``mpv https://www.youtube.com/watch?v=...``)
|
||||
* Improved OpenGL output (``--vo=opengl-hq``)
|
||||
* Make ``--softvol`` default (**mpv** is not a mixer control panel)
|
||||
* Improved support for .cue files
|
||||
* Screenshot improvements (can save screenshots as JPG or PNG, configurable
|
||||
filenames, support for taking screenshots with or without subtitles - the
|
||||
``screenshot`` video filter is not needed anymore, and should not be put
|
||||
into the mpv config file)
|
||||
* Removal of teletext support
|
||||
* Removal of most builtin demuxers, using libavformat instead
|
||||
* Removal of builtin network support, using libavformat instead (also,
|
||||
support https via libavformat)
|
||||
* Replace image VOs (``--vo=jpeg`` etc.) with ``--vo=image``
|
||||
* Do not lose settings when playing a new file in the same player instance
|
||||
* New location for config files, new name for the binary.
|
||||
* Slave mode compatibility broken (see below)
|
||||
* Encoding functionality (replacement for mencoder, see ``DOCS/encoding.rst``)
|
||||
* Remove ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding can
|
||||
handle these use cases. For yuv4mpeg, for example, use:
|
||||
``mpv input.mkv -o output.y4m --no-audio --oautofps --oneverdrop``.
|
||||
* Image subtitles (DVDs etc.) are rendered in color and use more correct
|
||||
positioning (color can be disabled with ``--sub-gray``)
|
||||
* Wayland support
|
||||
* Support for precise scrolling which scales the parameter of commands. If the
|
||||
input doesn't support precise scrolling the scale factor stays 1.
|
||||
* OSX: Cocoa event loop is independent from mplayer's event loop, so user
|
||||
actions like accessing menus and live resizing do not block the playback.
|
||||
* OSX: Apple Remote support.
|
||||
* OSX: Media Keys support.
|
||||
* Windows: Added WASAPI audio output.
|
||||
* New OSD bar with chapter marks and not positioned in the middle of the video
|
||||
(though this can be customized with the ``--osd-bar-align-y`` option).
|
||||
* Allow customizing whether a key binding for seeking shows the video time, the
|
||||
OSD bar, or nothing (see section `Input Command Prefixes`_).
|
||||
* Display list of chapters and audio/subtitle tracks on OSD (see section
|
||||
`Properties`_).
|
||||
* ``--keep-open`` option to stop the player from closing the window and
|
||||
exiting after playback ends.
|
||||
* Re-enable screensaver while the player is paused.
|
||||
* Matroska edition switching at runtime.
|
||||
* Support for libavfilter (for video->video and audio->audio). This allows
|
||||
using most of FFmpeg's filters, which improve greatly on the old MPlayer
|
||||
filters in features, performance, and correctness.
|
||||
* Improved downmixing and output of surround audio. Instead of using hardcoded
|
||||
pan filters to do remixing, use libavresample. Channel maps are used to
|
||||
identify the channel layout, so e.g. ``3.0`` and ``2.1`` audio can be
|
||||
distinguished.
|
||||
* Allow resuming playback at a later point with ``Shift+q``, also see
|
||||
``quit_watch_later`` input command.
|
||||
* Support mapping multiple commands to one key.
|
||||
* Allow changing/adjusting video filters at runtime. (This is also used to make
|
||||
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise.)
|
||||
* Native VAAPI support
|
||||
* OSX: VDA support using libavcodec hwaccel API insted of FFmpeg's decoder. Up
|
||||
to 2-2.5x reduction in CPU usage.
|
||||
* General bug fixes and removal of long-standing issues
|
||||
* General code cleanups (including refactoring or rewrites of many parts)
|
||||
* Many more changes
|
||||
|
||||
Detailed Listing of User-visible Changes
|
||||
----------------------------------------
|
||||
|
||||
This listing is about changed command line switches, slave commands, and similar
|
||||
things. Completely removed features are not listed.
|
||||
|
||||
Command Line Switches
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* There is a new command line syntax, which is generally preferred over the old
|
||||
syntax. ``-optname optvalue`` becomes ``--optname=optvalue``.
|
||||
|
||||
The old syntax will not be removed in the near future. However, the new
|
||||
syntax is mentioned in all documentation and so on, so it is a good thing to
|
||||
know about this change.
|
||||
|
||||
(The new syntax was introduced in mplayer2.)
|
||||
* In general, negating switches like ``-noopt`` now have to be written as
|
||||
``-no-opt``, or better ``--no-opt``.
|
||||
* Per-file options are not the default anymore. You can explicitly specify
|
||||
file-local options. See ``Usage`` section.
|
||||
* Table of renamed/replaced switches:
|
||||
|
||||
=========================== ========================================
|
||||
Old New
|
||||
=========================== ========================================
|
||||
``-no<opt>`` ``--no-<opt>`` (add a dash)
|
||||
``-nosound`` ``--no-audio``
|
||||
``-use-filename-title`` ``--title="${filename}"``
|
||||
``-loop 0`` ``--loop=inf``
|
||||
``-hardframedrop`` ``--framedrop=hard``
|
||||
``-osdlevel`` ``--osd-level``
|
||||
``-delay`` ``--audio-delay``
|
||||
``-subdelay`` ``--sub-delay``
|
||||
``-subpos`` ``--sub-pos``
|
||||
``-forcedsubsonly`` ``--sub-forced-only``
|
||||
``-benchmark`` ``--untimed`` (no stats)
|
||||
``-xineramascreen`` ``--screen`` (different values)
|
||||
``-ss`` ``--start``
|
||||
``-endpos`` ``--length``
|
||||
``-cursor-autohide-delay`` ``--cursor-autohide``
|
||||
``-sub-fuzziness`` ``--autosub-match``
|
||||
``-subfont`` ``--sub-text-font``
|
||||
``-font`` ``--osd-font``
|
||||
``-subfont-*`` ``--sub-text-*``, ``--osd-*``
|
||||
``-subfont-text-scale`` ``--sub-scale``
|
||||
``-spugauss`` ``--sub-gauss``
|
||||
``-vobsub`` ``--sub`` (pass the .idx file)
|
||||
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
|
||||
``-vc ffh264vdpau`` (etc.) ``--hwdec=vdpau``
|
||||
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
|
||||
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
|
||||
``-x W``, ``-y H`` ``--geometry=WxH`` + ``--no-keepaspect``
|
||||
``-xy W`` ``--autofit=W``
|
||||
``-a52drc level`` ``--ad-lavc-ac3drc=level``
|
||||
``-dumpstream`` ``--stream-dump=<filename>``
|
||||
``-capture`` ``--stream-capture=<filename>``
|
||||
``-stop-xscreensaver`` ``--stop-screensaver``
|
||||
``-subfile`` ``--sub``
|
||||
``-lavdopts ...`` ``--vd-lavc-...``
|
||||
``-lavfdopts`` ``--demuxer-lavf-...``
|
||||
``-rawaudio ...`` ``--demuxer-rawaudio-...``
|
||||
``-rawvideo ...`` ``--demuxer-rawvideo-...``
|
||||
``-mixer`` AO suboptions (``alsa``, ``oss``)
|
||||
``-mixer-channel`` AO suboptions (``alsa``, ``oss``)
|
||||
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
|
||||
``-af volnorm`` ``--af=drc`` (renamed)
|
||||
``-zoom`` Inverse available as ``--video-unscaled``
|
||||
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
|
||||
=========================== ========================================
|
||||
|
||||
.. note::
|
||||
|
||||
``-opt val`` becomes ``--opt=val``.
|
||||
|
||||
input.conf and Slave Commands
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Table of renamed input commands:
|
||||
|
||||
This lists only commands that are not always gracefully handled by the
|
||||
internal legacy translation layer. If an input.conf contains any legacy
|
||||
commands, a warning will be printed when starting the player. The warnings
|
||||
also show the replacement commands.
|
||||
|
||||
Properties containing ``_`` to separate words use ``-`` instead.
|
||||
|
||||
+--------------------------------+----------------------------------------+
|
||||
| Old | New |
|
||||
+================================+========================================+
|
||||
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
|
||||
| | (translation layer cannot deal with |
|
||||
| | whitespace) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``switch_ratio [<ratio>]`` | ``set aspect <ratio>`` |
|
||||
| | |
|
||||
| | ``set aspect 0`` (to reset aspect) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
|
||||
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
|
||||
| | ``<dir>`` parameter unsupported. Use |
|
||||
| | a negative ``<step>`` instead. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``step_property <prop>`` | Prefix ``cycle`` or ``add`` with |
|
||||
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
|
||||
| | ``<step>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``osd_show_property_text`` | ``show_text <text>`` |
|
||||
| ``<text>`` | The property expansion format string |
|
||||
| | syntax slightly changed. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``osd_show_text`` | Now does the same as |
|
||||
| | ``osd_show_property_text``. Use the |
|
||||
| | ``raw`` prefix to disable property |
|
||||
| | expansion. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_tracks`` | ``show_text ${track-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_chapters`` | ``show_text ${chapter-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
|
||||
* The playtree has been removed. **mpv**'s internal playlist is a simple and
|
||||
flat list now. This simplifies the code and makes **mpv** usage less
|
||||
confusing.
|
||||
* Slave mode is broken. This mode is entirely insane in the ``old`` versions of
|
||||
MPlayer. A proper slave mode application needed tons of code and hacks to get
|
||||
it right. The main problem is that slave mode is a bad and incomplete
|
||||
interface, and to get around that, applications parsed output messages
|
||||
intended for users. It is hard to know which messages exactly are parsed by
|
||||
slave mode applications. This makes it virtually impossible to improve
|
||||
terminal output intended for users without possibly breaking something.
|
||||
|
||||
This is absolutely insane, and **mpv** will not try to keep slave mode
|
||||
compatible. If you are a developer of a slave mode application, contact us,
|
||||
and a new and better protocol can be developed.
|
||||
|
||||
Policy for Removed Features
|
||||
---------------------------
|
||||
|
||||
Features are a good thing, because they make users happy. As such, it is
|
||||
attempted to preserve useful features as far as possible. But if a feature is
|
||||
likely to be not used by many, and causes problems otherwise, it will be
|
||||
removed. Developers should not be burdened with fixing or cleaning up code that
|
||||
has no actual use.
|
||||
|
||||
It is always possible to add back removed features. File a feature request if a
|
||||
feature you relied on has been removed, and you want it back. Though it might be
|
||||
rejected in the worst case, it is much more likely that it will be either added
|
||||
back, or that a better solution will be implemented.
|
||||
|
||||
Why this Fork?
|
||||
--------------
|
||||
|
||||
* MPlayer wants to maintain old code, even if it is very bad code. It seems
|
||||
mplayer2 was forked because MPlayer developers refused to get rid of all the
|
||||
cruft. The mplayer2 and MPlayer codebases also deviated enough to make a
|
||||
reunification unlikely.
|
||||
* mplayer2 development is slow, and it is hard to get in changes. Details
|
||||
withheld as to not turn this into a rant.
|
||||
* MPlayer rarely merged from mplayer2, and mplayer2 practically stopped
|
||||
merging from MPlayer (not even code cleanups or new features are merged)
|
||||
* **mpv** intends to continuously merge from mplayer-svn and mplayer2, while
|
||||
speeding up development. There is willingness for significant changes, even
|
||||
if this means breaking compatibility.
|
||||
@@ -3,7 +3,7 @@ ENCODING
|
||||
|
||||
You can encode files from one format/codec to another using this facility.
|
||||
|
||||
``--o=<filename>``
|
||||
``-o <filename>``
|
||||
Enables encoding mode and specifies the output file name.
|
||||
|
||||
``--of=<format>``
|
||||
@@ -38,7 +38,7 @@ You can encode files from one format/codec to another using this facility.
|
||||
|
||||
``--oautofps``
|
||||
Sets the output format time base to the guessed frame rate of the input
|
||||
video (simulates MEncoder behavior, useful for AVI; may cause frame drops).
|
||||
video (simulates mencoder behavior, useful for AVI; may cause frame drops).
|
||||
Note that not all codecs and not all formats support VFR encoding, and some
|
||||
which do have bugs when a target bitrate is specified - use ``--ofps`` or
|
||||
``--oautofps`` to force CFR encoding in these cases.
|
||||
@@ -74,7 +74,7 @@ You can encode files from one format/codec to another using this facility.
|
||||
.. admonition:: Example
|
||||
|
||||
"``--oac=libmp3lame --oacopts=b=128000``"
|
||||
selects 128 kbps MP3 encoding.
|
||||
selects 128kbps MP3 encoding.
|
||||
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
@@ -149,7 +149,3 @@ You can encode files from one format/codec to another using this facility.
|
||||
container formats, e.g. AVI). In this mode, discontinuities are not fixed
|
||||
and all pts are passed through as-is. Never seek backwards or use multiple
|
||||
input files in this mode!
|
||||
|
||||
``--no-ometadata``
|
||||
Turns off copying of metadata from input files to output files when
|
||||
encoding (which is enabled by default).
|
||||
@@ -0,0 +1,651 @@
|
||||
INPUT.CONF
|
||||
==========
|
||||
|
||||
The input.conf file consists of a list of key bindings, for example::
|
||||
|
||||
s screenshot # take a screenshot with the s key
|
||||
|
||||
Each line maps a key to an input command. Keys are specified with their literal
|
||||
value (upper case if combined with ``Shift``), or a name for special keys. For
|
||||
example, ``a`` maps to the ``a`` key without shift, and ``A`` maps to ``a``
|
||||
with shift.
|
||||
|
||||
A list of special keys can be obtained with
|
||||
|
||||
``mpv --input-keylist``
|
||||
|
||||
In general, keys can be combined with ``Shift``, ``Ctrl`` and ``Alt``::
|
||||
|
||||
ctrl+q quit
|
||||
|
||||
**mpv** can be started in input test mode, which displays key bindings and the
|
||||
commands they're bound to on the OSD, instead of executing the commands::
|
||||
|
||||
mpv --input-test --demuxer=rawvideo --demuxer-rawvideo=w=1280:h=720 /dev/zero
|
||||
|
||||
(Commands which normally close the player will not work in this mode, and you
|
||||
must kill **mpv** externally to make it exit.)
|
||||
|
||||
General Input Command Syntax
|
||||
----------------------------
|
||||
|
||||
``[Shift+][Ctrl+][Alt+][Meta+]<key> [{<section>}] [<prefixes>] <command> (<argument>)* [; <command>]``
|
||||
|
||||
Newlines always start a new binding. ``#`` starts a comment (outside of quoted
|
||||
string arguments). To bind commands to the ``#`` key, ``SHARP`` can be used.
|
||||
|
||||
``<key>`` is either the literal character the key produces (ASCII or Unicode
|
||||
character), or a symbolic name (as printed by ``--input-keylist``).
|
||||
|
||||
``<section>`` (braced with ``{`` and ``}``) is the input section for this
|
||||
command.
|
||||
|
||||
Arguments are separated by whitespace. This applies even to string arguments.
|
||||
For this reason, string arguments should be quoted with ``"``. Inside quotes,
|
||||
C-style escaping can be used.
|
||||
|
||||
Optional arguments can be skipped with ``-``.
|
||||
|
||||
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
|
||||
----------------------
|
||||
|
||||
``ignore``
|
||||
Use this to "block" keys that should be unbound, and do nothing. Useful for
|
||||
disabling default bindings, without disabling all bindings with
|
||||
``--no-input-default-bindings``.
|
||||
|
||||
``seek <seconds> [relative|absolute|absolute-percent|- [default-precise|exact|keyframes]]``
|
||||
Change the playback position. By default, seeks by a relative amount of
|
||||
seconds.
|
||||
|
||||
The second argument sets the seek mode:
|
||||
|
||||
relative (default)
|
||||
Seek relative to current position (a negative value seeks backwards).
|
||||
absolute
|
||||
Seek to a given time.
|
||||
absolute-percent
|
||||
Seek to a given percent position.
|
||||
|
||||
The third argument defines how exact the seek is:
|
||||
|
||||
default-precise (default)
|
||||
Follow the default behavior as set by ``--hr-seek``, which by default
|
||||
does imprecise seeks (like ``keyframes``).
|
||||
exact
|
||||
Always do exact/hr/precise seeks (slow).
|
||||
keyframes
|
||||
Always restart playback at keyframe boundaries (fast).
|
||||
|
||||
``frame_step``
|
||||
Play one frame, then pause.
|
||||
|
||||
``frame_back_step``
|
||||
Go back by one frame, then pause. Note that this can be very slow (it tries
|
||||
to be precise, not fast), and sometimes fails to behave as expected. How
|
||||
well this works depends on whether precise seeking works correctly (e.g.
|
||||
see the ``--hr-seek-demuxer-offset`` option). Video filters or other video
|
||||
postprocessing that modifies timing of frames (e.g. deinterlacing) should
|
||||
usually work, but might make backstepping silently behave incorrectly in
|
||||
corner cases.
|
||||
|
||||
This does not work with audio-only playback.
|
||||
|
||||
``set <property> "<value>"``
|
||||
Set the given property to the given value.
|
||||
|
||||
``add <property> [<value>]``
|
||||
Add the given value to the property. On overflow or underflow, clamp the
|
||||
property to the maximum. If ``<value>`` is omitted, assume ``1``.
|
||||
|
||||
``cycle <property> [up|down]``
|
||||
Cycle the given property. ``up`` and ``down`` set the cycle direction. On
|
||||
overflow, set the property back to the minimum, on underflow set it to the
|
||||
maximum. If ``up`` or ``down`` is omitted, assume ``up``.
|
||||
|
||||
``speed_mult <value>``
|
||||
Multiply the ``speed`` property by the given value.
|
||||
|
||||
``screenshot [subtitles|video|window|- [single|each-frame]]``
|
||||
Take a screenshot.
|
||||
|
||||
First argument:
|
||||
|
||||
<subtitles> (default)
|
||||
Save the video image, in its original resolution, and with subtitles.
|
||||
Some video outputs may still include the OSD in the output under certain
|
||||
circumstances.
|
||||
<video>
|
||||
Like ``subtitles``, but typically without OSD or subtitles. The exact
|
||||
behavior depends on the selected video output.
|
||||
<window>
|
||||
Save the contents of the mpv window. Typically scaled, with OSD and
|
||||
subtitles. The exact behavior depends on the selected video output, and
|
||||
if no support is available, this will act like ``video``.
|
||||
|
||||
Second argument:
|
||||
|
||||
<single> (default)
|
||||
Take a single screenshot.
|
||||
<each-frame>
|
||||
Take a screenshot each frame. Issue this command again to stop taking
|
||||
screenshots.
|
||||
|
||||
``screenshot_to_file "<filename>" [subtitles|video|window]``
|
||||
Take a screenshot and save it to a given file. The format of the file will
|
||||
be guessed by the extension (and ``--screenshot-format`` is ignored - the
|
||||
behavior when the extension is missing or unknown is arbitrary).
|
||||
|
||||
The second argument is like the first argument to ``screenshot``.
|
||||
|
||||
This command tries to never overwrite files. If the file already exists,
|
||||
it fails.
|
||||
|
||||
Like all input command parameters, the filename is subject to property
|
||||
expansion as described in `Property Expansion`_.
|
||||
|
||||
``playlist_next [weak|force]``
|
||||
Go to the next entry on the playlist.
|
||||
|
||||
weak (default)
|
||||
If the last file on the playlist is currently played, do nothing.
|
||||
force
|
||||
Terminate playback if there are no more files on the playlist.
|
||||
|
||||
``playlist_prev [weak|force]``
|
||||
Go to the previous entry on the playlist.
|
||||
|
||||
weak (default)
|
||||
If the first file on the playlist is currently played, do nothing.
|
||||
force
|
||||
Terminate playback if the first file is being played.
|
||||
|
||||
``loadfile "<file>" [replace|append]``
|
||||
Load the given file and play it.
|
||||
|
||||
Second argument:
|
||||
|
||||
<replace> (default)
|
||||
Stop playback of the current file, and play the new file immediately.
|
||||
<append>
|
||||
Append the file to the playlist.
|
||||
|
||||
``loadlist "<playlist>" [replace|append]``
|
||||
Load the given playlist file (like ``--playlist``).
|
||||
|
||||
``playlist_clear``
|
||||
Clear the playlist, except the currently played file.
|
||||
|
||||
``playlist_remove <index>``
|
||||
Remove the playlist entry at the given index. Index values start counting
|
||||
with 0. You cannot remove the entry for the currently played file.
|
||||
|
||||
``playlist_move <index1> <index2>``
|
||||
Move the playlist entry at index1, so that it takes the place of the
|
||||
entry index2. (Paradoxically, the moved playlist entry will not have
|
||||
the index value index2 after moving if index1 was lower than index2,
|
||||
because index2 refers to the target entry, not the index the entry
|
||||
will have after moving.)
|
||||
|
||||
``run "<command>"``
|
||||
Run the given command with ``/bin/sh -c``. The string is expanded like in
|
||||
`Property Expansion`_.
|
||||
|
||||
``quit [<code>]``
|
||||
Exit the player using the given exit code.
|
||||
|
||||
``quit_watch_later``
|
||||
Exit player, and store current playback position. Playing that file later
|
||||
will seek to the previous position on start.
|
||||
|
||||
``sub_add "<file>"``
|
||||
Load the given subtitle file. It is not selected as current subtitle after
|
||||
loading.
|
||||
|
||||
``sub_remove [<id>]``
|
||||
Remove the given subtitle track. If the ``id`` argument is missing, remove
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
``sub_reload [<id>]``
|
||||
Reload the given subtitle tracks. If the ``id`` argument is missing, remove
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
This works by unloading and re-adding the subtitle track.
|
||||
|
||||
``sub_step <skip>``
|
||||
Change subtitle timing such, that the subtitle event after the next
|
||||
``<skip>`` subtitle events is displayed. ``<skip>`` can be negative to step
|
||||
backwards.
|
||||
|
||||
``sub_seek <skip>``
|
||||
Seek to the next (skip set to 1) or the previous (skip set to -1) subtitle.
|
||||
This is similar to ``sub_step``, except that it seeks video and audio
|
||||
instead of adjusting the subtitle delay.
|
||||
|
||||
Like with ``sub_step``, this works with external text subtitles only. For
|
||||
embedded text subtitles (like with Matroska), this works only with subtitle
|
||||
events that have already been displayed.
|
||||
|
||||
``osd [<level>]``
|
||||
Toggle OSD level. If ``<level>`` is specified, set the OSD mode
|
||||
(see ``--osd-level`` for valid values).
|
||||
|
||||
``print_text "<string>"``
|
||||
Print text to stdout. The string can contain properties (see
|
||||
`Property Expansion`_).
|
||||
|
||||
``show_text "<string>" [<duration>|- [<level>]]``
|
||||
Show text on the OSD. The string can contain properties, which are expanded
|
||||
as described in `Property Expansion`_. This can be used to show playback
|
||||
time, filename, and so on.
|
||||
|
||||
<duration>
|
||||
The time in ms to show the message for. By default, it uses the same
|
||||
value as ``--osd-duration``.
|
||||
|
||||
<level>
|
||||
The minimum OSD level to show the text at (see ``--osd-level``).
|
||||
|
||||
``show_progress``
|
||||
Show the progress bar, the elapsed time and the total duration of the file
|
||||
on the OSD.
|
||||
|
||||
Input Commands that are Possibly Subject to Change
|
||||
--------------------------------------------------
|
||||
|
||||
``af set|add|toggle|del|clr "filter1=params,filter2,..."``
|
||||
Change audio filter chain. See ``vf`` command.
|
||||
|
||||
``vf set|add|toggle|del|clr "filter1=params,filter2,..."``
|
||||
Change video filter chain.
|
||||
|
||||
The first argument decides what happens:
|
||||
|
||||
set
|
||||
Overwrite the previous filter chain with the new one.
|
||||
|
||||
add
|
||||
Append the new filter chain to the previous one.
|
||||
|
||||
toggle
|
||||
Check if the given filter (with the exact parameters) is already
|
||||
in the video chain. If yes, remove the filter. If no, add the filter.
|
||||
(If several filters are passed to the command, this is done for
|
||||
each filter.)
|
||||
|
||||
del
|
||||
Remove the given filters from the video chain. Unlike in the other
|
||||
cases, the second parameter is a comma separated list of filter names
|
||||
or integer indexes. ``0`` would denote the first filter. Negative
|
||||
indexes start from the last filter, and ``-1`` denotes the last
|
||||
filter.
|
||||
|
||||
clr
|
||||
Remove all filters. Note that like the other sub-commands, this does
|
||||
not control automatically inserted filters.
|
||||
|
||||
You can assign labels to filter by prefixing them with ``@name:`` (where
|
||||
``name`` is a user-chosen arbitrary identifier). Labels can be used to
|
||||
refer to filters by name in all of the filter chain modification commands.
|
||||
For ``add``, using an already used label will replace the existing filter.
|
||||
|
||||
The ``vf`` command shows the list of requested filters on the OSD after
|
||||
changing the filter chain. This is roughly equivalent to
|
||||
``show_text ${vf}``. Note that auto-inserted filters for format conversion
|
||||
are not shown on the list, only what was requested by the user.
|
||||
|
||||
.. admonition:: Example for input.conf
|
||||
|
||||
- ``a vf set flip`` turn video upside-down on the ``a`` key
|
||||
- ``b vf set ""`` remove all video filters on ``b``
|
||||
- ``c vf toggle lavfi=gradfun`` toggle debanding on ``c``
|
||||
|
||||
``enable_section "<section>" [default|exclusive]``
|
||||
Enable all key bindings in the named input section.
|
||||
|
||||
The enabled input sections form a stack. Bindings in sections on the top of
|
||||
the stack are preferred to lower sections. This command puts the section
|
||||
on top of the stack. If the section was already on the stack, it is
|
||||
implicitly removed beforehand. (A section cannot be on the stack more than
|
||||
once.)
|
||||
|
||||
If ``exclusive`` is specified as second argument, all sections below the
|
||||
newly enabled section are disabled. They will be re-enabled as soon as
|
||||
all exclusive sections above them are removed.
|
||||
|
||||
``disable_section "<section>"``
|
||||
Disable the named input section. Undoes ``enable_section``.
|
||||
|
||||
``overlay_add <id> <x> <y> "<file>" <offset> "<fmt>" <w> <h> <stride>``
|
||||
Add an OSD overlay sourced from raw data. This might be useful for scripts
|
||||
and applications controlling mpv, and which want to display things on top
|
||||
of the video window.
|
||||
|
||||
Overlays are usually displayed in screen resolution, but with some VOs,
|
||||
the resolution is reduced to that of the video's. You can read the
|
||||
``osd-width`` and ``osd-height`` properties. At least with ``--vo-xv`` and
|
||||
anamorphic video (such as DVD), ``osd-par`` should be read as well, and the
|
||||
overlay should be aspect-compensated. (Future directions: maybe mpv should
|
||||
take care of some of these things automatically, but it's hard to tell
|
||||
where to draw the line.)
|
||||
|
||||
``id`` is an integer between 0 and 63 identifying the overlay element. The
|
||||
ID can be used to add multiple overlay parts, update a part by using this
|
||||
command with an already existing ID, or to remove a part with
|
||||
``overlay_remove``. Using a previously unused ID will add a new overlay,
|
||||
while reusing an ID will update it. (Future directions: there should be
|
||||
something to ensure different programs wanting to create overlays don't
|
||||
conflict with each others, should that ever be needed.)
|
||||
|
||||
``x`` and ``y`` specify the position where the OSD should be displayed.
|
||||
|
||||
``file`` specifies the file the raw image data is read from. It can be
|
||||
either a numeric UNIX file descriptor prefixed with ``@`` (e.g. ``@4``),
|
||||
or a filename. The file will be mapped into memory with ``mmap()``. Some VOs
|
||||
will pass the mapped pointer directly to display APIs (e.g. opengl or
|
||||
vdpau), so no actual copying is involved. Truncating the source file while
|
||||
the overlay is active will crash the player. You shouldn't change the data
|
||||
while the overlay is active, because the data is essentially accessed at
|
||||
random points. Instead, call ``overlay_add`` again (preferably with a
|
||||
different memory region to prevent tearing).
|
||||
|
||||
``offset`` is the offset of the first pixel in the source file. It is
|
||||
passed directly to ``mmap`` and is subject to certain restrictions
|
||||
(see ``man mmap`` for details). In particular, this value has to be a
|
||||
multiple of the system's page size.
|
||||
|
||||
``fmt`` is a string identifying the image format. Currently, only ``bgra``
|
||||
is defined. This format has 4 bytes per pixels, with 8 bits per component.
|
||||
The least significant 8 bits are blue, and the most significant 8 bits
|
||||
are alpha (in little endian, the components are B-G-R-A, with B as first
|
||||
byte). This uses premultiplied alpha: every color component is already
|
||||
multiplied with the alpha component. This means the numeric value of each
|
||||
component is equal to or smaller than the alpha component. (Violating this
|
||||
rule will lead to different results with different VOs: numeric overflows
|
||||
resulting from blending broken alpha values is considered something that
|
||||
shouldn't happen, and consequently implementations don't ensure that you
|
||||
get predictable behavior in this case.)
|
||||
|
||||
``w``, ``h``, and ``stride`` specify the size of the overlay. ``w`` is the
|
||||
visible width of the overlay, while ``stride`` gives the width in bytes in
|
||||
memory. In the simple case, and with the ``bgra`` format, ``stride==4*w``.
|
||||
In general, the total amount of memory accessed is ``stride * h``.
|
||||
(Technically, the minimum size would be ``stride * (h - 1) + w * 4``, but
|
||||
for simplicity, the player will access all ``stride * h`` bytes.)
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
When updating the overlay, you should prepare a second shared memory
|
||||
region (e.g. make use of the offset parameter) and add this as overlay,
|
||||
instead of reusing the same memory every time. Otherwise, you might
|
||||
get the equivalent of tearing, when your application and mpv write/read
|
||||
the buffer at the same time. Also, keep in mind that mpv might access
|
||||
an overlay's memory at random times whenever it feels the need to do
|
||||
so, for example when redrawing the screen.
|
||||
|
||||
``overlay_remove <id>``
|
||||
Remove an overlay added with ``overlay_add`` and the same ID. Does nothing
|
||||
if no overlay with this ID exists.
|
||||
|
||||
Undocumented commands: ``tv_start_scan``, ``tv_step_channel``, ``tv_step_norm``,
|
||||
``tv_step_chanlist``, ``tv_set_channel``, ``tv_last_channel``, ``tv_set_freq``,
|
||||
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
|
||||
``radio_set_channel``, ``radio_set_freq``, ``radio_step_freq`` (all of these
|
||||
should be replaced by properties), ``stop`` (questionable use), ``get_property``
|
||||
(?), ``vo_cmdline`` (experimental).
|
||||
|
||||
Input Command Prefixes
|
||||
----------------------
|
||||
|
||||
These prefixes are placed between key name and the actual command. Multiple
|
||||
prefixes can be specified. They are separated by whitespace.
|
||||
|
||||
``osd-auto`` (default)
|
||||
Use the default behavior for this command.
|
||||
``no-osd``
|
||||
Do not use any OSD for this command.
|
||||
``osd-bar``
|
||||
If possible, show a bar with this command. Seek commands will show the
|
||||
progress bar, property changing commands may show the newly set value.
|
||||
``osd-msg``
|
||||
If possible, show an OSD message with this command. Seek command show
|
||||
the current playback time, property changing commands show the newly set
|
||||
value as text.
|
||||
``osd-msg-bar``
|
||||
Combine osd-bar and osd-msg.
|
||||
``raw``
|
||||
Do not expand properties in string arguments. (Like ``"${property-name}"``.)
|
||||
``expand-properties`` (default)
|
||||
All string arguments are expanded as described in `Property Expansion`_.
|
||||
|
||||
|
||||
All of the osd prefixes are still overridden by the global ``--osd-level``
|
||||
settings.
|
||||
|
||||
Undocumented prefixes: ``pausing``, ``pausing_keep``, ``pausing_toggle``,
|
||||
``pausing_keep_force``. (Should these be made official?)
|
||||
|
||||
Input Sections
|
||||
--------------
|
||||
|
||||
Input sections group a set of bindings, and enable or disable them at once.
|
||||
In ``input.conf``, each key binding is assigned to an input section, rather
|
||||
than actually having explicit text sections.
|
||||
|
||||
Also see ``enable_section`` and ``disable_section`` commands.
|
||||
|
||||
Predefined bindings:
|
||||
|
||||
``default``
|
||||
Bindings without input section are implicitly assigned to this section. It
|
||||
is enabled by default during normal playback.
|
||||
``encode``
|
||||
Section which is active in encoding mode. It is enabled exclusively, so
|
||||
that bindings in the ``default`` sections are ignored.
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
Properties are used to set mpv options during runtime, or to query arbitrary
|
||||
information. They can be manipulated with the ``set``/``add``/``cycle``
|
||||
commands, and retrieved with ``show_text``, or anything else that uses property
|
||||
expansion. (See `Property Expansion`_.)
|
||||
|
||||
The ``W`` column indicates whether the property is generally writable. If an
|
||||
option is referenced, the property should take/return exactly the same values
|
||||
as the option.
|
||||
|
||||
=============================== = ==================================================
|
||||
Name W Comment
|
||||
=============================== = ==================================================
|
||||
``osd-level`` x see ``--osd-level``
|
||||
``osd-scale`` x osd font size multiplicator, see ``--osd-scale``
|
||||
``loop`` x see ``--loop``
|
||||
``speed`` x see ``--speed``
|
||||
``filename`` currently played file (path stripped)
|
||||
``path`` currently played file (full path)
|
||||
``media-title`` filename, title tag, or libquvi ``QUVIPROP_PAGETITLE``
|
||||
``demuxer``
|
||||
``stream-path`` filename (full path) of stream layer filename
|
||||
``stream-pos`` x byte position in source stream
|
||||
``stream-start`` start byte offset in source stream
|
||||
``stream-end`` end position in bytes in source stream
|
||||
``stream-length`` length in bytes (``${stream-end} - ${stream-start}``)
|
||||
``stream-time-pos`` x time position in source stream (also see ``time-pos``)
|
||||
``length`` length of the current file in seconds
|
||||
``avsync`` last A/V synchronization difference
|
||||
``percent-pos`` x position in current file (0-100)
|
||||
``ratio-pos`` x position in current file (0.0-1.0)
|
||||
``time-pos`` x position in current file in seconds
|
||||
``time-remaining`` estimated remaining length of the file in seconds
|
||||
``chapter`` x current chapter number
|
||||
``edition`` x current MKV edition number
|
||||
``titles`` number of DVD titles
|
||||
``chapters`` number of chapters
|
||||
``editions`` number of MKV editions
|
||||
``angle`` x current DVD angle
|
||||
``metadata`` metadata key/value pairs
|
||||
``metadata/<key>`` value of metadata entry ``<key>``
|
||||
``chapter-metadata`` metadata of current chapter (works similar)
|
||||
``pause`` x pause status (bool)
|
||||
``cache`` network cache fill state (0-100)
|
||||
``pts-association-mode`` x see ``--pts-association-mode``
|
||||
``hr-seek`` x see ``--hr-seek``
|
||||
``volume`` x current volume (0-100)
|
||||
``mute`` x current mute status (bool)
|
||||
``audio-delay`` x see ``--audio-delay``
|
||||
``audio-format`` audio format (string)
|
||||
``audio-codec`` audio codec selected for decoding
|
||||
``audio-bitrate`` audio bitrate
|
||||
``samplerate`` audio samplerate
|
||||
``channels`` number of audio channels
|
||||
``aid`` x current audio track (similar to ``--aid``)
|
||||
``audio`` x alias for ``aid``
|
||||
``balance`` x audio channel balance
|
||||
``fullscreen`` x see ``--fullscreen``
|
||||
``deinterlace`` x similar to ``--deinterlace``
|
||||
``colormatrix`` x see ``--colormatrix``
|
||||
``colormatrix-input-range`` x see ``--colormatrix-input-range``
|
||||
``colormatrix-output-range`` x see ``--colormatrix-output-range``
|
||||
``ontop`` x see ``--ontop``
|
||||
``border`` x see ``--border``
|
||||
``framedrop`` x see ``--framedrop``
|
||||
``gamma`` x see ``--gamma``
|
||||
``brightness`` x see ``--brightness``
|
||||
``contrast`` x see ``--contrast``
|
||||
``saturation`` x see ``--saturation``
|
||||
``hue`` x see ``--hue``
|
||||
``panscan`` x see ``--panscan``
|
||||
``video-format`` video format (string)
|
||||
``video-codec`` video codec selected for decoding
|
||||
``video-bitrate`` video bitrate
|
||||
``width`` video width (container or decoded size)
|
||||
``height`` video height
|
||||
``fps`` container FPS (may contain bogus values)
|
||||
``dwidth`` video width (after filters and aspect scaling)
|
||||
``dheight`` video height
|
||||
``aspect`` x video aspect
|
||||
``osd-width`` last known OSD width (can be 0)
|
||||
``osd-height`` last known OSD height (can be 0)
|
||||
``osd-par`` last known OSD display pixel aspect (can be 0)
|
||||
``vid`` x current video track (similar to ``--vid``)
|
||||
``video`` x alias for ``vid``
|
||||
``video-align-x`` x see ``--video-align-x``
|
||||
``video-align-y`` x see ``--video-align-y``
|
||||
``video-pan-x`` x see ``--video-pan-x``
|
||||
``video-pan-y`` x see ``--video-pan-y``
|
||||
``video-zoom`` x see ``--video-zoom``
|
||||
``program`` x switch TS program (write-only)
|
||||
``sid`` x current subtitle track (similar to ``--sid``)
|
||||
``sub`` x alias for ``sid``
|
||||
``sub-delay`` x see ``--sub-delay``
|
||||
``sub-pos`` x see ``--sub-pos``
|
||||
``sub-visibility`` x see ``--sub-visibility``
|
||||
``sub-forced-only`` x see ``--sub-forced-only``
|
||||
``sub-scale`` x subtitle font size multiplicator
|
||||
``ass-use-margins`` x see ``--ass-use-margins``
|
||||
``ass-vsfilter-aspect-compat`` x see ``--ass-vsfilter-aspect-compat``
|
||||
``ass-style-override`` x see ``--ass-style-override``
|
||||
``stream-capture`` x a filename, see ``--capture``
|
||||
``tv-brightness`` x
|
||||
``tv-contrast`` x
|
||||
``tv-saturation`` x
|
||||
``tv-hue`` x
|
||||
``playlist-pos`` current position on playlist
|
||||
``playlist-count`` number of total playlist entries
|
||||
``playlist`` playlist, current entry marked
|
||||
``track-list`` list of audio/video/sub tracks, current entry marked
|
||||
``chapter-list`` list of chapters, current entry marked
|
||||
``quvi-format`` x see ``--quvi-format``
|
||||
``af`` x see ``--af``
|
||||
``vf`` x see ``--vf``
|
||||
``options/name`` read-only access to value of option ``--name``
|
||||
=============================== = ==================================================
|
||||
|
||||
Property Expansion
|
||||
------------------
|
||||
|
||||
All string arguments to input commands as well as certain options (like
|
||||
``--playing-msg``) are subject to property expansion.
|
||||
|
||||
.. admonition:: Example for input.conf
|
||||
|
||||
``i show_text "Filename: ${filename}"``
|
||||
shows the filename of the current file when pressing the ``i`` key
|
||||
|
||||
Within ``input.conf``, property expansion can be inhibited by putting the
|
||||
``raw`` prefix in front of commands.
|
||||
|
||||
The following expansions are supported:
|
||||
|
||||
``${NAME}``
|
||||
Expands to the value of the property ``NAME``. If retrieving the property
|
||||
fails, expand to an error string. (Use ``${NAME:}`` with a trailing
|
||||
``:`` to expand to an empty string instead.)
|
||||
If ``NAME`` is prefixed with ``=``, expand to the raw value of the property
|
||||
(see section below).
|
||||
``${NAME:STR}``
|
||||
Expands to the value of the property ``NAME``, or ``STR`` if the
|
||||
property cannot be retrieved. ``STR`` is expanded recursively.
|
||||
``${?NAME:STR}``
|
||||
Expands to ``STR`` (recursively) if the property ``NAME`` is available.
|
||||
``${!NAME:STR}``
|
||||
Expands to ``STR`` (recursively) if the property ``NAME`` cannot be
|
||||
retrieved.
|
||||
``${?NAME==VALUE:STR}``
|
||||
Expands to ``STR`` (recursively) if the property ``NAME`` expands to a
|
||||
string equal to ``VALUE``. You can prefix ``NAME`` with ``=`` in order to
|
||||
compare the raw value of a property (see section below). If the property
|
||||
is unavailable, or other errors happen when retrieving it, the value is
|
||||
never considered equal.
|
||||
Note that ``VALUE`` can't contain any of the characters ``:`` or ``}``.
|
||||
Also, it is possible that escaping with ``"`` or ``%`` might be added in
|
||||
the future, should the need arise.
|
||||
``${!NAME==VALUE:STR}``
|
||||
Same as with the ``?`` variant, but ``STR`` is expanded if the value is
|
||||
not equal. (Using the same semantics as with ``?``.)
|
||||
``$$``
|
||||
Expands to ``$``.
|
||||
``$}``
|
||||
Expands to ``}``. (To produce this character inside recursive
|
||||
expansion.)
|
||||
``$>``
|
||||
Disable property expansion and special handling of ``$`` for the rest
|
||||
of the string.
|
||||
|
||||
In places where property expansion is allowed, C-style escapes are often
|
||||
accepted as well. Example:
|
||||
|
||||
- ``\n`` becomes a newline character
|
||||
- ``\\`` expands to ``\``
|
||||
|
||||
Raw and Formatted Properties
|
||||
----------------------------
|
||||
|
||||
Normally, properties are formatted as human-readable text, meant to be
|
||||
displayed on OSD or on the terminal. It is possible to retrieve an unformatted
|
||||
(raw) value from a property by prefixing its name with ``=``. These raw values
|
||||
can be parsed by other programs and follow the same conventions as the options
|
||||
associated with the properties.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
- ``${time-pos}`` expands to ``00:14:23`` (if playback position is at 14
|
||||
minutes 23 seconds)
|
||||
- ``${=time-pos}`` expands to ``863.4`` (same time, plus 400 milliseconds -
|
||||
milliseconds are normally not shown in the formatted case)
|
||||
|
||||
Sometimes, the difference in amount of information carried by raw and formatted
|
||||
property values can be rather big. In some cases, raw values have more
|
||||
information, like higher precision than seconds with ``time-pos``. Sometimes
|
||||
it is the other way around, e.g. ``aid`` shows track title and language in the
|
||||
formatted case, but only the track number if it is raw.
|
||||
@@ -0,0 +1,18 @@
|
||||
LUA SCRIPTING
|
||||
=============
|
||||
|
||||
mpv can load Lua scripts. These scripts can be used to control mpv in a similar
|
||||
way to slave mode. mpv provides the builtin module ``mp`` (can be loaded
|
||||
with ``require 'mpv'``), which provides functions to send commands to the
|
||||
mpv core and to retrieve information about playback state, user settings,
|
||||
file information, and so on.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
Lua scripting is work in progress, and it's in a very early stage. When
|
||||
writing scripts, rely only on the features and functions documented here.
|
||||
Everything else is subject to change.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
Nothing is finished and documented yet.
|
||||
@@ -2,23 +2,42 @@ mpv
|
||||
###
|
||||
|
||||
##############
|
||||
a media player
|
||||
a movie player
|
||||
##############
|
||||
|
||||
:Author: wm4
|
||||
:Copyright: GPLv2+
|
||||
:Manual section: 1
|
||||
:Manual group: multimedia
|
||||
|
||||
.. raw:: latex
|
||||
|
||||
\tableofcontents
|
||||
\pagebreak
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
| **mpv** [options] [file|URL|PLAYLIST|-]
|
||||
| **mpv** [options] [file|URL|-]
|
||||
| **mpv** [options] --playlist=PLAYLIST
|
||||
| **mpv** [options] files
|
||||
| **mpv** [options] {group of files and options}
|
||||
| **mpv** [bd]://[title][/device] [options]
|
||||
| **mpv** dvd://[title|[start\_title]-end\_title][/device] [options]
|
||||
| **mpv** \vcd://[/device]
|
||||
| **mpv** \tv://[channel][/input_id] [options]
|
||||
| **mpv** radio://[channel|frequency][/capture] [options]
|
||||
| **mpv** \pvr:// [options]
|
||||
| **mpv** \dvb://[card\_number@]channel [options]
|
||||
| **mpv** \mf://[filemask|\@listfile] [-mf options] [options]
|
||||
| **mpv** cdda://track[-endtrack][:speed][/device] [options]
|
||||
| **mpv** [file|mms[t]|http|httpproxy|rt[s]p|ftp|udp|smb]://[user:pass\@]URL[:port] [options]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
**mpv** is a media player based on MPlayer and mplayer2. It supports a wide variety of video
|
||||
**mpv** is a movie player based on MPlayer and mplayer2. It supports a wide variety of video
|
||||
file formats, audio and video codecs, and subtitle types. Special input URL
|
||||
types are available to read input from a variety of sources other than disk
|
||||
files. Depending on platform, a variety of different video and audio output
|
||||
@@ -39,16 +58,15 @@ Keyboard Control
|
||||
----------------
|
||||
|
||||
LEFT and RIGHT
|
||||
Seek backward/forward 5 seconds. Shift+arrow does a 1 second exact seek
|
||||
Seek backward/forward 10 seconds. Shift+arrow does a 1 second exact seek
|
||||
(see ``--hr-seek``).
|
||||
|
||||
UP and DOWN
|
||||
Seek forward/backward 1 minute. Shift+arrow does a 5 second exact seek (see
|
||||
``--hr-seek``).
|
||||
|
||||
Ctrl+LEFT and Ctrl+RIGHT
|
||||
Seek to the previous/next subtitle. Subject to some restrictions and
|
||||
might not work always; see ``sub_seek`` command.
|
||||
PGUP and PGDWN
|
||||
Seek forward/backward 10 minutes.
|
||||
|
||||
[ and ]
|
||||
Decrease/increase current playback speed by 10%.
|
||||
@@ -63,32 +81,37 @@ BACKSPACE
|
||||
Go backward/forward in the playlist.
|
||||
|
||||
ENTER
|
||||
Go forward in the playlist.
|
||||
Go forward in the playlist, even over the end.
|
||||
|
||||
p / SPACE
|
||||
Pause (pressing again unpauses).
|
||||
|
||||
\.
|
||||
Step forward. Pressing once will pause, every consecutive press will
|
||||
Step forward. Pressing once will pause movie, every consecutive press will
|
||||
play one frame and then go into pause mode again.
|
||||
|
||||
,
|
||||
Step backward. Pressing once will pause, every consecutive press will
|
||||
play one frame in reverse and then go into pause mode again.
|
||||
|
||||
q
|
||||
q / ESC
|
||||
Stop playing and quit.
|
||||
|
||||
Q
|
||||
Like ``q``, but store the current playback position. Playing the same file
|
||||
later will resume at the old playback position if possible.
|
||||
|
||||
U
|
||||
Stop playing (and quit if ``--idle`` is not used).
|
||||
|
||||
\+ and -
|
||||
Adjust audio delay by +/- 0.1 seconds.
|
||||
|
||||
/ and *
|
||||
Decrease/increase volume.
|
||||
|
||||
9 and 0
|
||||
Decrease/increase volume.
|
||||
|
||||
( and )
|
||||
Adjust audio balance in favor of left/right channel.
|
||||
|
||||
m
|
||||
Mute sound.
|
||||
|
||||
@@ -98,22 +121,19 @@ m
|
||||
\#
|
||||
Cycle through the available audio tracks.
|
||||
|
||||
TAB (MPEG-TS and libavformat only)
|
||||
Cycle through the available programs.
|
||||
|
||||
f
|
||||
Toggle fullscreen (see also ``--fs``).
|
||||
|
||||
ESC
|
||||
Exit fullscreen mode.
|
||||
|
||||
T
|
||||
Toggle stay-on-top (see also ``--ontop``).
|
||||
|
||||
w and e
|
||||
Decrease/increase pan-and-scan range.
|
||||
|
||||
o (also P)
|
||||
Show progression bar, elapsed time and total duration on the OSD.
|
||||
|
||||
O
|
||||
o
|
||||
Toggle OSD states: none / seek / seek + timer / seek + timer + total time.
|
||||
|
||||
d
|
||||
@@ -126,15 +146,12 @@ v
|
||||
j and J
|
||||
Cycle through the available subtitles.
|
||||
|
||||
F
|
||||
Toggle displaying "forced subtitles".
|
||||
|
||||
x and z
|
||||
Adjust subtitle delay by +/- 0.1 seconds.
|
||||
|
||||
l
|
||||
Set/clear A-B loop points. See ``ab_loop`` command for details.
|
||||
|
||||
Ctrl + and Ctrl -
|
||||
Adjust audio delay by +/- 0.1 seconds.
|
||||
|
||||
V
|
||||
Toggle subtitle VSFilter aspect compatibility mode. See
|
||||
``--ass-vsfilter-aspect-compat`` for more info.
|
||||
@@ -152,18 +169,23 @@ S
|
||||
I
|
||||
Show filename on the OSD.
|
||||
|
||||
PGUP and PGDWN
|
||||
P
|
||||
Show progression bar, elapsed time and total duration on the OSD.
|
||||
|
||||
! and @
|
||||
Seek to the beginning of the previous/next chapter. In most cases,
|
||||
"previous" will actually go to the beginning of the current chapter; see
|
||||
``--chapter-seek-threshold``.
|
||||
|
||||
Shift+PGUP and Shift+PGDWN
|
||||
Seek backward or forward by 10 minutes. (This used to be mapped to
|
||||
PGUP/PGDWN without Shift.)
|
||||
|
||||
D
|
||||
D (``--vo=vdpau``, ``--vf=yadif`` only)
|
||||
Activate/deactivate deinterlacer.
|
||||
|
||||
A
|
||||
Cycle through the available DVD angles.
|
||||
|
||||
c
|
||||
Change YUV colorspace.
|
||||
|
||||
(The following keys are valid only when using a video output that supports the
|
||||
corresponding adjustment, or the software equalizer (``--vf=eq``).)
|
||||
|
||||
@@ -179,24 +201,22 @@ corresponding adjustment, or the software equalizer (``--vf=eq``).)
|
||||
7 and 8
|
||||
Adjust saturation.
|
||||
|
||||
(The following keys are valid only on OS X.)
|
||||
(The following keys are valid only on OSX.)
|
||||
|
||||
command + 0
|
||||
Resize video window to half its original size.
|
||||
(On other platforms, you can bind keys to change the ``window-scale``
|
||||
property.)
|
||||
Resize movie window to half its original size.
|
||||
|
||||
command + 1
|
||||
Resize video window to its original size.
|
||||
Resize movie window to its original size.
|
||||
|
||||
command + 2
|
||||
Resize video window to double its original size.
|
||||
Resize movie window to double its original size.
|
||||
|
||||
command + f
|
||||
Toggle fullscreen (see also ``--fs``).
|
||||
|
||||
command + [ and command + ]
|
||||
Set video window alpha.
|
||||
Set movie window alpha.
|
||||
|
||||
(The following keys are valid if you have a keyboard with multimedia keys.)
|
||||
|
||||
@@ -215,6 +235,12 @@ support.)
|
||||
h and k
|
||||
Select previous/next channel.
|
||||
|
||||
n
|
||||
Change norm.
|
||||
|
||||
u
|
||||
Change channel list.
|
||||
|
||||
Mouse Control
|
||||
-------------
|
||||
|
||||
@@ -253,28 +279,6 @@ It has the following format::
|
||||
|
||||
``mpv --ao=pcm:file=%`expr length "$NAME"`%"$NAME" test.avi``
|
||||
|
||||
Paths
|
||||
-----
|
||||
|
||||
Some care must be taken when passing arbitrary paths and filenames to mpv. For
|
||||
example, paths starting with ``-`` will be interpreted as options. Likewise,
|
||||
if a path contains the sequence ``://``, the string before that might be
|
||||
interpreted as protocol prefix, even though ``://`` can be part of a legal
|
||||
UNIX path. To avoid problems with arbitrary paths, you should be sure that
|
||||
absolute paths passed to mpv start with ``/``, and relative paths with ``./``.
|
||||
|
||||
The name ``-`` itself is interpreted as stdin, and will cause mpv to disable
|
||||
console controls. (Which makes it suitable for playing data piped to stdin.)
|
||||
|
||||
For paths passed to suboptions, the situation is further complicated by the
|
||||
need to escape special characters. To work this around, the path can be
|
||||
additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
|
||||
|
||||
Some mpv options interpret paths starting with ``~``. Currently, the prefix
|
||||
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
|
||||
``~/`` expands to the user's home directory. (The trailing ``/`` is always
|
||||
required.)
|
||||
|
||||
Per-File Options
|
||||
----------------
|
||||
|
||||
@@ -324,7 +328,7 @@ Location and Syntax
|
||||
You can put all of the options in configuration files which will be read every
|
||||
time mpv is run. The system-wide configuration file 'mpv.conf' is in your
|
||||
configuration directory (e.g. ``/etc/mpv`` or ``/usr/local/etc/mpv``), the
|
||||
user-specific one is ``~/.config/mpv/mpv.conf``.
|
||||
user-specific one is ``~/.mpv/config``.
|
||||
User-specific options override system-wide options and options given on the
|
||||
command line override either. The syntax of the configuration files is
|
||||
``option=<value>``; everything after a *#* is considered a comment. Options
|
||||
@@ -359,13 +363,13 @@ File-specific Configuration Files
|
||||
---------------------------------
|
||||
|
||||
You can also write file-specific configuration files. If you wish to have a
|
||||
configuration file for a file called 'video.avi', create a file named
|
||||
'video.avi.conf' with the file-specific options in it and put it in
|
||||
``~/.config/mpv/``. You can also put the configuration file in the same directory
|
||||
as the file to be played. Both require you to set the ``--use-filedir-conf``
|
||||
configuration file for a file called 'movie.avi', create a file named
|
||||
'movie.avi.conf' with the file-specific options in it and put it in
|
||||
``~/.mpv/``. You can also put the configuration file in the same directory
|
||||
as the file to be played, as long as you give the ``--use-filedir-conf``
|
||||
option (either on the command line or in your global config file). If a
|
||||
file-specific configuration file is found in the same directory, no
|
||||
file-specific configuration is loaded from ``~/.config/mpv``. In addition, the
|
||||
file-specific configuration is loaded from ``~/.mpv``. In addition, the
|
||||
``--use-filedir-conf`` option enables directory-specific configuration files.
|
||||
For this, mpv first tries to load a mpv.conf from the same directory
|
||||
as the file played and then tries to load any file-specific configuration.
|
||||
@@ -396,7 +400,7 @@ profile name ``default`` to continue with normal options.
|
||||
|
||||
[extension.flv]
|
||||
profile-desc="profile for .flv files"
|
||||
vf=flip
|
||||
flip=yes
|
||||
|
||||
[ao.alsa]
|
||||
device=spdif
|
||||
@@ -420,135 +424,6 @@ taking screenshots is handled by the VOs, and adding the screenshot filter will
|
||||
break hardware decoding. (The filter may still be useful for taking screenshots
|
||||
at a certain point within the video chain when using multiple video filters.)
|
||||
|
||||
TERMINAL STATUS LINE
|
||||
====================
|
||||
|
||||
During playback, mpv shows the playback status on the terminal. It looks like
|
||||
something like this:
|
||||
|
||||
``AV: 00:03:12 / 00:24:25 (13%) A-V: -0.000``
|
||||
|
||||
The status line can be overridden with the ``--term-status-msg`` option.
|
||||
|
||||
The following is a list of things that can show up in the status line. Input
|
||||
properties, that can be used to get the same information manually, are also
|
||||
listed.
|
||||
|
||||
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
|
||||
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
|
||||
- The total file duration (or ``00:00:00`` if unknown) (``length`` property)
|
||||
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
|
||||
is the user-requested speed, and not the actual speed (usually they should
|
||||
be the same, unless playback is too slow). (``speed`` property.)
|
||||
- Playback percentage, e.g. ``(13%)``. How much of the file has been played.
|
||||
Normally calculated out of playback position and duration, but can fallback
|
||||
to other methods (like byte position) if these are not available.
|
||||
(``percent-pos`` property.)
|
||||
- The audio/video sync as ``A-V: 0.000``. This is the difference between
|
||||
audio and video time. Normally it should be 0 or close to 0. If it's growing,
|
||||
it might indicate a playback problem. (``avsync`` property.)
|
||||
- Total A/V sync change, e.g. ``ct: -0.417``. Normally invisible. Can show up
|
||||
if there is audio "missing", or not enough frames can be dropped. Usually
|
||||
this will indicate a problem. (``total-avsync-change`` property.)
|
||||
- Encoding state in ``{...}``, only shown in encoding mode.
|
||||
- Dropped frames, e.g. ``Dropped: 4``. Shows up only if the count is not 0. Can
|
||||
grow if the video framerate is higher than that of the display, or if video
|
||||
rendering is too slow. Also can be incremented on "hiccups" and when the video
|
||||
frame couldn't be displayed on time. (``vo-drop-frame-count`` property.)
|
||||
If the decoder drops frames, the number of decoder-dropped frames is appended
|
||||
to the display as well, e.g.: ``Dropped: 4/34``. This should almost never
|
||||
happen, unless decoder-framedropping is enabled with one of the
|
||||
``--framedrop`` options, the stream contains errors, or a weird codec is in
|
||||
use. (``drop-frame-count`` property.)
|
||||
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
|
||||
The first value shows the amount of video buffered in the demuxer in seconds,
|
||||
the second value shows *additional* data buffered in the stream cache in
|
||||
kilobytes. (``demuxer-cache-duration`` and ``cached-used`` properties.)
|
||||
|
||||
|
||||
PROTOCOLS
|
||||
=========
|
||||
|
||||
``http://...``, ``https://``, ...
|
||||
Many network protocols are supported, but the protocol prefix must always
|
||||
be specified. mpv will never attempt to guess whether a filename is
|
||||
actually a network address. A protocol prefix is always required.
|
||||
|
||||
Note that not all prefixes are documented here. Undocumented prefixes are
|
||||
either aliases to documented protocols, or are just redirections to
|
||||
protocols implemented and documented in FFmpeg.
|
||||
|
||||
``-``
|
||||
Play data from stdin.
|
||||
|
||||
``smb://PATH``
|
||||
Play a path from Samba share.
|
||||
|
||||
``bd://[title][/device]`` ``--bluray-device=PATH``
|
||||
Play a Blu-Ray disc. Currently, this does not accept ISO files. Instead,
|
||||
you must mount the ISO file as filesystem, and point ``--bluray-device``
|
||||
to the mounted directly.
|
||||
|
||||
``bdnav://[title][/device]``
|
||||
Play a Blu-Ray disc, with navigation features enabled. This feature is
|
||||
permanently experimental.
|
||||
|
||||
``dvd://[title|[starttitle]-endtitle][/device]`` ``--dvd-device=PATH``
|
||||
Play a DVD. If you want dvdnav menus, use ``dvd://menu``. If no title
|
||||
is given, the longest title is auto-selected.
|
||||
|
||||
``dvdnav://`` is an old alias for ``dvd://`` and does exactly the same
|
||||
thing.
|
||||
|
||||
``dvdread://...:``
|
||||
Play a DVD using the old libdvdread code. This is what MPlayer and older
|
||||
mpv versions use for ``dvd://``. Use is discouraged. It's provided only
|
||||
for compatibility and for transition.
|
||||
|
||||
``tv://[channel][/input_id]`` ``--tv-...``
|
||||
Analogue TV via V4L. Also useful for webcams. (Linux only.)
|
||||
|
||||
``pvr://`` ``--pvr-...``
|
||||
PVR. (Linux only.)
|
||||
|
||||
``dvb://[cardnumber@]channel`` ``--dvbin-...``
|
||||
Digital TV via DVB. (Linux only.)
|
||||
|
||||
``mf://[filemask|@listfile]`` ``--mf-...``
|
||||
Play a series of images as video.
|
||||
|
||||
``cdda://track[-endtrack][:speed][/device]`` ``--cdrom-device=PATH`` ``--cdda-...``
|
||||
Play CD.
|
||||
|
||||
``lavf://...``
|
||||
Access any FFmpeg/Libav libavformat protocol. Basically, this passed the
|
||||
string after the ``//`` directly to libavformat.
|
||||
|
||||
``av://type:options``
|
||||
This is intended for using libavdevice inputs. ``type`` is the libavdevice
|
||||
demuxer name, and ``options`` is the (pseudo-)filename passed to the
|
||||
demuxer.
|
||||
|
||||
For example, ``mpv av://lavfi:mandelbrot`` makes use of the libavfilter
|
||||
wrapper included in libavdevice, and will use the ``mandelbrot`` source
|
||||
filter to generate input data.
|
||||
|
||||
``avdevice://`` is an alias.
|
||||
|
||||
``file://PATH``
|
||||
A local path as URL. Might be useful in some special use-cases. Note that
|
||||
``PATH`` itself should start with a third ``/`` to make the path an
|
||||
absolute path.
|
||||
|
||||
``edl://[edl specification as in edl-mpv.rst]``
|
||||
Stitch together parts of multiple files and play them.
|
||||
|
||||
``null://``
|
||||
Simulate an empty file.
|
||||
|
||||
``memory://data``
|
||||
Use the ``data`` part as source data.
|
||||
|
||||
.. include:: options.rst
|
||||
|
||||
.. include:: ao.rst
|
||||
@@ -567,8 +442,6 @@ PROTOCOLS
|
||||
|
||||
.. include:: lua.rst
|
||||
|
||||
.. include:: ipc.rst
|
||||
|
||||
.. include:: changes.rst
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
@@ -577,28 +450,23 @@ ENVIRONMENT VARIABLES
|
||||
There are a number of environment variables that can be used to control the
|
||||
behavior of mpv.
|
||||
|
||||
``HOME``, ``XDG_CONFIG_HOME``
|
||||
Used to determine mpv config directory. If ``XDG_CONFIG_HOME`` is not set,
|
||||
``$HOME/.config/mpv`` is used.
|
||||
|
||||
``$HOME/.mpv`` is always added to the list of config search paths with a
|
||||
lower priority.
|
||||
|
||||
``XDG_CONFIG_DIRS``
|
||||
If set, XDG-style system configuration directories are used. Otherwise,
|
||||
the UNIX convention (``PREFIX/etc/mpv/``) is used.
|
||||
``HOME``
|
||||
Used to determine mpv config directory: ``$HOME/.mpv``
|
||||
|
||||
``TERM``
|
||||
Used to determine terminal type.
|
||||
|
||||
``MPV_HOME``
|
||||
Directory where mpv looks for user settings. Overrides ``HOME``, and mpv
|
||||
will try to load the config file as ``$MPV_HOME/mpv.conf``.
|
||||
will try to load the config file as ``$MPV_HOME/config``.
|
||||
|
||||
``MPV_VERBOSE`` (see also ``-v`` and ``--msg-level``)
|
||||
``MPV_LOCALEDIR``
|
||||
Directory where mpv looks for gettext translation files (if enabled).
|
||||
|
||||
``MPV_VERBOSE`` (see also ``-v`` and ``--msglevel``)
|
||||
Set the initial verbosity level across all message modules (default: 0).
|
||||
This is an integer, and the resulting verbosity corresponds to the number
|
||||
of ``--v`` options passed to the command line.
|
||||
The resulting verbosity corresponds to that of ``--msglevel=5`` plus the
|
||||
value of ``MPV_VERBOSE``.
|
||||
|
||||
``MPV_LEAK_REPORT``
|
||||
If set to ``1``, enable internal talloc leak reporting. Note that this can
|
||||
@@ -706,56 +574,12 @@ FILES
|
||||
``/usr/local/etc/mpv/mpv.conf``
|
||||
mpv system-wide settings (depends on ``--prefix`` passed to configure)
|
||||
|
||||
``~/.config/mpv/mpv.conf``
|
||||
``~/.mpv/config``
|
||||
mpv user settings
|
||||
|
||||
``~/.config/mpv/input.conf``
|
||||
key bindings (see `INPUT.CONF`_ section)
|
||||
``~/.mpv/input.conf``
|
||||
input bindings (see ``--input-keylist`` for the full list)
|
||||
|
||||
``~/.config/mpv/lua/``
|
||||
All files in this directly are loaded as if they were passed to the
|
||||
``--lua`` option. They are loaded in alphabetical order, and sub-directories
|
||||
and files with no ``.lua`` extension are ignored. The ``--load-scripts=no``
|
||||
option disables loading these files.
|
||||
|
||||
Note that the environment variables ``$XDG_CONFIG_HOME`` and ``$MPV_HOME`` can
|
||||
override the standard directory ``~/.config/mpv/``.
|
||||
|
||||
Also, the old config location at ``~/.mpv/`` is still read, and if the XDG
|
||||
variant does not exist, will still be preferred.
|
||||
|
||||
FILES ON WINDOWS
|
||||
================
|
||||
|
||||
On win32 (if compiled with MinGW, but not Cygwin), the default config file
|
||||
locations are different:
|
||||
|
||||
``%APPDATA%/mpv/mpv.conf``
|
||||
Preferred mpv config file. This maps to a system and user-specific path,
|
||||
for example ``C:\users\USERNAME\Application Data\mpv\mpv.conf``. You can
|
||||
find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.exe.
|
||||
|
||||
``%APPDATA%/mpv/input.conf``
|
||||
key bindings (see `INPUT.CONF`_ section)
|
||||
|
||||
``%APPDATA%/mpv/lua/``
|
||||
equivalent of ``~/.config/mpv/lua/`` on Unix.
|
||||
|
||||
The environment variable ``$MPV_HOME`` completely overrides these, like on
|
||||
UNIX.
|
||||
|
||||
Config files located in the same directory as ``mpv.exe`` are loaded with
|
||||
lower priority. Some config files are loaded only once, which means that
|
||||
e.g. of 2 ``input.conf`` files located in two config directories, only the
|
||||
one from the directory with higher priority will be loaded.
|
||||
|
||||
A third config directory with lowest priority is the directory named ``mpv``
|
||||
in the same directory as ``mpv.exe``. This used to be the directory with
|
||||
highest priority, but is now discouraged to use and might be removed in the
|
||||
future.
|
||||
|
||||
Note that mpv likes to mix ``/`` and ``\`` path separators for simplicity.
|
||||
kernel32.dll accepts this, but cmd.exe does not.
|
||||
|
||||
EXAMPLES OF MPV USAGE
|
||||
=====================
|
||||
@@ -773,8 +597,8 @@ Play only chapters 5, 6, 7:
|
||||
Play only titles 5, 6, 7:
|
||||
``mpv dvd://5-7``
|
||||
|
||||
Play a multi-angle DVD:
|
||||
``mpv dvd://1 --dvd-angle=2``
|
||||
Play a multiangle DVD:
|
||||
``mpv dvd://1 --dvdangle=2``
|
||||
|
||||
Play from a different DVD device:
|
||||
``mpv dvd://1 --dvd-device=/dev/dvd2``
|
||||
File diff suppressed because it is too large.
Load diff
@@ -6,7 +6,7 @@ offer basic mouse-controllability. It is intended to make interaction easier
|
||||
for new users and to enable precise and direct seeking.
|
||||
|
||||
The OSC is enabled by default if mpv was compiled with lua support. It can be
|
||||
disabled entirely using the ``--osc=no`` option.
|
||||
disabled using ``--osc=no``.
|
||||
|
||||
Using the OSC
|
||||
-------------
|
||||
@@ -113,7 +113,7 @@ time passed
|
||||
============= ================================================
|
||||
|
||||
cache status
|
||||
| Shows current cache fill status (only visible when below 45%)
|
||||
| Shows current cache fill status (only visible when below 48%)
|
||||
|
||||
time remaining
|
||||
| Shows remaining playback time timestamp
|
||||
@@ -122,24 +122,12 @@ time remaining
|
||||
left-click toggle between total and remaining time
|
||||
============= ================================================
|
||||
|
||||
Key Bindings
|
||||
~~~~~~~~~~~~
|
||||
|
||||
These key bindings are active by default if nothing else is already bound to
|
||||
these keys. In case of collision, the function needs to be bound to a
|
||||
different key. See `Script Commands`_ section.
|
||||
|
||||
============= ================================================
|
||||
del Hide the OSC permanently until mpv is restarted.
|
||||
============= ================================================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The OSC offers limited configuration through a config file
|
||||
``lua-settings/osc.conf`` placed in mpv's user dir and through the
|
||||
``--lua-opts`` command-line option. Options provided through the command-line
|
||||
will override those from the config file.
|
||||
The OSC offers limited configuration through a config file ``plugin_osc.conf``
|
||||
placed in mpv's user dir.
|
||||
|
||||
Config Syntax
|
||||
~~~~~~~~~~~~~
|
||||
@@ -147,25 +135,14 @@ Config Syntax
|
||||
The config file must exactly follow the following syntax::
|
||||
|
||||
# this is a comment
|
||||
optionA=value1
|
||||
optionB=value2
|
||||
parameter1=value1
|
||||
parameter2=value2
|
||||
|
||||
``#`` can only be used at the beginning of a line and there may be no
|
||||
spaces around the ``=`` or anywhere else.
|
||||
|
||||
Command-line Syntax
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To avoid collisions with other scripts, all options need to be prefixed with
|
||||
``osc-``.
|
||||
|
||||
Example::
|
||||
|
||||
--lua-opts=osc-optionA=value1,osc-optionB=value2
|
||||
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Configurable parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``showwindowed``
|
||||
| Default: yes
|
||||
@@ -199,10 +176,6 @@ Configurable Options
|
||||
| Default: 0
|
||||
| Horizontal alignment, -1 (left) to 1 (right)
|
||||
|
||||
``boxalpha``
|
||||
| Default: 80
|
||||
| Alpha of the background box, 0 (opaque) to 255 (fully transparent)
|
||||
|
||||
``hidetimeout``
|
||||
| Default: 500
|
||||
| Duration in ms until the OSC hides if no mouse movement, negative value
|
||||
@@ -216,41 +189,10 @@ Configurable Options
|
||||
| Default: 0
|
||||
| Size of the deadzone. The deadzone is an area that makes the mouse act
|
||||
like leaving the window. Movement there won't make the OSC show up and
|
||||
it will hide immediately if the mouse enters it. The deadzone starts
|
||||
at the window border opposite to the OSC and the size controls how much
|
||||
of the window it will span. Values between 0 and 1.
|
||||
it will hide immediately if the mouse enters it.
|
||||
|
||||
``minmousemove``
|
||||
| Default: 3
|
||||
| Minimum amount of pixels the mouse has to move between ticks to make
|
||||
the OSC show up
|
||||
|
||||
``seektooltip``
|
||||
| Default: yes
|
||||
| Display a tooltip over the seekbar indicating time at mouse position.
|
||||
|
||||
``seekbarstyle``
|
||||
| Default: slider
|
||||
| Sets the style of the seekbar, slider (diamond marker) or bar (fill)
|
||||
|
||||
Script Commands
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The OSC script listens to certain script commands. These commands can bound
|
||||
in ``input.conf``, or sent by other scripts.
|
||||
|
||||
``enable-osc``
|
||||
Undoes ``disable-osc`` or the effect of the ``del`` key.
|
||||
|
||||
``disable-osc``
|
||||
Hide the OSC permanently. This is also what the ``del`` key does.
|
||||
|
||||
|
||||
Example
|
||||
|
||||
You could put this into ``input.conf`` to hide the OSC with the ``a`` key and
|
||||
to unhide it with ``b``::
|
||||
|
||||
a script_message disable-osc
|
||||
b script_message enable-osc
|
||||
|
||||
@@ -7,11 +7,10 @@ syntax is:
|
||||
``--vf=<filter1[=parameter1:parameter2:...],filter2,...>``
|
||||
Setup a chain of video filters.
|
||||
|
||||
You can also set defaults for each filter. The defaults are applied before the
|
||||
normal filter parameters.
|
||||
|
||||
``--vf-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
|
||||
Set defaults for each filter.
|
||||
Many parameters are optional and set to default values if omitted. To
|
||||
explicitly use a default value, set a parameter to '-1'. Parameters ``w:h``
|
||||
means width x height in pixels, ``x:y`` means x;y position counted from the
|
||||
upper left corner of the bigger image.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -47,7 +46,7 @@ Available filters are:
|
||||
|
||||
``crop[=w:h:x:y]``
|
||||
Crops the given part of the image and discards the rest. Useful to remove
|
||||
black bands from widescreen videos.
|
||||
black bands from widescreen movies.
|
||||
|
||||
``<w>,<h>``
|
||||
Cropped width and height, defaults to original width and height.
|
||||
@@ -55,7 +54,7 @@ Available filters are:
|
||||
Position of the cropped picture, defaults to center.
|
||||
|
||||
``expand[=w:h:x:y:aspect:round]``
|
||||
Expands (not scales) video resolution to the given value and places the
|
||||
Expands (not scales) movie resolution to the given value and places the
|
||||
unscaled original at coordinates x, y.
|
||||
|
||||
``<w>,<h>``
|
||||
@@ -75,7 +74,7 @@ Available filters are:
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``expand=800::::4/3``
|
||||
``expand=800:::::4/3``
|
||||
Expands to 800x600, unless the source is higher resolution, in
|
||||
which case it expands to fill a 4/3 aspect.
|
||||
|
||||
@@ -83,19 +82,24 @@ Available filters are:
|
||||
Rounds up to make both width and height divisible by <r> (default: 1).
|
||||
|
||||
``flip``
|
||||
Flips the image upside down.
|
||||
Flips the image upside down. See also ``--flip``.
|
||||
|
||||
``mirror``
|
||||
Mirrors the image on the Y axis.
|
||||
|
||||
``rotate[=0|90|180|270]``
|
||||
Rotates the image by a multiple of 90 degrees clock-wise.
|
||||
``rotate[=<0-7>]``
|
||||
Rotates the image by 90 degrees and optionally flips it. For values
|
||||
between 4-7 rotation is only done if the movie geometry is portrait and
|
||||
not landscape.
|
||||
|
||||
``scale[=w:h:param:param2:chr-drop:noup:arnd``
|
||||
:0: Rotate by 90 degrees clockwise and flip (default).
|
||||
:1: Rotate by 90 degrees clockwise.
|
||||
:2: Rotate by 90 degrees counterclockwise.
|
||||
:3: Rotate by 90 degrees counterclockwise and flip.
|
||||
|
||||
``scale[=w:h[:interlaced[:chr_drop[:par[:par2[:noup[:arnd]]]]]]]``
|
||||
Scales the image with the software scaler (slow) and performs a YUV<->RGB
|
||||
color space conversion (see also ``--sws``).
|
||||
|
||||
All parameters are optional.
|
||||
colorspace conversion (see also ``--sws``).
|
||||
|
||||
``<w>,<h>``
|
||||
scaled width/height (default: original width/height)
|
||||
@@ -109,7 +113,15 @@ Available filters are:
|
||||
:-(n+8): Like -n above, but rounding the dimension to the closest
|
||||
multiple of 16.
|
||||
|
||||
``<param>[:<param2>]`` (see also ``--sws``)
|
||||
``<chr_drop>``
|
||||
chroma skipping
|
||||
|
||||
:0: Use all available input lines for chroma.
|
||||
:1: Use only every 2. input line for chroma.
|
||||
:2: Use only every 4. input line for chroma.
|
||||
:3: Use only every 8. input line for chroma.
|
||||
|
||||
``<par>[:<par2>]`` (see also ``--sws``)
|
||||
Set some scaling parameters depending on the type of scaler selected
|
||||
with ``--sws``::
|
||||
|
||||
@@ -120,17 +132,9 @@ Available filters are:
|
||||
0.33:0.33 Mitchell-Netravali spline
|
||||
1.00:0.00 cubic B-spline
|
||||
|
||||
--sws=7 (Gaussian): sharpness (0 (soft) - 100 (sharp))
|
||||
--sws=7 (gaussian): sharpness (0 (soft) - 100 (sharp))
|
||||
|
||||
--sws=9 (Lanczos): filter length (1-10)
|
||||
|
||||
``<chr-drop>``
|
||||
chroma skipping
|
||||
|
||||
:0: Use all available input lines for chroma (default).
|
||||
:1: Use only every 2. input line for chroma.
|
||||
:2: Use only every 4. input line for chroma.
|
||||
:3: Use only every 8. input line for chroma.
|
||||
--sws=9 (lanczos): filter length (1-10)
|
||||
|
||||
``<noup>``
|
||||
Disallow upscaling past the original dimensions.
|
||||
@@ -146,7 +150,7 @@ Available filters are:
|
||||
:0: Disable accurate rounding (default).
|
||||
:1: Enable accurate rounding.
|
||||
|
||||
``dsize[=w:h:aspect-method:r:aspect]``
|
||||
``dsize[=aspect|w:h:aspect-method:r]``
|
||||
Changes the intended display size/aspect at an arbitrary point in the
|
||||
filter chain. Aspect can be given as a fraction (4/3) or floating point
|
||||
number (1.33). Alternatively, you may specify the exact display width and
|
||||
@@ -195,9 +199,6 @@ Available filters are:
|
||||
Rounds up to make both width and height divisible by ``<r>``
|
||||
(default: 1).
|
||||
|
||||
``<aspect>``
|
||||
Force an aspect ratio.
|
||||
|
||||
``format[=fmt[:outfmt]]``
|
||||
Restricts the color space for the next filter without doing any conversion.
|
||||
Use together with the scale filter for a real conversion.
|
||||
@@ -222,8 +223,8 @@ Available filters are:
|
||||
====================== =====================
|
||||
|
||||
``noformat[=fmt]``
|
||||
Restricts the color space for the next filter without doing any conversion.
|
||||
Unlike the format filter, this will allow any color space except the one
|
||||
Restricts the colorspace for the next filter without doing any conversion.
|
||||
Unlike the format filter, this will allow any colorspace except the one
|
||||
you specify.
|
||||
|
||||
.. note:: For a list of available formats, see ``noformat=fmt=help``.
|
||||
@@ -231,23 +232,12 @@ Available filters are:
|
||||
``<fmt>``
|
||||
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
|
||||
|
||||
``pp=[filter string]``
|
||||
Enables the specified chain of post-processing subfilters. Subfilters must
|
||||
``pp[=filter1[:option1[:option2...]]/[-]filter2...]``
|
||||
Enables the specified chain of postprocessing subfilters. Subfilters must
|
||||
be separated by '/' and can be disabled by prepending a '-'. Each
|
||||
subfilter and some options have a short and a long name that can be used
|
||||
interchangeably, i.e. ``dr``/``dering`` are the same.
|
||||
|
||||
.. note::
|
||||
|
||||
Unlike in MPlayer or in earlier versions, you must quote the pp string
|
||||
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
This filter is most likely useless on modern HD video. It might be
|
||||
helpful with old low-resolution files.
|
||||
|
||||
All subfilters share common options to determine their scope:
|
||||
interchangeably, i.e. ``dr``/``dering`` are the same. All subfilters share
|
||||
common options to determine their scope:
|
||||
|
||||
``a/autoq``
|
||||
Automatically switch the subfilter off if the CPU is too slow.
|
||||
@@ -260,7 +250,7 @@ Available filters are:
|
||||
|
||||
.. note::
|
||||
|
||||
``--vf=pp:help`` shows a list of available subfilters.
|
||||
``--pphelp`` shows a list of available subfilters.
|
||||
|
||||
Available subfilters are:
|
||||
|
||||
@@ -342,7 +332,7 @@ Available filters are:
|
||||
filtering every second line with a (-1 4 2 4 -1) filter.
|
||||
|
||||
``l5/lowpass5``
|
||||
Vertically applied FIR low-pass deinterlacing filter that deinterlaces
|
||||
Vertically applied FIR lowpass deinterlacing filter that deinterlaces
|
||||
the given block by filtering all lines with a (-1 2 6 2 -1) filter.
|
||||
|
||||
``fq/forceQuant[:quantizer]``
|
||||
@@ -362,7 +352,7 @@ Available filters are:
|
||||
|
||||
.. note::
|
||||
|
||||
This filter is only available if FFmpeg/Libav has been compiled
|
||||
This filter is only available if FFmpeg/libav has been compiled
|
||||
with libpostproc enabled.
|
||||
|
||||
.. admonition:: Examples
|
||||
@@ -374,14 +364,14 @@ Available filters are:
|
||||
``--vf=pp=de/-al``
|
||||
default filters without brightness/contrast correction
|
||||
|
||||
``--vf=pp=[default/tmpnoise:1:2:3]``
|
||||
``--vf=pp=default/tmpnoise:1:2:3``
|
||||
Enable default filters & temporal denoiser.
|
||||
|
||||
``--vf=pp=[hb:y/vb:a]``
|
||||
``--vf=pp=hb:y/vb:a``
|
||||
Horizontal deblocking on luminance only, and switch vertical
|
||||
deblocking on or off automatically depending on available CPU time.
|
||||
|
||||
``lavfi=graph[:sws-flags[:o=opts]]``
|
||||
``lavfi=graph[:sws_flags[:o=opts]]``
|
||||
Filter video using FFmpeg's libavfilter.
|
||||
|
||||
``<graph>``
|
||||
@@ -415,7 +405,7 @@ Available filters are:
|
||||
``'--vf=lavfi=graph="gradfun=radius=30:strength=20,vflip"'``
|
||||
Same as before, but uses named parameters for everything.
|
||||
|
||||
``<sws-flags>``
|
||||
``<sws_flags>``
|
||||
If libavfilter inserts filters for pixel format conversion, this
|
||||
option gives the flags which should be passed to libswscale. This
|
||||
option is numeric and takes a bit-wise combination of ``SWS_`` flags.
|
||||
@@ -430,30 +420,21 @@ Available filters are:
|
||||
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
|
||||
forces a specific threading configuration.
|
||||
|
||||
``noise[=<strength>[:averaged][:pattern][:temporal][:uniform][:hq]``
|
||||
``noise[=luma[u][t|a][h][p]:chroma[u][t|a][h][p]]``
|
||||
Adds noise.
|
||||
|
||||
``strength``
|
||||
Set the noise for all components. If you want different strength
|
||||
values for luma and chroma, use libavfilter's noise filter directly
|
||||
(using ``--vf=lavfi=[noise=...]``), or tell the libavfilter developers
|
||||
to stop being stupid.
|
||||
:<0-100>: luma noise
|
||||
:<0-100>: chroma noise
|
||||
:u: uniform noise (gaussian otherwise)
|
||||
:t: temporal noise (noise pattern changes between frames)
|
||||
:a: averaged temporal noise (smoother, but a lot slower)
|
||||
:h: high quality (slightly better looking, slightly slower)
|
||||
:p: mix random noise with a (semi)regular pattern
|
||||
|
||||
``averaged``
|
||||
averaged temporal noise (smoother, but a lot slower)
|
||||
.. note::
|
||||
|
||||
``pattern``
|
||||
mix random noise with a (semi)regular pattern
|
||||
|
||||
``temporal``
|
||||
temporal noise (noise pattern changes between frames)
|
||||
|
||||
``uniform``
|
||||
uniform noise (Gaussian otherwise)
|
||||
|
||||
``hq``
|
||||
high quality (slightly better looking, slightly slower) - not available
|
||||
when using libavfilter
|
||||
Deprecated. Use libavfilter's ``noise`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp]``
|
||||
This filter aims to reduce image noise producing smooth images and making
|
||||
@@ -469,6 +450,11 @@ Available filters are:
|
||||
chroma temporal strength (default:
|
||||
``luma_tmp*chroma_spatial/luma_spatial``)
|
||||
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``hqdn3d`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``eq[=gamma:contrast:brightness:saturation:rg:gg:bg:weight]``
|
||||
Software equalizer that uses lookup tables (slow), allowing gamma correction
|
||||
in addition to simple brightness and contrast adjustment. The parameters are
|
||||
@@ -508,26 +494,39 @@ Available filters are:
|
||||
:0: nearest-neighbor sampling, fast but incorrect
|
||||
:1: linear interpolation (default)
|
||||
|
||||
``unsharp[=lx:ly:la:cx:cy:ca]``
|
||||
unsharp mask / Gaussian blur
|
||||
``unsharp[=l|cWxH:amount[:l|cWxH:amount]]``
|
||||
unsharp mask / gaussian blur
|
||||
|
||||
``l`` is for the luma component, ``c`` for the chroma component. ``x``/``y``
|
||||
is the filter size. ``a`` is the amount.
|
||||
``l``
|
||||
Apply effect on luma component.
|
||||
|
||||
``lx``, ``ly``, ``cx``, ``cy``
|
||||
``c``
|
||||
Apply effect on chroma components.
|
||||
|
||||
``<width>x<height>``
|
||||
width and height of the matrix, odd sized in both directions (min =
|
||||
3:3, max = 13:11 or 11:13, usually something between 3:3 and 7:7)
|
||||
3x3, max = 13x11 or 11x13, usually something between 3x3 and 7x7)
|
||||
|
||||
``la``, ``ca``
|
||||
``amount``
|
||||
Relative amount of sharpness/blur to add to the image (a sane range
|
||||
should be -1.5-1.5).
|
||||
|
||||
:<0: blur
|
||||
:>0: sharpen
|
||||
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``unsharp`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``swapuv``
|
||||
Swap U & V plane.
|
||||
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``swapuv`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``pullup[=jl:jr:jt:jb:sb:mp]``
|
||||
Pulldown reversal (inverse telecine) filter, capable of handling mixed
|
||||
hard-telecine, 24000/1001 fps progressive, and 30000/1001 fps progressive
|
||||
@@ -552,7 +551,7 @@ Available filters are:
|
||||
frames in the output.
|
||||
|
||||
``mp`` (metric plane)
|
||||
This option may be set to ``u`` or ``v`` to use a chroma plane instead of the
|
||||
This option may be set to 1 or 2 to use a chroma plane instead of the
|
||||
luma plane for doing ``pullup``'s computations. This may improve accuracy
|
||||
on very clean source material, but more likely will decrease accuracy,
|
||||
especially if there is chroma noise (rainbow effect) or any grayscale
|
||||
@@ -623,7 +622,7 @@ Available filters are:
|
||||
|
||||
``phase[=t|b|p|a|u|T|B|A|U][:v]``
|
||||
Delay interlaced video by one field time so that the field order changes.
|
||||
The intended use is to fix PAL videos that have been captured with the
|
||||
The intended use is to fix PAL movies that have been captured with the
|
||||
opposite field order to the film-to-video transfer. The options are:
|
||||
|
||||
``t``
|
||||
@@ -671,36 +670,41 @@ Available filters are:
|
||||
``v``
|
||||
Verbose operation. Prints the selected mode for each frame and the
|
||||
average squared difference between fields for ``t``, ``b``, and ``p``
|
||||
alternatives. (Ignored when libavfilter is used.)
|
||||
alternatives.
|
||||
|
||||
``yadif=[mode[:enabled=yes|no]]``
|
||||
Yet another deinterlacing filter
|
||||
|
||||
``<mode>``
|
||||
:frame: Output 1 frame for each frame.
|
||||
:field: Output 1 frame for each field.
|
||||
:frame-nospatial: Like ``frame`` but skips spatial interlacing check.
|
||||
:field-nospatial: Like ``field`` but skips spatial interlacing check.
|
||||
:0: Output 1 frame for each frame.
|
||||
:1: Output 1 frame for each field.
|
||||
:2: Like 0 but skips spatial interlacing check.
|
||||
:3: Like 1 but skips spatial interlacing check.
|
||||
|
||||
``<enabled>``
|
||||
:yes: Filter is active (default).
|
||||
:no: Filter is not active, but can be activated with the ``D`` key
|
||||
(or any other key that toggles the ``deinterlace`` property).
|
||||
|
||||
This filter, is automatically inserted when using the ``D`` key (or any
|
||||
other key that toggles the ``deinterlace`` property or when using the
|
||||
``--deinterlace`` switch), assuming the video output does not have native
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``yadif`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
This filter, or libavfilter's implementation if available, is automatically
|
||||
inserted when using the ``D`` key (or any other key that toggles the
|
||||
``deinterlace`` property), assuming the video output does not have native
|
||||
deinterlacing support.
|
||||
|
||||
If you just want to set the default mode, put this filter and its options
|
||||
into ``--vf-defaults`` instead, and enable deinterlacing with ``D`` or
|
||||
``--deinterlace``.
|
||||
``down3dright[=lines]``
|
||||
Reposition and resize stereoscopic images. Extracts both stereo fields and
|
||||
places them side by side, resizing them to maintain the original movie
|
||||
aspect.
|
||||
|
||||
Also note that the ``D`` key is stupid enough to insert an interlacer twice
|
||||
when inserting yadif with ``--vf``, so using the above methods is
|
||||
recommended.
|
||||
``<lines>``
|
||||
number of lines to select from the middle of the image (default: 12)
|
||||
|
||||
``delogo[=x:y:w:h:t:show]``
|
||||
``delogo[=x:y:w:h:t]``
|
||||
Suppresses a TV station logo by a simple interpolation of the surrounding
|
||||
pixels. Just set a rectangle covering the logo and watch it disappear (and
|
||||
sometimes something even uglier appear - your mileage may vary).
|
||||
@@ -713,8 +717,15 @@ Available filters are:
|
||||
Thickness of the fuzzy edge of the rectangle (added to ``w`` and
|
||||
``h``). When set to -1, a green rectangle is drawn on the screen to
|
||||
simplify finding the right ``x``,``y``,``w``,``h`` parameters.
|
||||
``show``
|
||||
Draw a rectangle showing the area defined by x/y/w/h.
|
||||
``file=<file>``
|
||||
You can specify a text file to load the coordinates from. Each line
|
||||
must have a timestamp (in seconds, and in ascending order) and the
|
||||
``x:y:w:h:t`` coordinates (``t`` can be omitted).
|
||||
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``delogo`` or ``removelogo`` filters
|
||||
through ``--vf=lavfi`` instead.
|
||||
|
||||
``screenshot``
|
||||
Optional filter for screenshot support. This is only needed if the video
|
||||
@@ -775,8 +786,8 @@ Available filters are:
|
||||
anaglyph red/cyan color (red filter on left eye, cyan filter on
|
||||
right eye)
|
||||
``arcd`` or ``anaglyph_red_cyan_dubois``
|
||||
anaglyph red/cyan color optimized with the least-squares
|
||||
projection of Dubois (red filter on left eye, cyan filter on right
|
||||
anaglyph red/cyan color optimized with the least squares
|
||||
projection of dubois (red filter on left eye, cyan filter on right
|
||||
eye)
|
||||
``agmg`` or ``anaglyph_green_magenta_gray``
|
||||
anaglyph green/magenta gray (green filter on left eye, magenta
|
||||
@@ -807,24 +818,29 @@ Available filters are:
|
||||
``mr`` or ``mono_right``
|
||||
mono output (right eye only)
|
||||
|
||||
``gradfun[=strength[:radius|:size=<size>]]``
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``stereo3d`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``gradfun[=strength[:radius]]``
|
||||
Fix the banding artifacts that are sometimes introduced into nearly flat
|
||||
regions by truncation to 8-bit color depth. Interpolates the gradients that
|
||||
regions by truncation to 8bit color depth. Interpolates the gradients that
|
||||
should go where the bands are, and dithers them.
|
||||
|
||||
``<strength>``
|
||||
Maximum amount by which the filter will change any one pixel. Also the
|
||||
threshold for detecting nearly flat regions (default: 1.5).
|
||||
threshold for detecting nearly flat regions (default: 1.2).
|
||||
|
||||
``<radius>``
|
||||
Neighborhood to fit the gradient to. Larger radius makes for smoother
|
||||
gradients, but also prevents the filter from modifying pixels near
|
||||
detailed regions (default: disabled).
|
||||
detailed regions (default: 16).
|
||||
|
||||
``<size>``
|
||||
size of the filter in percent of the image diagonal size. This is
|
||||
used to calculate the final radius size (default: 1).
|
||||
.. note::
|
||||
|
||||
Deprecated. Use libavfilter's ``gradfun`` filter through ``--vf=lavfi``
|
||||
instead.
|
||||
|
||||
``dlopen=dll[:a0[:a1[:a2[:a3]]]]``
|
||||
Loads an external library to filter the image. The library interface
|
||||
@@ -846,100 +862,11 @@ Available filters are:
|
||||
``a3=<string>``
|
||||
Specify the fourth parameter to pass to the library.
|
||||
|
||||
``vapoursynth=file:buffered-frames:concurrent-frames``
|
||||
Loads a VapourSynth filter script. This is intended for streamed
|
||||
processing: mpv actually provides a source filter, instead of using a
|
||||
native VapourSynth video source. The mpv source will answer frame
|
||||
requests only within a small window of frames (the size of this window
|
||||
is controlled with the ``buffered-frames`` parameter), and requests outside
|
||||
of that will return errors. As such, you can't use the full power of
|
||||
VapourSynth, but you can use certain filters.
|
||||
|
||||
If you just want to play video generated by a VapourSynth (i.e. using
|
||||
a native VapourSynth video source), it's better to use ``vspipe`` and a
|
||||
FIFO to feed the video to mpv. The same applies if the filter script
|
||||
requires random frame access (see ``buffered-frames`` parameter).
|
||||
|
||||
This filter is experimental. If it turns out that it works well and is
|
||||
used, it will be ported to libavfilter. Otherwise, it will be just removed.
|
||||
|
||||
``file``
|
||||
Filename of the script source. Currently, this is always a python
|
||||
script. The variable ``video_in`` is set to the mpv video source,
|
||||
and it is expected that the script reads video from it. (Otherwise,
|
||||
mpv will decode no video, and the video packet queue will overflow,
|
||||
eventually leading to audio being stopped.) The script is also
|
||||
expected to pass through timestamps using the ``_DurationNum`` and
|
||||
``_DurationDen`` frame properties.
|
||||
|
||||
.. admonition:: Example:
|
||||
|
||||
::
|
||||
|
||||
import vapoursynth as vs
|
||||
core = vs.get_core()
|
||||
core.std.AddBorders(video_in, 10, 10, 20, 20).set_output()
|
||||
|
||||
.. warning::
|
||||
|
||||
The script will be reloaded on every seek. This is done to reset
|
||||
the filter properly on discontinuities.
|
||||
|
||||
``buffered-frames``
|
||||
Maximum number of decoded video frames that should be buffered before
|
||||
the filter (default: 4). This specifies the maximum number of frames
|
||||
the script can requests backwards. E.g. if ``buffered-frames=5``, and
|
||||
the script just requested frame 15, it can still request frame 10, but
|
||||
frame 9 is not available anymore. If it requests frame 30, mpv will
|
||||
decode 15 more frames, and keep only frames 25-30.
|
||||
|
||||
The actual number of buffered frames also depends on the value of the
|
||||
``concurrent-frames`` option. Currently, both option values are
|
||||
multiplied to get the final buffer size.
|
||||
|
||||
(Normally, VapourSynth source filters must provide random access, but
|
||||
mpv was made for playback, and does not provide frame-exact random
|
||||
access. The way this video filter works is a compromise to make simple
|
||||
filters work anyway.)
|
||||
|
||||
``concurrent-frames``
|
||||
Number of frames that should be requested in parallel (default: 2). The
|
||||
level of concurrency depends on the filter and how quickly mpv can
|
||||
decode video to feed the filter. This value should probably be
|
||||
proportional to the number of cores on your machine. Most time,
|
||||
making it higher than the number of cores can actually make it
|
||||
slower.
|
||||
|
||||
``vapoursynth-lazy``
|
||||
The same as ``vapoursynth``, but doesn't load Python scripts. Instead, a
|
||||
custom backend using Lua and the raw VapourSynth API is used. The syntax
|
||||
is completely different, and absolutely no conveniencve features are
|
||||
provided. There's no type checking either, and you can trigger crashes.
|
||||
|
||||
.. admonition:: Example:
|
||||
|
||||
::
|
||||
|
||||
video_out = invoke("morpho", "Open", {clip = video_in})
|
||||
|
||||
The special variable ``video_in`` is the mpv video source, while the
|
||||
special variable ``video_out`` is used to read video from. The 1st argument
|
||||
is the plugin (queried with ``getPluginByNs``), the 2nd is the filter name,
|
||||
and the 3rd argument is a table with the arguments. Positional arguments
|
||||
are not supported. The types must match exactly. Since Lua is terrible and
|
||||
can't distinguish integers and floats, integer arguments must be prefixed
|
||||
with ``i_``, in which case the prefix is removed and the argument is cast
|
||||
to an integer. Should the argument's name start with ``i_``, you're out of
|
||||
luck.
|
||||
|
||||
Clips (VSNodeRef) are passed as light userdata, so trying to pass any
|
||||
other userdata type will result in hard crashes.
|
||||
|
||||
``vavpp``
|
||||
VA-AP-API video post processing. Works with ``--vo=vaapi`` and ``--vo=opengl``
|
||||
only. Currently deinterlaces. This filter is automatically inserted if
|
||||
deinterlacing is requested (either using the ``D`` key, by default mapped to
|
||||
the command ``cycle deinterlace``, or the ``--deinterlace`` option).
|
||||
VA-API video post processing. Works with ``--vo=vaapi`` only. Currently
|
||||
deinterlaces. This filter is automatically inserted if deinterlacing is
|
||||
requested (either using the ``D`` key, by default mapped to the command
|
||||
``cycle deinterlace``, or the ``--deinterlace`` option).
|
||||
|
||||
``deint=<method>``
|
||||
Select the deinterlacing algorithm.
|
||||
@@ -951,58 +878,3 @@ Available filters are:
|
||||
bob
|
||||
bob deinterlacing (default).
|
||||
|
||||
``vdpaupp``
|
||||
VDPAU video post processing. Works with ``--vo=vdpau`` and ``--vo=opengl``
|
||||
only. This filter is automatically inserted if deinterlacing is requested
|
||||
(either using the ``D`` key, by default mapped to the command
|
||||
``cycle deinterlace``, or the ``--deinterlace`` option). When enabling
|
||||
deinterlacing, it is always preferred over software deinterlacer filters
|
||||
if the ``vdpau`` VO is used, and also if ``opengl`` is used and hardware
|
||||
decoding was activated at least once (i.e. vdpau was loaded).
|
||||
|
||||
``sharpen=<-1-1>``
|
||||
For positive values, apply a sharpening algorithm to the video, for
|
||||
negative values a blurring algorithm (default: 0).
|
||||
``denoise=<0-1>``
|
||||
Apply a noise reduction algorithm to the video (default: 0; no noise
|
||||
reduction).
|
||||
``deint=<yes|no>``
|
||||
Whether deinterlacing is enabled (default: no). If enabled, it will use
|
||||
the mode selected with ``deint-mode``.
|
||||
``deint-mode=<first-field|bob|temporal|temporal-spatial>``
|
||||
Select deinterlacing mode (default: temporal).
|
||||
All modes respect ``--field-dominance``.
|
||||
|
||||
Note that there's currently a mechanism that allows the ``vdpau`` VO to
|
||||
change the ``deint-mode`` of auto-inserted ``vdpaupp`` filters. To avoid
|
||||
confusion, it's recommended not to use the ``--vo=vdpau`` suboptions
|
||||
related to filtering.
|
||||
|
||||
first-field
|
||||
Show only first field.
|
||||
bob
|
||||
Bob deinterlacing.
|
||||
temporal
|
||||
Motion-adaptive temporal deinterlacing. May lead to A/V desync
|
||||
with slow video hardware and/or high resolution.
|
||||
temporal-spatial
|
||||
Motion-adaptive temporal deinterlacing with edge-guided spatial
|
||||
interpolation. Needs fast video hardware.
|
||||
``chroma-deint``
|
||||
Makes temporal deinterlacers operate both on luma and chroma (default).
|
||||
Use no-chroma-deint to solely use luma and speed up advanced
|
||||
deinterlacing. Useful with slow video memory.
|
||||
``pullup``
|
||||
Try to apply inverse telecine, needs motion adaptive temporal
|
||||
deinterlacing.
|
||||
``hqscaling=<0-9>``
|
||||
0
|
||||
Use default VDPAU scaling (default).
|
||||
1-9
|
||||
Apply high quality VDPAU scaling (needs capable hardware).
|
||||
|
||||
``buffer=<num>``
|
||||
Buffer ``<num>`` frames in the filter chain. This filter is probably pretty
|
||||
useless, except for debugging. (Note that this won't help smoothing out
|
||||
latencies with decoding, because the filter will never output a frame if
|
||||
the buffer isn't full, except on EOF.)
|
||||
@@ -10,19 +10,10 @@ syntax is:
|
||||
If the list has a trailing ',', mpv will fall back on drivers not contained
|
||||
in the list. Suboptions are optional and can mostly be omitted.
|
||||
|
||||
You can also set defaults for each driver. The defaults are applied before the
|
||||
normal driver parameters.
|
||||
|
||||
``--vo-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
|
||||
Set defaults for each driver.
|
||||
|
||||
.. note::
|
||||
|
||||
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,``
|
||||
@@ -35,39 +26,37 @@ 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 adapter (check xvinfo results).
|
||||
Select a specific XVideo adaptor (check xvinfo results).
|
||||
``port=<number>``
|
||||
Select a specific XVideo port.
|
||||
``ck=<cur|use|set>``
|
||||
Select the source from which the color key is taken (default: cur).
|
||||
Select the source from which the colorkey is taken (default: cur).
|
||||
|
||||
cur
|
||||
The default takes the color key currently set in Xv.
|
||||
The default takes the colorkey currently set in Xv.
|
||||
use
|
||||
Use but do not set the color key from mpv (use the ``--colorkey``
|
||||
Use but do not set the colorkey from mpv (use the ``--colorkey``
|
||||
option to change it).
|
||||
set
|
||||
Same as use but also sets the supplied color key.
|
||||
Same as use but also sets the supplied colorkey.
|
||||
|
||||
``ck-method=<man|bg|auto>``
|
||||
Sets the color key drawing method (default: man).
|
||||
Sets the colorkey drawing method (default: man).
|
||||
|
||||
man
|
||||
Draw the color key manually (reduces flicker in some cases).
|
||||
Draw the colorkey manually (reduces flicker in some cases).
|
||||
bg
|
||||
Set the color key as window background.
|
||||
Set the colorkey as window background.
|
||||
auto
|
||||
Let Xv draw the color key.
|
||||
Let Xv draw the colorkey.
|
||||
|
||||
``colorkey=<number>``
|
||||
Changes the color key to an RGB value of your choice. ``0x000000`` is
|
||||
Changes the colorkey to an RGB value of your choice. ``0x000000`` is
|
||||
black and ``0xffffff`` is white.
|
||||
|
||||
``no-colorkey``
|
||||
Disables color-keying.
|
||||
Disables colorkeying.
|
||||
|
||||
``x11`` (X11 only)
|
||||
Shared memory video output driver without hardware acceleration that works
|
||||
@@ -79,36 +68,21 @@ Available video output drivers are:
|
||||
Uses the VDPAU interface to display and optionally also decode video.
|
||||
Hardware decoding is used with ``--hwdec=vdpau``.
|
||||
|
||||
.. note::
|
||||
|
||||
Earlier versions of mpv (and MPlayer, mplayer2) provided sub-options
|
||||
to tune vdpau post-processing, like ``deint``, ``sharpen``, ``denoise``,
|
||||
``chroma-deint``, ``pullup``, ``hqscaling``. These sub-options are
|
||||
deprecated, and you should use the ``vdpaupp`` video filter instead.
|
||||
|
||||
``sharpen=<-1-1>``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
For positive values, apply a sharpening algorithm to the video, for
|
||||
negative values a blurring algorithm (default: 0).
|
||||
``denoise=<0-1>``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
Apply a noise reduction algorithm to the video (default: 0; no noise
|
||||
reduction).
|
||||
``deint=<-4-4>``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
Select deinterlacing mode (default: 0). In older versions (as well as
|
||||
MPlayer/mplayer2) you could use this option to enable deinterlacing.
|
||||
This doesn't work anymore, and deinterlacing is enabled with either
|
||||
the ``D`` key (by default mapped to the command ``cycle deinterlace``),
|
||||
or the ``--deinterlace`` option. Also, to select the default deint mode,
|
||||
you should use something like ``--vf-defaults=vdpaupp:deint-mode=temporal``
|
||||
instead of this sub-option.
|
||||
Select deinterlacing mode (default: -3). Positive values choose mode
|
||||
and enable deinterlacing. Corresponding negative values select the
|
||||
same deinterlacing mode, but do not enable deinterlacing on startup
|
||||
(useful in configuration files to specify which mode will be enabled by
|
||||
the "D" key). All modes respect ``--field-dominance``.
|
||||
|
||||
0
|
||||
Pick the ``vdpaupp`` video filter default, which corresponds to 3.
|
||||
Same as -3.
|
||||
1
|
||||
Show only first field.
|
||||
2
|
||||
@@ -120,19 +94,13 @@ Available video output drivers are:
|
||||
Motion-adaptive temporal deinterlacing with edge-guided spatial
|
||||
interpolation. Needs fast video hardware.
|
||||
``chroma-deint``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
Makes temporal deinterlacers operate both on luma and chroma (default).
|
||||
Use no-chroma-deint to solely use luma and speed up advanced
|
||||
deinterlacing. Useful with slow video memory.
|
||||
``pullup``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
Try to apply inverse telecine, needs motion adaptive temporal
|
||||
deinterlacing.
|
||||
``hqscaling=<0-9>``
|
||||
(Deprecated. See note about ``vdpaupp``.)
|
||||
|
||||
0
|
||||
Use default VDPAU scaling (default).
|
||||
1-9
|
||||
@@ -173,7 +141,7 @@ Available video output drivers are:
|
||||
to a YUV format before the VO. Sometimes useful to force availability
|
||||
of certain YUV-only features, like video equalizer or deinterlacing.
|
||||
|
||||
Using the VDPAU frame queuing functionality controlled by the queuetime
|
||||
Using the VDPAU frame queueing functionality controlled by the queuetime
|
||||
options makes mpv's frame flip timing less sensitive to system CPU load and
|
||||
allows mpv to start decoding the next frame(s) slightly earlier, which can
|
||||
reduce jitter caused by individual slow-to-decode frames. However, the
|
||||
@@ -198,9 +166,6 @@ 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.
|
||||
|
||||
@@ -233,29 +198,18 @@ Available video output drivers are:
|
||||
Always force textures to power of 2, even if the device reports
|
||||
non-power-of-2 texture sizes as supported.
|
||||
|
||||
``texture-memory=<mode>``
|
||||
``texture-memory=N``
|
||||
Only affects operation with shaders/texturing enabled, and (E)OSD.
|
||||
Possible values:
|
||||
Values for N:
|
||||
|
||||
``default`` (default)
|
||||
Use ``D3DPOOL_DEFAULT``, with a ``D3DPOOL_SYSTEMMEM`` texture for
|
||||
locking. If the driver supports ``D3DDEVCAPS_TEXTURESYSTEMMEMORY``,
|
||||
``D3DPOOL_SYSTEMMEM`` is used directly.
|
||||
|
||||
``default-pool``
|
||||
Use ``D3DPOOL_DEFAULT``. (Like ``default``, but never use a
|
||||
shadow-texture.)
|
||||
|
||||
``default-pool-shadow``
|
||||
Use ``D3DPOOL_DEFAULT``, with a ``D3DPOOL_SYSTEMMEM`` texture for
|
||||
locking. (Like ``default``, but always force the shadow-texture.)
|
||||
|
||||
``managed``
|
||||
Use ``D3DPOOL_MANAGED``.
|
||||
|
||||
``scratch``
|
||||
Use ``D3DPOOL_SCRATCH``, with a ``D3DPOOL_SYSTEMMEM`` texture for
|
||||
locking.
|
||||
0
|
||||
default, will often use an additional shadow texture + copy
|
||||
1
|
||||
use ``D3DPOOL_MANAGED``
|
||||
2
|
||||
use ``D3DPOOL_DEFAULT``
|
||||
3
|
||||
use ``D3DPOOL_SYSTEMMEM``, but without shadow texture
|
||||
|
||||
``swap-discard``
|
||||
Use ``D3DSWAPEFFECT_DISCARD``, which might be faster.
|
||||
@@ -268,7 +222,10 @@ 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``).
|
||||
|
||||
``opengl``
|
||||
OpenGL video output driver. It supports extended scaling methods, dithering
|
||||
@@ -284,11 +241,6 @@ Available video output drivers are:
|
||||
Some features are available with OpenGL 3 capable graphics drivers only
|
||||
(or if the necessary extensions are available).
|
||||
|
||||
Hardware decoding over OpenGL-interop is supported to some degree. Note
|
||||
that in this mode, some corner case might not be gracefully handled, and
|
||||
color space conversion and chroma upsampling is generally in the hand of
|
||||
the hardware decoder APIs.
|
||||
|
||||
``lscale=<filter>``
|
||||
|
||||
``bilinear``
|
||||
@@ -297,16 +249,11 @@ Available video output drivers are:
|
||||
|
||||
``lanczos2``
|
||||
Lanczos scaling with radius=2. Provides good quality and speed.
|
||||
This is the default when using ``opengl-hq``.
|
||||
|
||||
``lanczos3``
|
||||
Lanczos with radius=3.
|
||||
|
||||
``lanczos``
|
||||
Generic Lanczos scaling filter. Set radius with ``lradius``.
|
||||
|
||||
``spline36``
|
||||
This is the default when using ``opengl-hq``.
|
||||
|
||||
``bicubic_fast``
|
||||
Bicubic filter. Has a blurring effect on the image, even if no
|
||||
scaling is done.
|
||||
@@ -323,14 +270,6 @@ Available video output drivers are:
|
||||
Mitchell-Netravali. The ``b`` and ``c`` parameters can be set with
|
||||
``lparam1`` and ``lparam2``. Both are set to 1/3 by default.
|
||||
|
||||
``gaussian``
|
||||
Gaussian filter with a parameter ``p`` for sharpness control.
|
||||
``p`` can be set to float number between 1(blurry) and 100(sharp)
|
||||
and has a default value of about 28.8 (see ``lparam1``).
|
||||
|
||||
Note that for extremely small value of ``p``, a large filter radius
|
||||
might be required to avoid unintended artifacts (see ``lradius``).
|
||||
|
||||
|
||||
There are some more filters. For a complete list, pass ``help`` as
|
||||
value, e.g.::
|
||||
@@ -344,16 +283,6 @@ Available video output drivers are:
|
||||
``lparam2=<value>``
|
||||
See ``lparam1``.
|
||||
|
||||
``lradius=<r>``
|
||||
Set radius for filters listed below, must be a float number between 1.0
|
||||
and 8.0. Defaults to be 2.0 if not specified.
|
||||
|
||||
``sinc``, ``lanczos``, ``blackman``, ``gaussian``
|
||||
|
||||
Note that depending on filter implementation details and video scaling
|
||||
ratio, the radius that actually being used might be different
|
||||
(most likely being increased a bit).
|
||||
|
||||
``scaler-resizes-only``
|
||||
Disable the scaler if the video image is not resized. In that case,
|
||||
``bilinear`` is used instead whatever is set with ``lscale``. Bilinear
|
||||
@@ -363,9 +292,8 @@ Available video output drivers are:
|
||||
disabled.
|
||||
|
||||
``stereo=<value>``
|
||||
Select a method for stereo display. You may have to use
|
||||
``--video-aspect`` to fix the aspect value. Experimental, do not expect
|
||||
too much from it.
|
||||
Select a method for stereo display. You may have to use ``--aspect`` to
|
||||
fix the aspect value. Experimental, do not expect too much from it.
|
||||
|
||||
no
|
||||
Normal 2D display
|
||||
@@ -374,30 +302,26 @@ Available video output drivers are:
|
||||
green-magenta
|
||||
Convert side by side input to full-color green-magenta stereo.
|
||||
quadbuffer
|
||||
Convert side by side input to quad buffered stereo. Only supported
|
||||
Convert side by side input to quadbuffered stereo. Only supported
|
||||
by very few OpenGL cards.
|
||||
|
||||
``srgb``
|
||||
Convert and color correct the output to sRGB before displaying it on
|
||||
the screen. This option enables linear light scaling. It also forces
|
||||
the options ``indirect`` and ``gamma``.
|
||||
Enable gamma-correct scaling by working in linear light. This
|
||||
makes use of sRGB textures and framebuffers.
|
||||
This option forces the options ``indirect`` and ``gamma``.
|
||||
|
||||
This option is equivalent to using ``icc-profile`` with an sRGB ICC
|
||||
profile, but it is implemented without a 3DLUT and does not require
|
||||
LittleCMS 2. If both ``srgb`` and ``icc-profile`` are present, the
|
||||
latter takes precedence, as they are somewhat redundant.
|
||||
.. note::
|
||||
|
||||
Note: When playing back BT.2020 content with this option enabled, out
|
||||
of gamut colors will be numerically clipped, which can potentially
|
||||
change the hue and/or luminance. If this is not desired, it is
|
||||
recommended to use ``icc-profile`` with an sRGB ICC profile instead,
|
||||
when playing back wide-gamut BT.2020 content.
|
||||
for YUV colorspaces, gamma 1/0.45 (2.222) is assumed. RGB input is
|
||||
always assumed to be in sRGB.
|
||||
|
||||
This option is not really useful, as gamma-correct scaling has not much
|
||||
influence on typical video playback. Most visible effect comes from
|
||||
slightly different gamma.
|
||||
|
||||
``pbo``
|
||||
Enable use of PBOs. This is slightly faster, but can sometimes lead to
|
||||
sporadic and temporary image corruption (in theory, because reupload
|
||||
is not retried when it fails), and perhaps actually triggers slower
|
||||
paths with drivers that don't support PBOs properly.
|
||||
Enable use of PBOs. This is faster, but can sometimes lead to sporadic
|
||||
and temporary image corruption.
|
||||
|
||||
``dither-depth=<N|no|auto>``
|
||||
Set dither target depth to N. Default: no.
|
||||
@@ -449,22 +373,12 @@ Available video output drivers are:
|
||||
this options will make rendering a single operation.
|
||||
Note that chroma scalers are always done as 1-pass filters.
|
||||
|
||||
``cscale=<filter>``
|
||||
``cscale=<n>``
|
||||
As ``lscale``, but for chroma (2x slower with little visible effect).
|
||||
Note that with some scaling filters, upscaling is always done in
|
||||
RGB. If chroma is not subsampled, this option is ignored, and the
|
||||
luma scaler is used instead. Setting this option is often useless.
|
||||
|
||||
``lscale-down=<filter>``, ``cscale-down=<filter>``
|
||||
Like ``lscale`` and ``cscale``, but apply these filters on downscaling
|
||||
instead. If these options are unset, the filter implied by ``lscale``
|
||||
(and ``cscale``, respectively) will be applied.
|
||||
|
||||
``cparam1``, ``cparam2``, ``cradius``
|
||||
Set filter parameters and radius for ``cscale``.
|
||||
|
||||
See ``lparam1``, ``lparam2`` and ``lradius``.
|
||||
|
||||
``fancy-downscaling``
|
||||
When using convolution based filters, extend the filter size
|
||||
when downscaling. Trades quality for reduced downscaling performance.
|
||||
@@ -474,16 +388,7 @@ Available video output drivers are:
|
||||
Borders will be distorted due to filtering.
|
||||
|
||||
``glfinish``
|
||||
Call ``glFinish()`` before and after swapping buffers (default: disabled).
|
||||
Slower, but might help getting better results when doing framedropping.
|
||||
The details depend entirely on the OpenGL driver.
|
||||
|
||||
``waitvsync``
|
||||
Call ``glXWaitVideoSyncSGI`` after each buffer swap (default: disabled).
|
||||
This may or may not help with video timing accuracy and frame drop. It's
|
||||
possible that this makes video output slower, or has no effect at all.
|
||||
|
||||
X11 only.
|
||||
Call ``glFinish()`` before swapping buffers
|
||||
|
||||
``sw``
|
||||
Continue even if a software renderer is detected.
|
||||
@@ -496,7 +401,7 @@ Available video output drivers are:
|
||||
auto
|
||||
auto-select (default)
|
||||
cocoa
|
||||
Cocoa/OS X
|
||||
Cocoa/OSX
|
||||
win
|
||||
Win32/WGL
|
||||
x11
|
||||
@@ -517,65 +422,37 @@ Available video output drivers are:
|
||||
Selects the internal format of textures used for FBOs. The format can
|
||||
influence performance and quality of the video output. (FBOs are not
|
||||
always used, and typically only when using extended scalers.)
|
||||
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb10_a2, rgb16, rgb16f,
|
||||
rgb32f, rgba12, rgba16, rgba16f, rgba32f.
|
||||
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb16, rgb16f, rgb32f,
|
||||
rgba12, rgba16, rgba16f, rgba32f.
|
||||
Default: rgb.
|
||||
|
||||
``gamma=<0.0..10.0>``
|
||||
Set a gamma value. If gamma is adjusted in other ways (like with
|
||||
the ``--gamma`` option or key bindings and the ``gamma`` property), the
|
||||
value is multiplied with the other gamma value.
|
||||
|
||||
Setting this value to 1.0 can be used to always enable gamma control.
|
||||
(Disables delayed enabling.)
|
||||
``gamma``
|
||||
Always enable gamma control. (Disables delayed enabling.)
|
||||
|
||||
``icc-profile=<file>``
|
||||
Load an ICC profile and use it to transform linear RGB to screen output.
|
||||
Needs LittleCMS 2 support compiled in. This option overrides the ``srgb``
|
||||
property, as using both is somewhat redundant. It also enables linear
|
||||
light scaling.
|
||||
|
||||
|
||||
``icc-profile-auto``
|
||||
Automatically select the ICC display profile currently specified by
|
||||
the display settings of the operating system.
|
||||
|
||||
NOTE: Only implemented on OS X with Cocoa.
|
||||
Needs LittleCMS2 support compiled in.
|
||||
|
||||
``icc-cache=<file>``
|
||||
Store and load the 3D LUT created from the ICC profile in this file.
|
||||
This can be used to speed up loading, since LittleCMS 2 can take a while
|
||||
This can be used to speed up loading, since LittleCMS2 can take a while
|
||||
to create the 3D LUT. Note that this file contains an uncompressed LUT.
|
||||
Its size depends on the ``3dlut-size``, and can be very big.
|
||||
|
||||
``icc-intent=<value>``
|
||||
Specifies the ICC Intent used for transformations between color spaces.
|
||||
This affects the rendering when using ``icc-profile`` or ``srgb`` and
|
||||
also affects the way DCP XYZ content gets converted to RGB.
|
||||
|
||||
0
|
||||
perceptual
|
||||
1
|
||||
relative colorimetric (default)
|
||||
relative colorimetric
|
||||
2
|
||||
saturation
|
||||
3
|
||||
absolute colorimetric
|
||||
|
||||
``approx-gamma``
|
||||
Approximate the actual gamma function as a pure power curve of
|
||||
1.95. A number of video editing programs and studios apparently use this
|
||||
for mastering instead of the true curve. Most notably, anything in the
|
||||
Apple ecosystem uses this approximation - including all programs
|
||||
compatible with it. It's a sound idea to try enabling this flag first
|
||||
when watching videos and shows to see if things look better that way.
|
||||
|
||||
This only affects the output when using either ``icc-profile`` or ``srgb``.
|
||||
absolute colorimetric (default)
|
||||
|
||||
``3dlut-size=<r>x<g>x<b>``
|
||||
Size of the 3D LUT generated from the ICC profile in each dimension.
|
||||
Default is 128x256x64.
|
||||
Sizes must be a power of two, and 512 at most.
|
||||
Sizes must be a power of two, and 256 at most.
|
||||
|
||||
``alpha=<blend|yes|no>``
|
||||
Decides what to do if the input has an alpha component (default: blend).
|
||||
@@ -596,17 +473,12 @@ Available video output drivers are:
|
||||
Set the YUV chroma sample location. auto means use the bitstream
|
||||
flags (default: auto).
|
||||
|
||||
``rectangle-textures``
|
||||
Force use of rectangle textures (default: no). Normally this shouldn't
|
||||
have any advantages over normal textures. Note that hardware decoding
|
||||
overrides this flag.
|
||||
|
||||
``opengl-hq``
|
||||
Same as ``opengl``, but with default settings for high quality rendering.
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
--vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16
|
||||
--vo=opengl:lscale=lanczos2:dither-depth=auto:pbo:fbo-format=rgb16
|
||||
|
||||
Note that some cheaper LCDs do dithering that gravely interferes with
|
||||
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
|
||||
@@ -615,7 +487,7 @@ Available video output drivers are:
|
||||
can achieve better quality or performance by changing the ``fbo-format``
|
||||
suboption to ``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include
|
||||
Mesa/Intel not accepting ``rgb16``, Mesa sometimes not being compiled with
|
||||
float texture support, and some OS X setups being very slow with ``rgb16``
|
||||
float texture support, and some OSX setups being very slow with ``rgb16``
|
||||
but fast with ``rgb32f``.
|
||||
|
||||
``opengl-old``
|
||||
@@ -629,8 +501,6 @@ 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
|
||||
@@ -739,9 +609,8 @@ Available video output drivers are:
|
||||
Set the effect strength for the ``lscale``/``cscale`` filters that
|
||||
support it.
|
||||
``stereo=<value>``
|
||||
Select a method for stereo display. You may have to use
|
||||
``--video-aspect`` to fix the aspect value. Experimental, do not expect
|
||||
too much from it.
|
||||
Select a method for stereo display. You may have to use ``--aspect`` to
|
||||
fix the aspect value. Experimental, do not expect too much from it.
|
||||
|
||||
0
|
||||
Normal 2D display
|
||||
@@ -750,7 +619,7 @@ Available video output drivers are:
|
||||
2
|
||||
Convert side by side input to full-color green-magenta stereo.
|
||||
3
|
||||
Convert side by side input to quad buffered stereo. Only supported
|
||||
Convert side by side input to quadbuffered stereo. Only supported
|
||||
by very few OpenGL cards.
|
||||
|
||||
The following options are only useful if writing your own fragment programs.
|
||||
@@ -792,7 +661,7 @@ Available video output drivers are:
|
||||
auto
|
||||
auto-select (default)
|
||||
cocoa
|
||||
Cocoa/OS X
|
||||
Cocoa/OSX
|
||||
win
|
||||
Win32/WGL
|
||||
x11
|
||||
@@ -805,9 +674,6 @@ 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.
|
||||
|
||||
@@ -820,9 +686,6 @@ 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).
|
||||
@@ -863,8 +726,6 @@ 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.
|
||||
@@ -910,17 +771,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.
|
||||
|
||||
``alpha``
|
||||
Use a buffer format that supports videos and images with alpha
|
||||
information
|
||||
``rgb565``
|
||||
Use RGB565 as buffer format. This format is implemented on most
|
||||
platforms, especially on embedded where it is far more efficient then
|
||||
RGB8888.
|
||||
``triple-buffering``
|
||||
Use 3 buffers instead of 2. This can lead to more fluid playback, but
|
||||
uses more memory.
|
||||
|
||||
informations
|
||||
-1609
File diff suppressed because it is too large.
Load diff
@@ -1,183 +0,0 @@
|
||||
JSON IPC
|
||||
========
|
||||
|
||||
mpv can be controlled by external programs using the JSON-based IPC protocol. It
|
||||
can be enabled by specifying the path to a unix socket using the option
|
||||
``--input-unix-socket``. Clients can connect to this socket and send commands to
|
||||
the player or receive events from it. You can use the ``socat`` tool to send
|
||||
commands (and receive reply) from the shell.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is not intended to be a secure network protocol. It is explicitly
|
||||
insecure: there is no authentication, no encryption, and the commands
|
||||
themselves are insecure too. For example, the ``run`` command is exposed,
|
||||
which can run arbitrary system commands. The use-case is controlling the
|
||||
player locally. This is not different from the MPlayer slave protocol.
|
||||
|
||||
Protocol
|
||||
--------
|
||||
|
||||
Clients can execute commands on the player by sending JSON messages of the
|
||||
following form:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["command_name", "param1", "param2", ...] }
|
||||
|
||||
where ``command_name`` is the name of the command to be executed, followed by a
|
||||
list of parameters. Parameters must be formatted as native JSON values
|
||||
(integers, strings, booleans, ...). Every message **must** be terminated with
|
||||
``\n``. Additionally, ``\n`` must not appear anywhere inside the message. In
|
||||
practice this means that messages should be minified before being sent to mpv.
|
||||
|
||||
mpv will then send back a reply indicating whether the command was run
|
||||
correctly, and an additional field holding the command-specific return data (it
|
||||
can also be null).
|
||||
|
||||
::
|
||||
|
||||
{ "error": "success", "data": null }
|
||||
|
||||
mpv will also send events to clients with JSON messages of the following form:
|
||||
|
||||
::
|
||||
|
||||
{ "event": "event_name" }
|
||||
|
||||
where ``event_name`` is the name of the event. Additional event-specific fields
|
||||
can also be present. See `List of events`_ for a list of all supported events.
|
||||
|
||||
If the first character (after skipping whitespace) is not ``{``, the command
|
||||
will be interpreted as non-JSON text command, as they are used in input.conf
|
||||
(or ``mpv_command_string()`` in the client API). Additionally, line starting
|
||||
with ``#`` and empty lines are ignored.
|
||||
|
||||
Currently, embedded 0 bytes terminate the current line, but you should not
|
||||
rely on this.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
Additionally to the commands described in `List of Input Commands`_, a few
|
||||
extra commands can also be used as part of the protocol:
|
||||
|
||||
``client_name``
|
||||
Return the name of the client as string. This is the string ``ipc-N`` with
|
||||
N being an integer number.
|
||||
|
||||
``get_time_us``
|
||||
Return the current mpv internal time in microseconds as a number. This is
|
||||
basically the system time, with an arbitrary offset.
|
||||
|
||||
``get_property``
|
||||
Return the value of the given property. The value will be sent in the data
|
||||
field of the replay message.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["get_property", "volume"] }
|
||||
{ "data": 50.0, "error": "success" }
|
||||
|
||||
``get_property_string``
|
||||
Like ``get_property``, but the resulting data will always be a string.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["get_property_string", "volume"] }
|
||||
{ "data": "50.000000", "error": "success" }
|
||||
|
||||
``set_property``
|
||||
Set the given property to the given value. See `Properties`_ for more
|
||||
information about properties.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["set_property", "pause", true] }
|
||||
{ "error": "success" }
|
||||
|
||||
``set_property_string``
|
||||
Like ``set_property``, but the argument value must be passed as string.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["set_property_string", "pause", "yes"] }
|
||||
{ "error": "success" }
|
||||
|
||||
``observe_property``
|
||||
Watch a property for changes. If the given property is changed, then an
|
||||
event of type ``property-change`` will be generated
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["observe_property", 1, "volume"] }
|
||||
{ "error": "success" }
|
||||
{ "event": "property-change", "id": 1, "data": 52.0, "name": "volume" }
|
||||
|
||||
``observe_property_string``
|
||||
Like ``observe_property``, but the resulting data will always be a string.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["observe_property_string", 1, "volume"] }
|
||||
{ "error": "success" }
|
||||
{ "event": "property-change", "id": 1, "data": "52.000000", "name": "volume" }
|
||||
|
||||
``unobserve_property``
|
||||
Undo ``observe_property`` or ``observe_property_string``. This requires the
|
||||
numeric id passed to the observe command as argument.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["unobserve_property", 1] }
|
||||
{ "error": "success" }
|
||||
|
||||
``request_log_messages``
|
||||
Enable output of mpv log messages. They will be received as events. The
|
||||
parameter to this command is the log-level (see ``mpv_request_log_messages``
|
||||
C API function).
|
||||
|
||||
Log message output is meant for humans only (mostly for debugging).
|
||||
Attempting to retrieve information by parsing these messages will just
|
||||
lead to breakages with future mpv releases. Instead, make a feature request,
|
||||
and ask for a proper event that returns the information you need.
|
||||
|
||||
``suspend``
|
||||
Suspend the mpv main loop. There is a long-winded explanation of this in
|
||||
the C API function ``mpv_suspend()``. In short, this prevents the player
|
||||
from displaying the next video frame, so that you don't get blocked when
|
||||
trying to access the player.
|
||||
|
||||
``resume``
|
||||
Undo one ``suspend`` call. ``suspend`` increments an internal counter, and
|
||||
``resume`` decrements it. When 0 is reached, the player is actually resumed.
|
||||
|
||||
``get_version``
|
||||
Returns the client API version the C API of the remote mpv instance
|
||||
provides. (Also see ``DOCS/client-api-changes.rst``.)
|
||||
|
||||
UTF-8
|
||||
-----
|
||||
|
||||
Normally, all strings are in UTF-8. Sometimes it can happen that strings are
|
||||
in some broken encoding (often happens with file tags and such, and filenames
|
||||
on many Unixes are not required to be in UTF-8 either). This means that mpv
|
||||
sometimes sends invalid JSON. If that is a problem for the client application's
|
||||
parser, it should filter the raw data for invalid UTF-8 sequences and perform
|
||||
the desired replacement, before feeding the data to its JSON parser.
|
||||
|
||||
mpv will not attempt to construct invalid UTF-8 with broken escape sequences.
|
||||
@@ -1,759 +0,0 @@
|
||||
LUA SCRIPTING
|
||||
=============
|
||||
|
||||
mpv can load Lua scripts. Scripts passed to the ``--lua`` option, or found in
|
||||
the ``lua`` subdirectory of the mpv configuration directory (usually
|
||||
``~/.config/mpv/lua/``) will be loaded on program start. mpv also appends the
|
||||
``lua`` subdirectory to the end of Lua's path so you can import scripts from
|
||||
there too. Since it's added to the end, don't name scripts you want to import
|
||||
the same as Lua libraries because they will be overshadowed by them.
|
||||
|
||||
mpv provides the built-in module ``mp``, which provides functions to send
|
||||
commands to the mpv core and to retrieve information about playback state, user
|
||||
settings, file information, and so on.
|
||||
|
||||
These scripts can be used to control mpv in a similar way to slave mode.
|
||||
Technically, the Lua code uses the client API internally.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
A script which leaves fullscreen mode when the player is paused:
|
||||
|
||||
::
|
||||
|
||||
function on_pause()
|
||||
mp.set_property("fullscreen", "no")
|
||||
end
|
||||
mp.register_event("pause", on_pause)
|
||||
|
||||
This script provides a pretty weird feature, but Lua scripting was made to
|
||||
allow users implement features which are not going to be added to the mpv core.
|
||||
|
||||
Mode of operation
|
||||
-----------------
|
||||
|
||||
Your script will be loaded by the player at program start from the ``lua``
|
||||
configuration subdirectory, from a path specified with the ``--lua`` option, or
|
||||
in some cases, internally (like ``--osc``). Each script runs in its own
|
||||
thread. Your script is first run "as is", and once that is done, the event loop
|
||||
is entered. This event loop will dispatch events received by mpv and call your
|
||||
own event handlers which you have registered with ``mp.register_event``, or
|
||||
timers added with ``mp.add_timeout`` or similar.
|
||||
|
||||
When the player quits, all scripts will be asked to terminate. This happens via
|
||||
a ``shutdown`` event, which by default will make the event loop return. If your
|
||||
script got into an endless loop, mpv will probably behave fine during playback
|
||||
(unless the player is suspended, see ``mp.suspend``), but it won't terminate
|
||||
when quitting, because it's waiting on your script.
|
||||
|
||||
Internally, the C code will call the Lua function ``mp_event_loop`` after
|
||||
loading a Lua script. This function is normally defined by the default prelude
|
||||
loaded before your script (see ``player/lua/defaults.lua`` in the mpv sources).
|
||||
The event loop will wait for events and dispatch events registered with
|
||||
``mp.register_event``. It will also handle timers added with ``mp.add_timeout``
|
||||
and similar (by waiting with a timeout).
|
||||
|
||||
Since mpv 0.6.0, the player will wait until the script is fully loaded before
|
||||
continuing normal operation. The player considers a script as fully loaded as
|
||||
soon as it starts waiting for mpv events (or it exits). In practice this means
|
||||
the player will more or less hang until the script returns from the main chunk
|
||||
(and ``mp_event_loop`` is called), or the script calls ``mp_event_loop`` or
|
||||
``mp.dispatch_events`` directly. This is done to make it possible for a script
|
||||
to fully setup event handlers etc. before playback actually starts. In older
|
||||
mpv versions, this happened asynchronously.
|
||||
|
||||
mp functions
|
||||
------------
|
||||
|
||||
The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
``require 'mp'``. It provides the core client API.
|
||||
|
||||
``mp.command(string)``
|
||||
Run the given command. This is similar to the commands used in input.conf.
|
||||
See `List of Input Commands`_.
|
||||
|
||||
Returns ``true`` on success, or ``nil, error`` on error.
|
||||
|
||||
``mp.commandv(arg1, arg2, ...)``
|
||||
Similar to ``mp.command``, but pass each command argument as separate
|
||||
parameter. This has the advantage that you don't have to care about
|
||||
quoting and escaping in some cases.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
mp.command("loadfile " .. filename .. " append")
|
||||
mp.commandv("loadfile", filename, "append")
|
||||
|
||||
These two commands are equivalent, except that the first version breaks
|
||||
if the filename contains spaces or certain special characters.
|
||||
|
||||
Note that properties are *not* expanded. You can use either ``mp.command``,
|
||||
the ``expand-properties`` prefix, or the ``mp.get_property`` family of
|
||||
functions.
|
||||
|
||||
``mp.command_native(table [,def])``
|
||||
Similar to ``mp.commandv``, but pass the argument list as table. This has
|
||||
the advantage that in at least some cases, arguments can be passed as
|
||||
native types.
|
||||
|
||||
Returns a result table on success (usually empty), or ``def, error`` on
|
||||
error. ``def`` is the second parameter provided to the function, and is
|
||||
nil if it's missing.
|
||||
|
||||
``mp.get_property(name [,def])``
|
||||
Return the value of the given property as string. These are the same
|
||||
properties as used in input.conf. See `Properties`_ for a list of
|
||||
properties. The returned string is formatted similar to ``${=name}``
|
||||
(see `Property Expansion`_).
|
||||
|
||||
Returns the string on success, or ``def, error`` on error. ``def`` is the
|
||||
second parameter provided to the function, and is nil if it's missing.
|
||||
|
||||
``mp.get_property_osd(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value formatted for
|
||||
OSD. This is the same string as printed with ``${name}`` when used in
|
||||
input.conf.
|
||||
|
||||
Returns the string on success, or ``def, error`` on error. ``def`` is the
|
||||
second parameter provided to the function, and is an empty string if it's
|
||||
missing. Unlike ``get_property()``, assigning the return value to a variable
|
||||
will always result in a string.
|
||||
|
||||
``mp.get_property_bool(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value as Boolean.
|
||||
|
||||
Returns a Boolean on success, or ``def, error`` on error.
|
||||
|
||||
``mp.get_property_number(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value as number.
|
||||
|
||||
Note that while Lua does not distinguish between integers and floats,
|
||||
mpv internals do. This function simply request a double float from mpv,
|
||||
and mpv will usually convert integer property values to float.
|
||||
|
||||
Returns a number on success, or ``def, error`` on error.
|
||||
|
||||
``mp.get_property_native(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value using the best
|
||||
Lua type for the property. Most time, this will return a string, Boolean,
|
||||
or number. Some properties (for example ``chapter-list``) are returned as
|
||||
tables.
|
||||
|
||||
Returns a value on success, or ``def, error`` on error. Note that ``nil``
|
||||
might be a possible, valid value too in some corner cases.
|
||||
|
||||
``mp.set_property(name, value)``
|
||||
Set the given property to the given string value. See ``mp.get_property``
|
||||
and `Properties`_ for more information about properties.
|
||||
|
||||
Returns true on success, or ``nil, error`` on error.
|
||||
|
||||
``mp.set_property_bool(name, value)``
|
||||
Similar to ``mp.set_property``, but set the given property to the given
|
||||
Boolean value.
|
||||
|
||||
``mp.set_property_number(name, value)``
|
||||
Similar to ``mp.set_property``, but set the given property to the given
|
||||
numeric value.
|
||||
|
||||
Note that while Lua does not distinguish between integers and floats,
|
||||
mpv internals do. This function will test whether the number can be
|
||||
represented as integer, and if so, it will pass an integer value to mpv,
|
||||
otherwise a double float.
|
||||
|
||||
``mp.set_property_native(name, value)``
|
||||
Similar to ``mp.set_property``, but set the given property using its native
|
||||
type.
|
||||
|
||||
Since there are several data types which can not represented natively in
|
||||
Lua, this might not always work as expected. For example, while the Lua
|
||||
wrapper can do some guesswork to decide whether a Lua table is an array
|
||||
or a map, this would fail with empty tables. Also, there are not many
|
||||
properties for which it makes sense to use this, instead of
|
||||
``set_property``, ``set_property_bool``, ``set_property_number``.
|
||||
For these reasons, this function should probably be avoided for now, except
|
||||
for properties that use tables natively.
|
||||
|
||||
``mp.get_time()``
|
||||
Return the current mpv internal time in seconds as a number. This is
|
||||
basically the system time, with an arbitrary offset.
|
||||
|
||||
``mp.add_key_binding(key, name|fn [,fn [,flags]])``
|
||||
Register callback to be run on a key binding. The binding will be mapped to
|
||||
the given ``key``, which is a string describing the physical key. This uses
|
||||
the same key names as in input.conf, and also allows combinations
|
||||
(e.g. ``ctrl+a``).
|
||||
|
||||
After calling this function, key presses will cause the function ``fn`` to
|
||||
be called (unless the user remapped the key with another binding).
|
||||
|
||||
The ``name`` argument should be a short symbolic string. It allows the user
|
||||
to remap the key binding via input.conf using the ``script_message``
|
||||
command, and the name of the key binding (see below for
|
||||
an example). The name should be unique across other bindings in the same
|
||||
script - if not, the previous binding with the same name will be
|
||||
overwritten. You can omit the name, in which case a random name is generated
|
||||
internally.
|
||||
|
||||
The last argument is used for optional flags. This is a table, which can
|
||||
have the following entries:
|
||||
|
||||
``repeatable``
|
||||
If set to ``true``, enables key repeat for this specific binding.
|
||||
|
||||
``complex``
|
||||
If set to ``true``, then ``fn`` is called on both key up and down
|
||||
events (as well as key repeat, if enabled), with the first
|
||||
argument being a table. This table has an ``event`` entry, which
|
||||
is set to one of the strings ``down``, ``repeat``, ``up`` or
|
||||
``press`` (the latter if key up/down can't be tracked). It further
|
||||
has an ``is_mouse`` entry, which tells whether the event was caused
|
||||
by a mouse button.
|
||||
|
||||
Internally, key bindings are dispatched via the ``script_message_to`` or
|
||||
``script_binding`` input commands and ``mp.register_script_message``.
|
||||
|
||||
Trying to map multiple commands to a key will essentially prefer a random
|
||||
binding, while the other bindings are not called. It is guaranteed that
|
||||
user defined bindings in the central input.conf are preferred over bindings
|
||||
added with this function (but see ``mp.add_forced_key_binding``).
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function something_handler()
|
||||
print("the key was pressed")
|
||||
end
|
||||
mp.add_key_binding("x", "something", something_handler)
|
||||
|
||||
This will print the message ``the key was pressed`` when ``x`` was pressed.
|
||||
|
||||
The user can remap these key bindings. Then the user has to put the
|
||||
following into his input.conf to remap the command to the ``y`` key:
|
||||
|
||||
::
|
||||
|
||||
y script_binding something
|
||||
|
||||
|
||||
This will print the message when the key ``y`` is pressed. (``x`` will
|
||||
still work, unless the user remaps it.)
|
||||
|
||||
You can also explicitly send a message to a named script only. Assume the
|
||||
above script was using the filename ``fooscript.lua``:
|
||||
|
||||
::
|
||||
|
||||
y script_binding fooscript.something
|
||||
|
||||
``mp.add_forced_key_binding(...)``
|
||||
This works almost the same as ``mp.add_key_binding``, but registers the
|
||||
key binding in a way that will overwrite the user's custom bindings in his
|
||||
input.conf. (``mp.add_key_binding`` overwrites default key bindings only,
|
||||
but not those by the user's input.conf.)
|
||||
|
||||
``mp.remove_key_binding(name)``
|
||||
Remove a key binding added with ``mp.add_key_binding`` or
|
||||
``mp.add_forced_key_binding``. Use the same name as you used when adding
|
||||
the bindings. It's not possible to remove bindings for which you omitted
|
||||
the name.
|
||||
|
||||
``mp.register_event(name, fn)``
|
||||
Call a specific function when an event happens. The event name is a string,
|
||||
and the function fn is a Lua function value.
|
||||
|
||||
Some events have associated data. This is put into a Lua table and passed
|
||||
as argument to fn. The Lua table by default contains a ``name`` field,
|
||||
which is a string containing the event name. If the event has an error
|
||||
associated, the ``error`` field is set to a string describing the error,
|
||||
on success it's not set.
|
||||
|
||||
If multiple functions are registered for the same event, they are run in
|
||||
registration order, which the first registered function running before all
|
||||
the other ones.
|
||||
|
||||
Returns true if such an event exists, false otherwise.
|
||||
|
||||
See `Events`_ and `List of events`_ for details.
|
||||
|
||||
``mp.unregister_event(fn)``
|
||||
Undo ``mp.register_event(..., fn)``. This removes all event handlers that
|
||||
are equal to the ``fn`` parameter. This uses normal Lua ``==`` comparison,
|
||||
so be careful when dealing with closures.
|
||||
|
||||
``mp.observe_property(name, type, fn)``
|
||||
Watch a property for changes. If the property ``name`` is changed, then
|
||||
the function ``fn(name)`` will be called. ``type`` can be ``nil``, or be
|
||||
set to one of ``none``, ``native``, ``bool``, ``string``, or ``number``.
|
||||
``none`` is the same as ``nil``. For all other values, the new value of
|
||||
the property will be passed as second argument to ``fn``, using
|
||||
``mp.get_property_<type>`` to retrieve it. This means if ``type`` is for
|
||||
example ``string``, ``fn`` is roughly called as in
|
||||
``fn(name, mp.get_property_string(name))``.
|
||||
|
||||
If possible, change events are coalesced. If a property is changed a bunch
|
||||
of times in a row, only the last change triggers the change function. (The
|
||||
exact behavior depends on timing and other things.)
|
||||
|
||||
In some cases the function is not called even if the property changes.
|
||||
Whether this can happen depends on the property.
|
||||
|
||||
If the ``type`` is ``none`` or ``nil``, sporadic property change events are
|
||||
possible. This means the change function ``fn`` can be called even if the
|
||||
property doesn't actually change.
|
||||
|
||||
``mp.unobserve_property(fn)``
|
||||
Undo ``mp.observe_property(..., fn)``. This removes all property handlers
|
||||
that are equal to the ``fn`` parameter. This uses normal Lua ``==``
|
||||
comparison, so be careful when dealing with closures.
|
||||
|
||||
``mp.add_timeout(seconds, fn)``
|
||||
Call the given function fn when the given number of seconds has elapsed.
|
||||
Note that the number of seconds can be fractional. For now, the timer's
|
||||
resolution may be as low as 50 ms, although this will be improved in the
|
||||
future.
|
||||
|
||||
This is a one-shot timer: it will be removed when it's fired.
|
||||
|
||||
Returns a timer object. See ``mp.add_periodic_timer`` for details.
|
||||
|
||||
``mp.add_periodic_timer(seconds, fn)``
|
||||
Call the given function periodically. This is like ``mp.add_timeout``, but
|
||||
the timer is re-added after the function fn is run.
|
||||
|
||||
Returns a timer object. The timer object provides the following methods:
|
||||
|
||||
``stop()``
|
||||
Disable the timer. Does nothing if the timer is already disabled.
|
||||
This will remember the current elapsed time when stopping, so that
|
||||
``resume()`` essentially unpauses the timer.
|
||||
|
||||
``kill()``
|
||||
Disable the timer. Resets the elapsed time. ``resume()`` will
|
||||
restart the timer.
|
||||
|
||||
``resume()``
|
||||
Restart the timer. If the timer was disabled with ``stop()``, this
|
||||
will resume at the time it was stopped. If the timer was disabled
|
||||
with ``kill()``, or if it's a previously fired one-shot timer (added
|
||||
with ``add_timeout()``), this starts the timer from the beginning,
|
||||
using the initially configured timeout.
|
||||
|
||||
``timeout`` (RW)
|
||||
This field contains the current timeout period. This value is not
|
||||
updated as time progresses. It's only used to calculate when the
|
||||
timer should fire next when the timer expires.
|
||||
|
||||
If you write this, you can call ``t:kill() ; t:resume()`` to reset
|
||||
the current timeout to the new one. (``t:stop()`` won't use the
|
||||
new timeout.)
|
||||
|
||||
``oneshot`` (RW)
|
||||
Whether the timer is periodic (``false``) or fires just once
|
||||
(``true``). This value is used when the timer expires (but before
|
||||
the timer callback function fn is run).
|
||||
|
||||
|
||||
``mp.get_opt(key)``
|
||||
Return a setting from the ``--lua-opts`` option. It's up to the user and
|
||||
the script how this mechanism is used. Currently, all scripts can access
|
||||
this equally, so you should be careful about collisions.
|
||||
|
||||
``mp.get_script_name()``
|
||||
Return the name of the current script. The name is usually made of the
|
||||
filename of the script, with directory and file extension removed. If
|
||||
there are several script which would have the same name, it's made unique
|
||||
by appending a number.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
The script ``/path/to/fooscript.lua`` becomes ``fooscript``.
|
||||
|
||||
``mp.osd_message(text [,duration])``
|
||||
Show an OSD message on the screen. ``duration`` is in seconds, and is
|
||||
optional (uses ``--osd-duration`` by default).
|
||||
|
||||
Advanced mp functions
|
||||
---------------------
|
||||
|
||||
These also live in the ``mp`` module, but are documented separately as they
|
||||
are useful only in special situations.
|
||||
|
||||
``mp.suspend()``
|
||||
Suspend the mpv main loop. There is a long-winded explanation of this in
|
||||
the C API function ``mpv_suspend()``. In short, this prevents the player
|
||||
from displaying the next video frame, so that you don't get blocked when
|
||||
trying to access the player.
|
||||
|
||||
This is automatically called by the event handler.
|
||||
|
||||
``mp.resume()``
|
||||
Undo one ``mp.suspend()`` call. ``mp.suspend()`` increments an internal
|
||||
counter, and ``mp.resume()`` decrements it. When 0 is reached, the player
|
||||
is actually resumed.
|
||||
|
||||
``mp.resume_all()``
|
||||
This resets the internal suspend counter and resumes the player. (It's
|
||||
like calling ``mp.resume()`` until the player is actually resumed.)
|
||||
|
||||
You might want to call this if you're about to do something that takes a
|
||||
long time, but doesn't really need access to the player (like a network
|
||||
operation). Note that you still can access the player at any time.
|
||||
|
||||
``mp.get_wakeup_pipe()``
|
||||
Calls ``mpv_get_wakeup_pipe()`` and returns the read end of the wakeup
|
||||
pipe. (See ``client.h`` for details.)
|
||||
|
||||
``mp.get_next_timeout()``
|
||||
Return the relative time in seconds when the next timer (``mp.add_timeout``
|
||||
and similar) expires. If there is no timer, return ``nil``.
|
||||
|
||||
``mp.dispatch_events([allow_wait])``
|
||||
This can be used to run custom event loops. If you want to have direct
|
||||
control what the Lua script does (instead of being called by the default
|
||||
event loop), you can set the global variable ``mp_event_loop`` to your
|
||||
own function running the event loop. From your event loop, you should call
|
||||
``mp.dispatch_events()`` to dequeue and dispatch mpv events.
|
||||
|
||||
If the ``allow_wait`` parameter is set to ``true``, the function will block
|
||||
until the next event is received or the next timer expires. Otherwise (and
|
||||
this is the default behavior), it returns as soon as the event loop is
|
||||
emptied. It's strongly recommended to use ``mp.get_next_timeout()`` and
|
||||
``mp.get_wakeup_pipe()`` if you're interested in properly working
|
||||
notification of new events and working timers.
|
||||
|
||||
This function calls ``mp.suspend()`` and ``mp.resume_all()`` on its own.
|
||||
|
||||
``mp.enable_messages(level)``
|
||||
Set the minimum log level of which mpv message output to receive. These
|
||||
messages are normally printed to the terminal. By calling this function,
|
||||
you can set the minimum log level of messages which should be received with
|
||||
the ``log-message`` event. See the description of this event for details.
|
||||
The level is a string, see ``msg.log`` for allowed log levels.
|
||||
|
||||
``mp.register_script_message(name, fn)``
|
||||
This is a helper to dispatch ``script_message`` or ``script_message_to``
|
||||
invocations to Lua functions. ``fn`` is called if ``script_message`` or
|
||||
``script_message_to`` (with this script as destination) is run
|
||||
with ``name`` as first parameter. The other parameters are passed to ``fn``.
|
||||
If a message with the given name is already registered, it's overwritten.
|
||||
|
||||
Used by ``mp.add_key_binding``, so be careful about name collisions.
|
||||
|
||||
``mp.unregister_script_message(name)``
|
||||
Undo a previous registration with ``mp.register_script_message``. Does
|
||||
nothing if the ``name`` wasn't registered.
|
||||
|
||||
mp.msg functions
|
||||
----------------
|
||||
|
||||
This module allows outputting messages to the terminal, and can be loaded
|
||||
with ``require 'mp.msg'``.
|
||||
|
||||
``msg.log(level, ...)``
|
||||
The level parameter is the message priority. It's a string and one of
|
||||
``fatal``, ``error``, ``warn``, ``info``, ``v``, ``debug``. The user's
|
||||
settings will determine which of these messages will be visible. Normally,
|
||||
all messages are visible, except ``v`` and ``debug``.
|
||||
|
||||
The parameters after that are all converted to strings. Spaces are inserted
|
||||
to separate multiple parameters.
|
||||
|
||||
You don't need to add newlines.
|
||||
|
||||
``msg.fatal(...)``, ``msg.error(...)``, ``msg.warn(...)``, ``msg.info(...)``, ``msg.verbose(...)``, ``msg.debug(...)``
|
||||
All of these are shortcuts and equivalent to the corresponding
|
||||
``msg.log(level, ...)`` call.
|
||||
|
||||
mp.options functions
|
||||
--------------------
|
||||
|
||||
mpv comes with a built-in module to manage options from config-files and the
|
||||
command-line. All you have to do is to supply a table with default options to
|
||||
the read_options function. The function will overwrite the default values
|
||||
with values found in the config-file and the command-line (in that order).
|
||||
|
||||
``options.read_options(table [, identifier])``
|
||||
A ``table`` with key-value pairs. The type of the default values is
|
||||
important for converting the values read from the config file or
|
||||
command-line back. Do not use ``nil`` as a default value!
|
||||
|
||||
The ``identifier`` is used to identify the config-file and the command-line
|
||||
options. These needs to unique to avoid collisions with other scripts.
|
||||
Defaults to ``mp.get_script_name()``.
|
||||
|
||||
|
||||
Example implementation::
|
||||
|
||||
require 'mp.options'
|
||||
local options = {
|
||||
optionA = "defaultvalueA",
|
||||
optionB = -0.5,
|
||||
optionC = true,
|
||||
}
|
||||
options.read_options(options, "myscript")
|
||||
print(option.optionA)
|
||||
|
||||
|
||||
The config file will be stored in ``lua-settings/identifier.conf`` in mpv's user
|
||||
folder. Comment lines can be started with # and stray spaces are not removed.
|
||||
Boolean values will be represented with yes/no.
|
||||
|
||||
Example config::
|
||||
|
||||
# comment
|
||||
optionA=Hello World
|
||||
optionB=9999
|
||||
optionC=no
|
||||
|
||||
|
||||
Command-line options are read from the ``--lua-opts`` parameter. To avoid
|
||||
collisions, all keys have to be prefixed with ``identifier-``.
|
||||
|
||||
Example command-line::
|
||||
|
||||
--lua-opts=myscript-optionA=TEST:myscript-optionB=0:myscript-optionC=yes
|
||||
|
||||
|
||||
mp.utils options
|
||||
----------------
|
||||
|
||||
This built-in module provides generic helper functions for Lua, and have
|
||||
strictly speaking nothing to do with mpv or video/audio playback. They are
|
||||
provided for convenience. Most compensate for Lua's scarce standard library.
|
||||
|
||||
Be warned that any of these functions might disappear any time. They are not
|
||||
strictly part of the guaranteed API.
|
||||
|
||||
``utils.getcwd()``
|
||||
Returns the directory that mpv was launched from. On error, ``nil, error``
|
||||
is returned.
|
||||
|
||||
``utils.readdir(path [, filter])``
|
||||
Enumerate all entries at the given path on the filesystem, and return them
|
||||
as array. Each entry is a directory entry (without the path).
|
||||
The list is unsorted (in whatever order the operating system returns it).
|
||||
|
||||
If the ``filter`` argument is given, it must be one of the following
|
||||
strings:
|
||||
|
||||
``files``
|
||||
List regular files only. This excludes directories, special files
|
||||
(like UNIX device files or FIFOs), and dead symlinks. It includes
|
||||
UNIX symlinks to regular files.
|
||||
|
||||
``dirs``
|
||||
List directories only, or symlinks to directories. ``.`` and ``..``
|
||||
are not included.
|
||||
|
||||
``normal``
|
||||
Include the results of both ``files`` and ``dirs``. (This is the
|
||||
default.)
|
||||
|
||||
``all``
|
||||
List all entries, even device files, dead symlinks, FIFOs, and the
|
||||
``.`` and ``..`` entries.
|
||||
|
||||
On error, ``nil, error`` is returned.
|
||||
|
||||
``utils.split_path(path)``
|
||||
Split a path into directory component and filename component, and return
|
||||
them. The first return value is always the directory. The second return
|
||||
value is the trailing part of the path, the directory entry.
|
||||
|
||||
``utils.join_path(p1, p2)``
|
||||
Return the concatenation of the 2 paths. Tries to be clever. For example,
|
||||
if ```p2`` is an absolute path, p2 is returned without change.
|
||||
|
||||
``utils.subprocess(t)``
|
||||
Runs an external process and waits until it exits. Returns process status
|
||||
and the captured output.
|
||||
|
||||
The parameter ``t`` is a table. The function reads the following entries:
|
||||
|
||||
``args``
|
||||
Array of strings. The first array entry is the executable. This
|
||||
can be either an absolute path, or a filename with no path
|
||||
components, in which case the ``PATH`` environment variable is
|
||||
used to resolve the executable. The other array elements are
|
||||
passed as command line arguments.
|
||||
|
||||
``cancellable``
|
||||
Optional. If set to ``true`` (default), then if the user stops
|
||||
playback or goes to the next file while the process is running,
|
||||
the process will be killed.
|
||||
|
||||
``max_size``
|
||||
Optional. The maximum size in bytes of the data that can be captured
|
||||
from stdout. (Default: 16 MB.)
|
||||
|
||||
The function returns a table as result with the following entries:
|
||||
|
||||
``status``
|
||||
The raw exit status of the process. It will be negative on error.
|
||||
|
||||
``stdout``
|
||||
Captured output stream as string, limited to ``max_size``.
|
||||
|
||||
``error``
|
||||
``nil`` on success. The string ``killed`` if the process was
|
||||
terminated in an unusual way. The string ``init`` if the process
|
||||
could not be started.
|
||||
|
||||
On Windows, ``killed`` is only returned when the process has been
|
||||
killed by mpv as a result of ``cancellable`` being set to ``true``.
|
||||
|
||||
In all cases, ``mp.resume_all()`` is implicitly called.
|
||||
|
||||
``utils.parse_json(str [, trail])``
|
||||
Parses the given string argument as JSON, and returns it as a Lua table. On
|
||||
error, returns ``nil, error``. (Currently, ``error`` is just a string
|
||||
reading ``error``, because there is no fine-grained error reporting of any
|
||||
kind.)
|
||||
|
||||
The returned value uses similar conventions as ``mp.get_property_native()``
|
||||
to distinguish empty objects and arrays.
|
||||
|
||||
If the ``trail`` parameter is ``true`` (or any value equal to ``true``),
|
||||
then trailing non-whitespace text is tolerated by the function, and the
|
||||
trailing text is returned as 3rd return value. (The 3rd return value is
|
||||
always there, but with ``trail`` set, no error is raised.)
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
Events are notifications from player core to scripts. You can register an
|
||||
event handler with ``mp.register_event``.
|
||||
|
||||
Note that all scripts (and other parts of the player) receive events equally,
|
||||
and there's no such thing as blocking other scripts from receiving events.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function my_fn(event)
|
||||
print("start of playback!")
|
||||
end
|
||||
|
||||
mp.register_event("playback-start", my_fn)
|
||||
|
||||
|
||||
|
||||
List of events
|
||||
--------------
|
||||
|
||||
``start-file``
|
||||
Happens right before a new file is loaded. When you receive this, the
|
||||
player is loading the file (or possibly already done with it).
|
||||
|
||||
``end-file``
|
||||
Happens after a file was unloaded. Typically, the player will load the
|
||||
next file right away, or quit if this was the last file.
|
||||
|
||||
``file-loaded``
|
||||
Happens after a file was loaded and begins playback.
|
||||
|
||||
``seek``
|
||||
Happens on seeking (including ordered chapter segment changes).
|
||||
|
||||
``playback-restart``
|
||||
Start of playback after seek or after file was loaded.
|
||||
|
||||
``idle``
|
||||
Idle mode is entered. This happens when playback ended, and the player was
|
||||
started with ``--idle`` or ``--force-window``. This mode is implicitly ended
|
||||
when the ``start-file`` or ``shutdown`` events happen.
|
||||
|
||||
``tick``
|
||||
Called after a video frame was displayed. This is a hack, and you should
|
||||
avoid using it. Use timers instead and maybe watch pausing/unpausing events
|
||||
to avoid wasting CPU when the player is paused.
|
||||
|
||||
``shutdown``
|
||||
Sent when the player quits, and the script should terminate. Normally
|
||||
handled automatically. See `Mode of operation`_.
|
||||
|
||||
``log-message``
|
||||
Receives messages enabled with ``mp.enable_messages``. The message data
|
||||
is contained in the table passed as first parameter to the event handler.
|
||||
The table contains, in addition to the default event fields, the following
|
||||
fields:
|
||||
|
||||
``prefix``
|
||||
The module prefix, identifies the sender of the message. This is what
|
||||
the terminal player puts in front of the message text when using the
|
||||
``--v`` option, and is also what is used for ``--msg-level``.
|
||||
|
||||
``level``
|
||||
The log level as string. See ``msg.log`` for possible log level names.
|
||||
Note that later versions of mpv might add new levels or remove
|
||||
(undocumented) existing ones.
|
||||
|
||||
``text``
|
||||
The log message. Note that this is the direct output of a printf()
|
||||
style output API. The text will contain embedded newlines, and it's
|
||||
possible that a single message contains multiple lines, or that a
|
||||
message contains a partial line.
|
||||
|
||||
It's safe to display messages only if they end with a newline character,
|
||||
and to buffer them otherwise.
|
||||
|
||||
Keep in mind that these messages are meant to be hints for humans. You
|
||||
should not parse them, and prefix/level/text of messages might change
|
||||
any time.
|
||||
|
||||
``get-property-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``set-property-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``command-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``script-input-dispatch``
|
||||
Undocumented (used internally).
|
||||
|
||||
``client-message``
|
||||
Undocumented (used internally).
|
||||
|
||||
``video-reconfig``
|
||||
Happens on video output or filter reconfig.
|
||||
|
||||
``audio-reconfig``
|
||||
Happens on audio output or filter reconfig.
|
||||
|
||||
The following events also happen, but are deprecated: ``tracks-changed``,
|
||||
``track-switched``, ``pause``, ``unpause``, ``metadata-update``,
|
||||
``chapter-change``. Use ``mp.observe_property()`` instead.
|
||||
|
||||
Extras
|
||||
------
|
||||
|
||||
This documents experimental features, or features that are "too special" and
|
||||
we don't guarantee a stable interface to it.
|
||||
|
||||
``mp.add_hook(type, priority, fn)``
|
||||
Add a hook callback for ``type`` (a string identifying a certain kind of
|
||||
hook). These hooks allow the player to call script functions and wait for
|
||||
their result (normally, the Lua scripting interface is asynchronous from
|
||||
the point of view of the player core). ``priority`` is an arbitrary integer
|
||||
that allows ordering among hooks of the same kind. Using the value 50 is
|
||||
recommended as neutral default value. ``fn`` is the function that will be
|
||||
called during execution of the hook.
|
||||
|
||||
Currently existing hooks:
|
||||
|
||||
``on_load``
|
||||
Called when a file is to be opened, before anything is actually done.
|
||||
For example, you could read and write the ``stream-open-filename``
|
||||
property to redirect an URL to something else (consider support for
|
||||
streaming sites which rarely give the user a direct media URL), or
|
||||
you could set per-file options with by setting the property
|
||||
``file-local-options/<option name>``. The player will wait until all
|
||||
hooks are run.
|
||||
@@ -1,71 +0,0 @@
|
||||
Release Policy
|
||||
==============
|
||||
|
||||
Every few months, a new release is cut off of the master branch and is assigned
|
||||
a 0.X.0 version number.
|
||||
|
||||
As part of the maintenance process, minor releases are made, which are assigned
|
||||
0.X.Y version numbers. Minor releases contain bug fixes only. They never merge
|
||||
the master branch, and no features are added to it. Only the latest release is
|
||||
maintained.
|
||||
|
||||
The goal of releases is to provide stability and an unchanged base for the sake
|
||||
of Linux distributions. If you want the newest features, just use the master
|
||||
branch, which is stable most of the time, except sometimes, when it's not.
|
||||
|
||||
Releases other than the latest release are unsupported and unmaintained.
|
||||
|
||||
Release procedure
|
||||
-----------------
|
||||
|
||||
- Create branch release/0.X or cherry-pick commits to the relevant branch.
|
||||
|
||||
- Create and/or update the ``RELEASE_NOTES`` file.
|
||||
|
||||
- Create and/or update the ``VERSION`` file.
|
||||
|
||||
- Update ``DOCS/client-api-changes.rst`` (on major releases).
|
||||
|
||||
- Create tag v0.X.Y.
|
||||
|
||||
- Push branch and tag to GitHub.
|
||||
|
||||
- Create a new GitHub release using the content of ``RELEASE_NOTES`` related to
|
||||
the new version.
|
||||
|
||||
Release notes template
|
||||
----------------------
|
||||
|
||||
Here is a template that can be used for writing the ``RELEASE_NOTES`` file.
|
||||
|
||||
```markdown
|
||||
Release 0.X.Y
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- List of changes.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- List of bug fixes.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- List of new features.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log <start>..<end>`` in
|
||||
the git repository.
|
||||
```
|
||||
|
||||
Note that the "Release 0.X.Y" title should be removed when creating a new GitHub
|
||||
release.
|
||||
|
||||
When creating a new point release its changes should be added on top of the
|
||||
``RELEASE_NOTES`` file (with the appropriate title) so that all the changes in
|
||||
the current 0.X branch will be included. This way the ``RELEASE_NOTES`` file
|
||||
can be used by distributors as changelog for point releases too.
|
||||
+54
-81
@@ -1,11 +1,12 @@
|
||||
This file intends to give a big picture overview of how mpv is structured.
|
||||
NOTE: DOCS/OUTDATED-tech/* may contain more detailed information, but most of it
|
||||
is possibly or definitely outdated. This file intends to give a big
|
||||
picture of how mpv is structured.
|
||||
|
||||
player/*.c:
|
||||
Essentially makes up the player applications, including the main() function
|
||||
and the playback loop.
|
||||
|
||||
Generally, it accesses all other subsystems, initializes them, and pushes
|
||||
data between them during playback.
|
||||
mpvcore/mplayer.c:
|
||||
This contains the main play loop, anything related to mpv and playback
|
||||
related initializations. It also contains the main function. Generally, it
|
||||
accesses all other subsystems, initializes them, and pushes data between
|
||||
them during playback.
|
||||
|
||||
The structure is as follows (as of commit e13c05366557cb):
|
||||
* main():
|
||||
@@ -17,12 +18,12 @@ player/*.c:
|
||||
* check help options etc. (call handle_help_options()), possibly exit
|
||||
* call play_files() function that works down the playlist:
|
||||
* run idle loop (idle_loop()), until there are files in the
|
||||
playlist or an exit command was given (only if --idle it set)
|
||||
playlist or an exit command was given (slave mode only)
|
||||
* actually load and play a file in play_current_file():
|
||||
* run all the dozens of functions to load the file and
|
||||
initialize playback
|
||||
* run a small loop that does normal playback, until the file is
|
||||
done or a command terminates playback
|
||||
done or a slave command terminates playback
|
||||
(on each iteration, run_playloop() is called, which is rather
|
||||
big and complicated - it decodes some audio and video on
|
||||
each frame, waits for input, etc.)
|
||||
@@ -31,30 +32,17 @@ player/*.c:
|
||||
* loop, or exit if no next file or quit is requested
|
||||
(see enum stop_play_reason)
|
||||
* call exit_player_with_rc()
|
||||
* run_playloop():
|
||||
* calls fill_audio_out_buffers()
|
||||
This checks whether new audio needs to be decoded, and pushes it
|
||||
to the AO.
|
||||
* calls write_video()
|
||||
Decode new video, and push it to the VO.
|
||||
* determines whether playback of the current file has ended
|
||||
* determines when to start playback after seeks
|
||||
* and calls a whole lot of other stuff
|
||||
(Really, this function does everything.)
|
||||
|
||||
Things worth saying about the playback core:
|
||||
- most state is in MPContext (core.h), which is not available to the
|
||||
subsystems
|
||||
- the currently played tracks are in mpctx->current_tracks, and decoder
|
||||
state in d_video/d_audio/d_sub
|
||||
- the currently played tracks are in sh_video and sh_audio
|
||||
- the timeline stuff is used only with MKV ordered chapters (and some other
|
||||
minor features: cue, edl)
|
||||
- most state is in MPContext (mp_core.h), which is not available to the
|
||||
subsystems
|
||||
- the other subsystems rarely call back into the frontend, and the frontend
|
||||
polls them instead (probably a good thing)
|
||||
- one exceptions are wakeup callbacks, which notify a "higher" component
|
||||
of a changed situation in a subsystem
|
||||
|
||||
I like to call the player/*.c files the "frontend".
|
||||
I like to call mplayer.c (and some other files) the "frontend".
|
||||
|
||||
talloc.h & talloc.c:
|
||||
Hierarchical memory manager copied from Samba. It's like a malloc() with
|
||||
@@ -94,48 +82,38 @@ talloc.h & talloc.c:
|
||||
rid of the talloc emulation later and use TA natively.
|
||||
(See ta/README for details.)
|
||||
|
||||
player/command.c:
|
||||
This contains the implementation for client API commands and properties.
|
||||
Properties are essentially dynamic variables changed by certain commands.
|
||||
This is basically responsible for all user commands, like initiating
|
||||
seeking, switching tracks, etc. It calls into other player/*.c files,
|
||||
where most of the work is done, but also calls other parts of mpv.
|
||||
mpvcore/mp_core.h:
|
||||
Data structures for mplayer.c and command.c. They are usually not accessed
|
||||
by other parts of mpv for the sake of modularization.
|
||||
|
||||
player/core.h:
|
||||
Data structures and function prototypes for most of player/*.c. They are
|
||||
usually not accessed by other parts of mpv for the sake of modularization.
|
||||
Note that there are lots of global variables floating around everywhere
|
||||
else. This is an ongoing transition, and eventually there should be no
|
||||
global variables anymore.
|
||||
|
||||
player/client.c:
|
||||
This implements the client API (libmpv/client.h). For the most part, this
|
||||
just calls into other parts of the player. This also manages a ringbuffer
|
||||
of events from player to clients.
|
||||
options.h contains the global option struct MPOpts, and its default values
|
||||
are in defaultopts.c for some reason.
|
||||
|
||||
options/options.h, options/options.c
|
||||
options.h contains the global option struct MPOpts. The option declarations
|
||||
(option names, types, and MPOpts offsets for the option parser) are in
|
||||
options.c. Most default values for options and MPOpts are in
|
||||
mp_default_opts at the end of options.c.
|
||||
|
||||
MPOpts is unfortunately quite monolithic, and virtually accessed by
|
||||
everything.But some components (like video outputs and video filters) have
|
||||
their own sub-option tables separate from MPOpts.
|
||||
|
||||
The actual option parser is spread over m_option.c, m_config.c, and
|
||||
parser-mpcmd.c, and uses the option table in options.c.
|
||||
|
||||
input/input.c:
|
||||
This translates keyboard input comming from VOs and other sources (such
|
||||
as remote control devices like Apple IR or client API commands) to the
|
||||
mpvcore/input/input.c:
|
||||
This translates keyboard input comming from libvo and other sources (such
|
||||
as remote control devices like Apple IR or slave mode commands) to the
|
||||
key bindings listed in the user's (or the builtin) input.conf and turns
|
||||
them into items of type struct mp_cmd. These commands are queued, and read
|
||||
by playloop.c. They get pushed with run_command() to command.c.
|
||||
by mplayer.c. They get pushed with run_command() to command.c.
|
||||
|
||||
Note that keyboard input and commands used by the client API are the same.
|
||||
The client API only uses the command parser though, and has its own queue
|
||||
of input commands somewhere else.
|
||||
Note that keyboard input and slave mode input are essentially the same
|
||||
things. Just looking at input.conf should make this clear. (The other
|
||||
direction of slave mode communication, mpv to application, consists of
|
||||
random mp_msg() calls all over the code in all parts of the player.)
|
||||
|
||||
common/msg.h:
|
||||
All terminal output must go through mp_msg().
|
||||
mpvcore/command.c:
|
||||
This contains the implementation for slave commands and properties.
|
||||
Properties are essentially dynamic variables changed by certain commands.
|
||||
This is basically responsible for all user commands, like initiating
|
||||
seeking, switching tracks, etc. It calls into mplayer.c, where most of the
|
||||
work is done, but also into other parts of mpv.
|
||||
|
||||
mpvcore/mp_msg.h:
|
||||
All terminal output should go through mp_msg().
|
||||
|
||||
stream/*:
|
||||
File input is implemented here. stream.h/.c provides a simple stream based
|
||||
@@ -194,7 +172,7 @@ video/out/:
|
||||
vo_opengl can pick a windowing system at runtime, e.g. the same binary can
|
||||
provide both X11 and Cocoa support on OSX.
|
||||
|
||||
VOs can be reconfigured at runtime. A vo_config() call can change the video
|
||||
VOs can be reconfigured at runtime. A config() call can change the video
|
||||
resolution and format, without destroying the window.
|
||||
|
||||
vo_vdpau and vo_opengl should be taken as reference.
|
||||
@@ -210,18 +188,15 @@ audio/decode/:
|
||||
|
||||
audio/filter/:
|
||||
Audio filter chain. af_lavrresample is inserted if any form of conversion
|
||||
between audio formats is needed. (af_convert24.c and af_convertsignendian.c
|
||||
are also used for some formats not directly supported by FFmpeg.)
|
||||
between audio formats is needed. (af_format.c is also used for some corner
|
||||
cases.)
|
||||
|
||||
audio/out/:
|
||||
Audio outputs.
|
||||
|
||||
Unlike VOs, AOs can't be reconfigured on a format change. On audio format
|
||||
changes, the AO will simply be closed and re-opened.
|
||||
|
||||
There are wrappers to support for two types of audio APIs: push.c and
|
||||
pull.c. ao.c calls into one of these. They contain generic code to deal
|
||||
with the data flow these APIs impose.
|
||||
Unlike VOs, AOs can't be reconfigured on a format change. Without
|
||||
--gapless-audio, even playing a new file will close and re-open the audio
|
||||
device.
|
||||
|
||||
Note that mpv synchronizes the video to the audio. That's the reason
|
||||
why buggy audio drivers can have a bad influence on playback quality.
|
||||
@@ -229,19 +204,17 @@ audio/out/:
|
||||
sub/:
|
||||
Contains subtitle and OSD rendering.
|
||||
|
||||
osd.c/.h is actually the OSD code. It queries dec_sub.c to retrieve
|
||||
sub.c/.h is actually the OSD code. It queries dec_sub.c to retrieve
|
||||
decoded/rendered subtitles. osd_libass.c is the actual implementation of
|
||||
the OSD text renderer (which uses libass, and takes care of all the tricky
|
||||
fontconfig/freetype API usage and text layouting).
|
||||
|
||||
The VOs call osd.c to render OSD and subtitle (via e.g. osd_draw()). osd.c
|
||||
in turn asks dec_sub.c for subtitle overlay bitmaps, which relays the
|
||||
request to one of the sd_*.c subtitle decoders/renderers.
|
||||
|
||||
Subtitle loading is in demux/. The MPlayer subreader.c is mostly gone - parts
|
||||
of it survive in demux_subreader.c. It's used as last fallback, or to handle
|
||||
some text subtitle types on Libav. It should go away eventually. Normally,
|
||||
subtitles are loaded via demux_lavf.c.
|
||||
Subtitle loading is now in demux/ instead. demux_libass.c wraps loading
|
||||
.ass subtitles via libass. demux_lavf.c loads most subtitle types via
|
||||
FFmpeg. demux_subreader.c is the old MPlayer code. It's used as last
|
||||
fallback, or to handle some text subtitle types on Libav. (It also can
|
||||
load UTF-16 encoded subtitles without requiring the use of -subcp.)
|
||||
demux_subreader.c should eventually go away (maybe).
|
||||
|
||||
The subtitles are passed to dec_sub.c and the subtitle decoders in sd_*.c
|
||||
as they are demuxed. All text subtitles are rendered by sd_ass.c. If text
|
||||
@@ -256,8 +229,8 @@ sub/:
|
||||
detection as well as timing postprocessing work. (Timing postprocessing
|
||||
removes tiny gaps or overlaps between subtitle events.)
|
||||
|
||||
player/timeline/:
|
||||
A timeline is the abstraction used by loadfile.c to combine several files
|
||||
mpvcore/timeline/:
|
||||
A timeline is the abstraction used by mplayer.c to combine several files
|
||||
into one seemingly linear video. It's mainly used for ordered chapters
|
||||
playback. The high level code to find and load other files containing the
|
||||
segments for playing an ordered chapters file is in tl_matroska.c.
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
waf build system overview
|
||||
=========================
|
||||
|
||||
mpv's new build system is based on waf and it should completly replace the
|
||||
custom ./configure + Makefile based system inherited from MPlayer.
|
||||
|
||||
Goals and the choice of waf
|
||||
===========================
|
||||
|
||||
The new system comes with some goals, which can be summed up as: be as good as
|
||||
the old one at what it did well (customizability) and fix some of it's major
|
||||
shortcomings:
|
||||
|
||||
1) The build system must be uniform in how it handles any single feature check.
|
||||
Repetition and boilerplate have to be avoided.
|
||||
|
||||
When adding a new feature using the old configure, one had to add a fair
|
||||
amount of code to the shell script to do option parsing, detection of the
|
||||
feature and declaration of variables for the Makefile to pickup. The worst
|
||||
part is this pieces are spread apart in the configure and copy pasted for
|
||||
any single case. That brings us to..
|
||||
|
||||
2) --enable-feature has to override the user and help him understand that he
|
||||
has libraries missing and should install them for the feature to be enabled.
|
||||
|
||||
3) Must be customizable, hackable, pleasant to the developer eyes and to work
|
||||
with in general.
|
||||
|
||||
4) Must have separate configuration and build steps.
|
||||
|
||||
Goal 2 comes as a given on pretty much any build system, since autotools made
|
||||
this behaviour very popular among users (and rightly so).
|
||||
|
||||
Goal 1+3 were somewhat harder to accomplish as it looks like all of the build
|
||||
systems we evaluated (waf included!) had problems with them. For reference we
|
||||
had proof of concept build systems with waf, CMake and autotools.
|
||||
|
||||
What puts waf apart from CMake and autotools, is that projects using it use
|
||||
Python to program their build system. Also while the Waf Book shows really
|
||||
simple API usages, you can write your own build system on top of waf that is
|
||||
tailored to the project's specific needs.
|
||||
|
||||
mpv's custom configure step on top of waf
|
||||
=========================================
|
||||
|
||||
To some extents mpv has a custom build system written on top of waf. This
|
||||
document will not go over the standard waf behaviour as that is documented in
|
||||
the `Waf book <http://docs.waf.googlecode.com/git/book_17/single.html>`_.
|
||||
|
||||
All of the configuration process is handled with a declarative approach. Lists
|
||||
of dictionaries define the checks, and some custom Python code traverses these
|
||||
lists and depending on the check definition it calls into the actual waf API.
|
||||
|
||||
A simple example using pkg-config would be::
|
||||
|
||||
{
|
||||
'name': '--vdpau',
|
||||
'desc': 'VDPAU acceleration',
|
||||
'deps': [ 'x11' ],
|
||||
'func': check_pkg_config('vdpau', '>= 0.2'),
|
||||
}
|
||||
|
||||
This defines a feature called ``vdpau`` which can be enabled or disabled by
|
||||
the users with configure flags (that's the meaning of ``--``). This feature
|
||||
depends on another feature whose name is ``x11``, and the autodetection check
|
||||
consists of running ``pkg-config`` and looking for ``vdpau`` with version
|
||||
``>= 0.2``. If the check succeds a ``#define HAVE_VDPAU 1`` will be added to
|
||||
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.
|
||||
|
||||
The defines names are automatically prepended with ``HAVE_``, capitalized and
|
||||
special characters are replaced with underscores. This happens in
|
||||
``waftools/inflectors.py``.
|
||||
|
||||
Mandatory fields:
|
||||
-----------------
|
||||
|
||||
``name``: indicates the unique identifier used by the custom dependency code
|
||||
to refer to a feature. If the unique identifier is prepended with ``--``
|
||||
the build system will also generate options for ``./waf configure`` so that
|
||||
the feature can be enabled and disabled.
|
||||
|
||||
``desc``: this is the textual representation of the feature used in the
|
||||
interactions with the users.
|
||||
|
||||
``func``: function that will perform the check. These functions are defined in
|
||||
``waftools/checks``. The reusable checks are all functions that return
|
||||
functions. The return functions will then be applied using waf's configuration
|
||||
context.
|
||||
|
||||
The source code for the reusable checks is a bit convoluted, but it should be
|
||||
easy to pick up their usage from the ``wscript``. Their signature mirrors
|
||||
the semantics of some of the shell functions used in mplayer.
|
||||
|
||||
If someone expresses some interest, I will extend this document with official
|
||||
documentation for each check function.
|
||||
|
||||
Optional fields
|
||||
---------------
|
||||
|
||||
``deps``: list of dependencies of this feature. It is a list of names of
|
||||
other features as defined in the ``name`` field (minus the eventual leading
|
||||
``--``). All of the dependencies must be satisfied. If they are not the check
|
||||
will be skipped without even running ``func``.
|
||||
|
||||
``deps_any``: like deps but it is satisfied even if only one of the dependencies
|
||||
is satisfied. You can think of ``deps`` as a 'and' condition and ``deps_any``
|
||||
as a 'or' condition.
|
||||
|
||||
``deps_neg``: like deps but it is satisfied when none of the dependencies is
|
||||
satisfied.
|
||||
|
||||
``req``: defaults to False. If set to True makes this feature a hard
|
||||
dependency of mpv (configuration will fail if autodetection fails). If set to
|
||||
True you must also provide ``fmsg``.
|
||||
|
||||
``fmsg``: string with the failure message in case a required dependency is not
|
||||
satisfied.
|
||||
|
||||
``os_specific_checks``: this takes a dictionary that has ``os-`` dependencies
|
||||
as keys (such as ``os-win32``), and by values has another dictionary that is
|
||||
merged on top of the current feature definition only for that specific OS.
|
||||
For example::
|
||||
|
||||
{
|
||||
'name': '--pthreads',
|
||||
'desc': 'POSIX threads',
|
||||
'func': check_pthreads,
|
||||
'os_specific_checks': {
|
||||
'os-win32': {
|
||||
'func': check_pthreads_w32_static.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
will override the value of ``func`` with ``check_pthreads_w32_static`` only
|
||||
if the target OS of the build is Windows.
|
||||
|
||||
``groups``: groups a dependency with another one. This can be used to disabled
|
||||
all the grouped dependencies with one ``--disable-``. At the moment this is
|
||||
only used for OpenGL backends, where you want to disable them when
|
||||
``--disable-gl`` is passed to the configure.
|
||||
|
||||
mpv's custom build step on top of waf
|
||||
=====================================
|
||||
|
||||
Build step is pretty much vanilla waf. The only difference being that the list
|
||||
of source files can contain both strings or tuples. If a tuple is found,
|
||||
the second element in the tuple will the used to match the features detected
|
||||
in the configure step (the ``name`` field described above). If this feature
|
||||
was not enabled during configure, the source file will not be compiled in.
|
||||
|
||||
All of the custom Python for this is inside the function ``filtered_sources``
|
||||
contained in the file ``waftools/dependencies.py``.
|
||||
|
||||
Also ``dependencies_use`` and ``dependencies_includes`` collect cflags and
|
||||
ldflags that were generated from the features checks in the configure step.
|
||||
+186
-149
@@ -19,22 +19,22 @@
|
||||
# with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include old_build/config.mak
|
||||
include config.mak
|
||||
|
||||
###### variable declarations #######
|
||||
|
||||
SOURCES_AUDIO_INPUT-$(ALSA) += stream/ai_alsa1x.c
|
||||
SOURCES_AUDIO_INPUT-$(OSS_AUDIO)+= stream/ai_oss.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
|
||||
SOURCES-$(CDDA) += stream/stream_cdda.c \
|
||||
stream/cdinfo.c
|
||||
SOURCES-$(DVBIN) += stream/dvb_tune.c \
|
||||
stream/stream_dvb.c
|
||||
SOURCES-$(DVDREAD) += stream/stream_dvd.c \
|
||||
stream/stream_dvd_common.c
|
||||
SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
|
||||
stream/stream_dvd_common.c
|
||||
|
||||
SOURCES-$(HAVE_SYS_MMAN_H) += audio/filter/af_export.c
|
||||
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
|
||||
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
|
||||
demux/demux_libass.c
|
||||
@@ -45,21 +45,48 @@ SOURCES-$(LIBBS2B) += audio/filter/af_bs2b.c
|
||||
SOURCES-$(LIBPOSTPROC) += video/filter/vf_pp.c
|
||||
SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c
|
||||
|
||||
SOURCES-$(COCOA) += video/out/cocoa/view.m \
|
||||
video/out/cocoa/window.m \
|
||||
video/out/cocoa/additions.m \
|
||||
video/out/cocoa_common.m \
|
||||
osdep/macosx_application.m \
|
||||
osdep/macosx_events.m \
|
||||
osdep/ar/HIDRemote.m \
|
||||
osdep/path-macosx.m
|
||||
|
||||
SOURCES-$(MNG) += demux/demux_mng.c
|
||||
SOURCES-$(MPG123) += audio/decode/ad_mpg123.c
|
||||
|
||||
SOURCES-$(NEED_GETTIMEOFDAY) += osdep/gettimeofday.c
|
||||
SOURCES-$(NEED_GLOB) += osdep/glob-win.c
|
||||
SOURCES-$(WIN32) += osdep/path-win.c
|
||||
|
||||
SOURCES-$(PRIORITY) += osdep/priority.c
|
||||
SOURCES-$(PVR) += stream/stream_pvr.c
|
||||
SOURCES-$(RADIO) += stream/stream_radio.c
|
||||
SOURCES-$(RADIO_CAPTURE) += stream/audio_in.c
|
||||
SOURCES-$(STREAM_CACHE) += stream/cache.c
|
||||
|
||||
SOURCES-$(TV) += stream/stream_tv.c stream/tv.c \
|
||||
stream/frequencies.c stream/tvi_dummy.c \
|
||||
demux/demux_tv.c
|
||||
stream/frequencies.c stream/tvi_dummy.c
|
||||
|
||||
SOURCES-$(TV_V4L2) += stream/tvi_v4l2.c stream/audio_in.c
|
||||
SOURCES-$(VCD) += stream/stream_vcd.c
|
||||
SOURCES-$(DUMMY_OSD) += sub/osd_dummy.c
|
||||
SOURCES-$(LIBASS_OSD) += sub/osd_libass.c
|
||||
|
||||
SOURCES-$(ALSA) += audio/out/ao_alsa.c
|
||||
SOURCES-$(CACA) += video/out/vo_caca.c
|
||||
SOURCES-$(SDL2) += audio/out/ao_sdl.c video/out/vo_sdl.c
|
||||
SOURCES-$(SDL) += audio/out/ao_sdl.c
|
||||
SOURCES-$(SDL2) += video/out/vo_sdl.c
|
||||
SOURCES-$(COREAUDIO) += audio/out/ao_coreaudio.c \
|
||||
audio/out/ao_coreaudio_utils.c \
|
||||
audio/out/ao_coreaudio_properties.c
|
||||
SOURCES-$(COREVIDEO) += video/out/vo_corevideo.c
|
||||
SOURCES-$(DIRECT3D) += video/out/vo_direct3d.c \
|
||||
video/out/w32_common.c
|
||||
SOURCES-$(DSOUND) += audio/out/ao_dsound.c
|
||||
SOURCES-$(WASAPI) += audio/out/ao_wasapi.c
|
||||
SOURCES-$(GL) += video/out/gl_common.c video/out/gl_osd.c \
|
||||
video/out/vo_opengl.c video/out/gl_lcms.c \
|
||||
video/out/gl_video.c video/out/dither.c \
|
||||
@@ -67,67 +94,67 @@ SOURCES-$(GL) += video/out/gl_common.c video/out/gl_osd.c \
|
||||
video/out/pnm_loader.c
|
||||
|
||||
SOURCES-$(ENCODING) += video/out/vo_lavc.c audio/out/ao_lavc.c \
|
||||
common/encode_lavc.c
|
||||
mpvcore/encode_lavc.c
|
||||
|
||||
SOURCES-$(GL_WIN32) += video/out/w32_common.c video/out/gl_w32.c
|
||||
SOURCES-$(GL_X11) += video/out/x11_common.c video/out/gl_x11.c
|
||||
SOURCES-$(EGL_X11) += video/out/x11_common.c video/out/gl_x11egl.c
|
||||
SOURCES-$(GL_COCOA) += video/out/gl_cocoa.c
|
||||
SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
|
||||
video/out/gl_wayland.c
|
||||
|
||||
SOURCES-$(JACK) += audio/out/ao_jack.c
|
||||
SOURCES-$(JOYSTICK) += input/joystick.c
|
||||
SOURCES-$(LIRC) += input/lirc.c
|
||||
SOURCES-$(JOYSTICK) += mpvcore/input/joystick.c
|
||||
SOURCES-$(LIBQUVI) += mpvcore/resolve_quvi.c
|
||||
SOURCES-$(LIBQUVI9) += mpvcore/resolve_quvi9.c
|
||||
SOURCES-$(LIRC) += mpvcore/input/lirc.c
|
||||
SOURCES-$(OPENAL) += audio/out/ao_openal.c
|
||||
SOURCES-$(OSS_AUDIO) += audio/out/ao_oss.c
|
||||
SOURCES-$(OSS) += audio/out/ao_oss.c
|
||||
SOURCES-$(PULSE) += audio/out/ao_pulse.c
|
||||
SOURCES-$(PORTAUDIO) += audio/out/ao_portaudio.c
|
||||
SOURCES-$(RSOUND) += audio/out/ao_rsound.c
|
||||
SOURCES-$(SNDIO) += audio/out/ao_sndio.c
|
||||
SOURCES-$(VDPAU) += video/vdpau.c video/vdpau_mixer.c \
|
||||
video/out/vo_vdpau.c video/decode/vdpau.c \
|
||||
video/filter/vf_vdpaupp.c
|
||||
SOURCES-$(VDPAU_GL_X11) += video/out/gl_hwdec_vdpau.c
|
||||
SOURCES-$(VDPAU) += video/vdpau.c video/out/vo_vdpau.c
|
||||
SOURCES-$(VDA) += video/decode/vda.c
|
||||
SOURCES-$(VDPAU_DEC) += video/decode/vdpau.c
|
||||
SOURCES-$(VDPAU_DEC_OLD) += video/decode/vdpau_old.c
|
||||
SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
|
||||
video/decode/vaapi.c \
|
||||
video/vaapi.c
|
||||
SOURCES-$(VAAPI_VPP) += video/filter/vf_vavpp.c
|
||||
SOURCES-$(VAAPI_GLX) += video/out/gl_hwdec_vaglx.c
|
||||
|
||||
SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
|
||||
SOURCES-$(XV) += video/out/vo_xv.c
|
||||
SOURCES-$(WAYLAND) += video/out/vo_wayland.c \
|
||||
video/out/wayland_common.c \
|
||||
video/out/wayland/buffer.c \
|
||||
video/out/wayland/memfile.c
|
||||
SOURCES-$(WAYLAND) += video/out/vo_wayland.c video/out/wayland_common.c
|
||||
|
||||
SOURCES-$(LIBAVFILTER) += video/filter/vf_lavfi.c \
|
||||
audio/filter/af_lavfi.c
|
||||
SOURCES-$(VF_LAVFI) += video/filter/vf_lavfi.c
|
||||
SOURCES-$(AF_LAVFI) += audio/filter/af_lavfi.c
|
||||
|
||||
SOURCES-$(LUA) += mpvcore/mp_lua.c
|
||||
|
||||
ifeq ($(HAVE_AVUTIL_REFCOUNTING),no)
|
||||
SOURCES-yes += video/decode/lavc_dr1.c
|
||||
endif
|
||||
|
||||
SOURCES-$(LUA) += player/lua.c
|
||||
SOURCES-$(VAPOURSYNTH_CORE) += video/filter/vf_vapoursynth.c
|
||||
SOURCES-$(DLOPEN) += video/filter/vf_dlopen.c
|
||||
|
||||
SOURCES = audio/audio.c \
|
||||
audio/audio_buffer.c \
|
||||
audio/chmap.c \
|
||||
audio/chmap_sel.c \
|
||||
audio/fmt-conversion.c \
|
||||
audio/format.c \
|
||||
audio/mixer.c \
|
||||
audio/reorder_ch.c \
|
||||
audio/decode/ad_lavc.c \
|
||||
audio/decode/ad_spdif.c \
|
||||
audio/decode/dec_audio.c \
|
||||
audio/filter/af.c \
|
||||
audio/filter/af_center.c \
|
||||
audio/filter/af_channels.c \
|
||||
audio/filter/af_convert24.c \
|
||||
audio/filter/af_convertsignendian.c \
|
||||
audio/filter/af_delay.c \
|
||||
audio/filter/af_dummy.c \
|
||||
audio/filter/af_equalizer.c \
|
||||
audio/filter/af_export.c \
|
||||
audio/filter/af_extrastereo.c \
|
||||
audio/filter/af_forcespeed.c \
|
||||
audio/filter/af_force.c \
|
||||
audio/filter/af_format.c \
|
||||
audio/filter/af_hrtf.c \
|
||||
audio/filter/af_karaoke.c \
|
||||
@@ -139,29 +166,18 @@ SOURCES = audio/audio.c \
|
||||
audio/filter/af_sub.c \
|
||||
audio/filter/af_surround.c \
|
||||
audio/filter/af_sweep.c \
|
||||
audio/filter/af_tools.c \
|
||||
audio/filter/af_drc.c \
|
||||
audio/filter/af_volume.c \
|
||||
audio/filter/filter.c \
|
||||
audio/filter/tools.c \
|
||||
audio/filter/window.c \
|
||||
audio/out/ao.c \
|
||||
audio/out/ao_null.c \
|
||||
audio/out/ao_pcm.c \
|
||||
audio/out/pull.c \
|
||||
audio/out/push.c \
|
||||
common/av_common.c \
|
||||
common/av_log.c \
|
||||
common/codecs.c \
|
||||
common/common.c \
|
||||
common/msg.c \
|
||||
common/playlist.c \
|
||||
common/tags.c \
|
||||
common/version.c \
|
||||
demux/codec_tags.c \
|
||||
demux/demux.c \
|
||||
demux/demux_edl.c \
|
||||
demux/demux_cue.c \
|
||||
demux/demux_disc.c \
|
||||
demux/demux_lavf.c \
|
||||
demux/demux_mf.c \
|
||||
demux/demux_mkv.c \
|
||||
@@ -170,58 +186,41 @@ SOURCES = audio/audio.c \
|
||||
demux/demux_subreader.c \
|
||||
demux/ebml.c \
|
||||
demux/mf.c \
|
||||
demux/packet.c \
|
||||
input/cmd_list.c \
|
||||
input/cmd_parse.c \
|
||||
input/event.c \
|
||||
input/input.c \
|
||||
input/ipc.c \
|
||||
input/keycodes.c \
|
||||
misc/bstr.c \
|
||||
misc/charset_conv.c \
|
||||
misc/dispatch.c \
|
||||
misc/json.c \
|
||||
misc/rendezvous.c \
|
||||
misc/ring.c \
|
||||
options/m_config.c \
|
||||
options/m_option.c \
|
||||
options/m_property.c \
|
||||
options/options.c \
|
||||
options/parse_commandline.c \
|
||||
options/parse_configfile.c \
|
||||
options/path.c \
|
||||
mpvcore/asxparser.c \
|
||||
mpvcore/av_common.c \
|
||||
mpvcore/av_log.c \
|
||||
mpvcore/av_opts.c \
|
||||
mpvcore/bstr.c \
|
||||
mpvcore/charset_conv.c \
|
||||
mpvcore/codecs.c \
|
||||
mpvcore/command.c \
|
||||
mpvcore/cpudetect.c \
|
||||
mpvcore/m_config.c \
|
||||
mpvcore/m_option.c \
|
||||
mpvcore/m_property.c \
|
||||
mpvcore/mp_common.c \
|
||||
mpvcore/mp_msg.c \
|
||||
mpvcore/mp_ring.c \
|
||||
mpvcore/mplayer.c \
|
||||
mpvcore/options.c \
|
||||
mpvcore/parser-cfg.c \
|
||||
mpvcore/parser-mpcmd.c \
|
||||
mpvcore/path.c \
|
||||
mpvcore/playlist.c \
|
||||
mpvcore/playlist_parser.c \
|
||||
mpvcore/screenshot.c \
|
||||
mpvcore/version.c \
|
||||
mpvcore/input/input.c \
|
||||
mpvcore/timeline/tl_edl.c \
|
||||
mpvcore/timeline/tl_matroska.c \
|
||||
mpvcore/timeline/tl_cue.c \
|
||||
osdep/io.c \
|
||||
osdep/numcores.c \
|
||||
osdep/semaphore_osx.c \
|
||||
osdep/subprocess-posix.c \
|
||||
osdep/terminal-unix.c \
|
||||
osdep/timer.c \
|
||||
osdep/timer-linux.c \
|
||||
osdep/threads.c \
|
||||
player/audio.c \
|
||||
player/client.c \
|
||||
player/configfiles.c \
|
||||
player/command.c \
|
||||
player/discnav.c \
|
||||
player/loadfile.c \
|
||||
player/main.c \
|
||||
player/misc.c \
|
||||
player/osd.c \
|
||||
player/playloop.c \
|
||||
player/screenshot.c \
|
||||
player/scripting.c \
|
||||
player/sub.c \
|
||||
player/video.c \
|
||||
player/timeline/tl_matroska.c \
|
||||
player/timeline/tl_mpv_edl.c \
|
||||
player/timeline/tl_cue.c \
|
||||
stream/cache.c \
|
||||
stream/cache_file.c \
|
||||
stream/cookies.c \
|
||||
stream/rar.c \
|
||||
stream/stream.c \
|
||||
stream/stream_avdevice.c \
|
||||
stream/stream_edl.c \
|
||||
stream/stream_file.c \
|
||||
stream/stream_lavf.c \
|
||||
stream/stream_memory.c \
|
||||
@@ -232,13 +231,15 @@ SOURCES = audio/audio.c \
|
||||
sub/draw_bmp.c \
|
||||
sub/find_subfiles.c \
|
||||
sub/img_convert.c \
|
||||
sub/osd.c \
|
||||
sub/sd_lavc.c \
|
||||
sub/sd_lavc_conv.c \
|
||||
sub/sd_lavf_srt.c \
|
||||
sub/sd_microdvd.c \
|
||||
sub/sd_movtext.c \
|
||||
sub/sd_spu.c \
|
||||
sub/sd_srt.c \
|
||||
sub/spudec.c \
|
||||
sub/sub.c \
|
||||
ta/ta.c \
|
||||
ta/ta_utils.c \
|
||||
ta/ta_talloc.c \
|
||||
@@ -250,13 +251,14 @@ SOURCES = audio/audio.c \
|
||||
video/mp_image_pool.c \
|
||||
video/sws_utils.c \
|
||||
video/decode/dec_video.c \
|
||||
video/decode/vd.c \
|
||||
video/decode/vd_lavc.c \
|
||||
video/filter/vf.c \
|
||||
video/filter/pullup.c \
|
||||
video/filter/vf_buffer.c \
|
||||
video/filter/vf_crop.c \
|
||||
video/filter/vf_delogo.c \
|
||||
video/filter/vf_divtc.c \
|
||||
video/filter/vf_down3dright.c \
|
||||
video/filter/vf_dsize.c \
|
||||
video/filter/vf_eq.c \
|
||||
video/filter/vf_expand.c \
|
||||
@@ -278,6 +280,7 @@ SOURCES = audio/audio.c \
|
||||
video/filter/vf_sub.c \
|
||||
video/filter/vf_swapuv.c \
|
||||
video/filter/vf_unsharp.c \
|
||||
video/filter/vf_vo.c \
|
||||
video/filter/vf_yadif.c \
|
||||
video/out/bitmap_packer.c \
|
||||
video/out/aspect.c \
|
||||
@@ -285,23 +288,31 @@ SOURCES = audio/audio.c \
|
||||
video/out/vo.c \
|
||||
video/out/vo_null.c \
|
||||
video/out/vo_image.c \
|
||||
video/out/win_state.c \
|
||||
osdep/$(GETCH) \
|
||||
osdep/$(TIMER) \
|
||||
$(SOURCES-yes)
|
||||
|
||||
OBJECTS += $(addsuffix .o, $(basename $(SOURCES)))
|
||||
OBJECTS-$(PE_EXECUTABLE) += osdep/mpv-rc.o
|
||||
OBJECTS += $(OBJECTS-yes)
|
||||
|
||||
DEP_FILES = $(patsubst %.S,%.d,$(patsubst %.cpp,%.d,$(patsubst %.c,%.d,$(SOURCES:.m=.d) $(SOURCES:.m=.d))))
|
||||
|
||||
ALL_TARGETS += mpv
|
||||
ALL_TARGETS += mpv$(EXESUF)
|
||||
|
||||
INSTALL_BIN += install-mpv
|
||||
INSTALL_BIN_STRIP += install-mpv-strip
|
||||
INSTALL_MAN =
|
||||
INSTALL_PDF =
|
||||
|
||||
ifeq ($(BUILD_MAN),yes)
|
||||
INSTALL_MAN += install-mpv-man
|
||||
ALL_TARGETS += DOCS/man/mpv.1
|
||||
ALL_TARGETS += DOCS/man/en/mpv.1
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_PDF),yes)
|
||||
INSTALL_PDF += install-mpv-pdf
|
||||
ALL_TARGETS += DOCS/man/en/mpv.pdf
|
||||
endif
|
||||
|
||||
DIRS = . \
|
||||
@@ -309,19 +320,14 @@ DIRS = . \
|
||||
audio/decode \
|
||||
audio/filter \
|
||||
audio/out \
|
||||
common \
|
||||
compat \
|
||||
input \
|
||||
player/timeline \
|
||||
mpvcore \
|
||||
mpvcore/input \
|
||||
mpvcore/timeline \
|
||||
demux \
|
||||
misc \
|
||||
options \
|
||||
osdep \
|
||||
osdep/ar \
|
||||
player \
|
||||
stream \
|
||||
sub \
|
||||
ta \
|
||||
video \
|
||||
video/decode \
|
||||
video/filter \
|
||||
@@ -330,6 +336,7 @@ DIRS = . \
|
||||
|
||||
ADDSUFFIXES = $(foreach suf,$(1),$(addsuffix $(suf),$(2)))
|
||||
ADD_ALL_DIRS = $(call ADDSUFFIXES,$(1),$(DIRS))
|
||||
ADD_ALL_EXESUFS = $(1) $(call ADDSUFFIXES,$(EXESUFS_ALL),$(1))
|
||||
|
||||
###### brief build output #######
|
||||
|
||||
@@ -342,17 +349,36 @@ endif
|
||||
|
||||
all: $(ALL_TARGETS)
|
||||
|
||||
%.tex: %.rst
|
||||
$(RST2LATEX) --config=DOCS/man/docutils.conf $< $@
|
||||
|
||||
%.pdf: %.tex
|
||||
pdflatex -interaction=batchmode -jobname=$(basename $@) $<; pdflatex -interaction=batchmode -jobname=$(basename $@) $<
|
||||
|
||||
%.1: %.rst
|
||||
$(RST2MAN) $< $@
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
mpv: $(OBJECTS) player/main_fn.o
|
||||
%.o: %.cpp
|
||||
$(CC) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.m
|
||||
$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%-rc.o: %.rc
|
||||
$(WINDRES) -I. $< $@
|
||||
|
||||
mpv$(EXESUF): $(OBJECTS)
|
||||
mpv$(EXESUF):
|
||||
$(CC) -o $@ $^ $(EXTRALIBS)
|
||||
|
||||
input/input.c: input/input_conf.h
|
||||
input/input_conf.h: TOOLS/file2string.pl etc/input.conf
|
||||
mpvcore/input/input.c: mpvcore/input/input_conf.h
|
||||
mpvcore/input/input_conf.h: TOOLS/file2string.pl etc/input.conf
|
||||
./$^ >$@
|
||||
|
||||
MKVLIB_DEPS = TOOLS/lib/Parse/Matroska.pm \
|
||||
@@ -381,17 +407,17 @@ sub/osd_libass.c: sub/osd_font.h
|
||||
sub/osd_font.h: TOOLS/file2string.pl sub/osd_font.otf
|
||||
./$^ >$@
|
||||
|
||||
player/lua/%.inc: TOOLS/file2string.pl player/lua/%.lua
|
||||
mpvcore/mp_lua.c: mpvcore/lua/defaults.inc
|
||||
mpvcore/lua/defaults.inc: TOOLS/file2string.pl mpvcore/lua/defaults.lua
|
||||
./$^ >$@
|
||||
|
||||
player/lua.c: player/lua/defaults.inc \
|
||||
player/lua/assdraw.inc \
|
||||
player/lua/osc.inc \
|
||||
player/lua/ytdl_hook.inc \
|
||||
player/lua/options.inc
|
||||
mpvcore/mp_lua.c: mpvcore/lua/assdraw.inc
|
||||
mpvcore/lua/assdraw.inc: TOOLS/file2string.pl mpvcore/lua/assdraw.lua
|
||||
./$^ >$@
|
||||
|
||||
etc/_mpv: TOOLS/zsh.pl ./mpv
|
||||
./$< > $@
|
||||
mpvcore/mp_lua.c: mpvcore/lua/osc.inc
|
||||
mpvcore/lua/osc.inc: TOOLS/file2string.pl mpvcore/lua/osc.lua
|
||||
./$^ >$@
|
||||
|
||||
# ./configure must be rerun if it changed
|
||||
config.mak: configure
|
||||
@@ -405,44 +431,52 @@ version.h .version: version.sh
|
||||
# Force version.sh to run to potentially regenerate version.h
|
||||
-include .version
|
||||
|
||||
%: %.c
|
||||
%$(EXESUF): %.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
|
||||
###### dependency declarations / specific CFLAGS ######
|
||||
|
||||
common/version.c: version.h
|
||||
mpvcore/version.c osdep/mpv-rc.o: version.h
|
||||
|
||||
DOCS/man/mpv.1: DOCS/man/af.rst \
|
||||
DOCS/man/ao.rst \
|
||||
DOCS/man/changes.rst \
|
||||
DOCS/man/encode.rst \
|
||||
DOCS/man/input.rst \
|
||||
DOCS/man/options.rst \
|
||||
DOCS/man/vf.rst \
|
||||
DOCS/man/vo.rst
|
||||
osdep/mpv-rc.o: osdep/mpv.exe.manifest etc/mpv-icon.ico
|
||||
|
||||
DOCS/man/en/mpv.1 DOCS/man/en/mpv.pdf: DOCS/man/en/af.rst \
|
||||
DOCS/man/en/ao.rst \
|
||||
DOCS/man/en/changes.rst \
|
||||
DOCS/man/en/encode.rst \
|
||||
DOCS/man/en/input.rst \
|
||||
DOCS/man/en/options.rst \
|
||||
DOCS/man/en/vf.rst \
|
||||
DOCS/man/en/vo.rst
|
||||
|
||||
###### installation / clean / generic rules #######
|
||||
|
||||
install: $(INSTALL_BIN) install-data $(INSTALL_MAN)
|
||||
install: $(INSTALL_BIN) install-data $(INSTALL_MAN) $(INSTALL_PDF)
|
||||
install-no-man: $(INSTALL_BIN) install-data
|
||||
install-strip: $(INSTALL_BIN_STRIP) install-data $(INSTALL_MAN)
|
||||
install-strip: $(INSTALL_BIN_STRIP) install-data $(INSTALL_MAN) $(INSTALL_PDF)
|
||||
install-strip-no-man: $(INSTALL_BIN_STRIP) install-data
|
||||
|
||||
install-dirs:
|
||||
if test ! -d $(BINDIR) ; then $(INSTALL) -d $(BINDIR) ; fi
|
||||
|
||||
install-%: % install-dirs
|
||||
install-%: %$(EXESUF) install-dirs
|
||||
$(INSTALL) -m 755 $< $(BINDIR)
|
||||
|
||||
install-%-strip: % install-dirs
|
||||
install-%-strip: %$(EXESUF) install-dirs
|
||||
$(INSTALL) -m 755 -s $< $(BINDIR)
|
||||
|
||||
install-mpv-man: install-mpv-man-en
|
||||
|
||||
install-mpv-man-en: DOCS/man/mpv.1
|
||||
install-mpv-man-en: DOCS/man/en/mpv.1
|
||||
if test ! -d $(MANDIR)/man1 ; then $(INSTALL) -d $(MANDIR)/man1 ; fi
|
||||
$(INSTALL) -m 644 DOCS/man/mpv.1 $(MANDIR)/man1/
|
||||
$(INSTALL) -m 644 DOCS/man/en/mpv.1 $(MANDIR)/man1/
|
||||
|
||||
install-mpv-pdf: install-mpv-pdf-en
|
||||
|
||||
install-mpv-pdf-en: DOCS/man/en/mpv.pdf
|
||||
if test ! -d $(DOCDIR) ; then $(INSTALL) -d $(DOCDIR) ; fi
|
||||
$(INSTALL) -m 644 DOCS/man/en/mpv.pdf $(DOCDIR)/
|
||||
|
||||
ICONSIZES = 16x16 32x32 64x64
|
||||
|
||||
@@ -460,44 +494,47 @@ install-mpv-desktop: etc/mpv.desktop
|
||||
$(INSTALL) -d $(prefix)/share/applications
|
||||
$(INSTALL) -m 644 etc/mpv.desktop $(prefix)/share/applications/
|
||||
|
||||
install-mpv-config: etc/encoding-profiles.conf
|
||||
$(INSTALL) -d $(CONFDIR)
|
||||
$(INSTALL) -m 644 etc/encoding-profiles.conf $(CONFDIR)
|
||||
|
||||
install-mpv-zsh: etc/_mpv
|
||||
$(INSTALL) -d $(prefix)/share/zsh/vendor-completions
|
||||
$(INSTALL) -m 644 etc/_mpv $(prefix)/share/zsh/vendor-completions/
|
||||
|
||||
install-data: install-mpv-icons install-mpv-desktop install-mpv-config install-mpv-zsh
|
||||
install-data: install-mpv-icons install-mpv-desktop
|
||||
|
||||
uninstall:
|
||||
$(RM) $(BINDIR)/mpv
|
||||
$(RM) $(MANDIR)/man1/mpv.1 $(MANDIR)/man1/mpv.1
|
||||
$(RM) $(BINDIR)/mpv$(EXESUF)
|
||||
$(RM) $(MANDIR)/man1/mpv.1
|
||||
$(RM) $(MANDIR)/en/man1/mpv.1
|
||||
$(RM) $(DOCDIR)/mpv.pdf
|
||||
$(RM) $(prefix)/share/applications/mpv.desktop
|
||||
$(RM) $(prefix)/share/zsh/vendor-completions/_mpv
|
||||
$(RM) $(foreach size,$(ICONSIZES),$(prefix)/share/icons/hicolor/$(size)/apps/mpv.png)
|
||||
|
||||
clean:
|
||||
-$(RM) $(call ADD_ALL_DIRS,/*.o /*.d /*.a /*.ho /*~)
|
||||
-$(RM) $(call ADD_ALL_DIRS,/*.o /*.a /*.ho /*~)
|
||||
-$(RM) mpv
|
||||
-$(RM) $(call ADD_ALL_EXESUFS,mpv)
|
||||
-$(RM) $(call ADDSUFFIXES,.pdf .tex .log .aux .out .toc,DOCS/man/*/mpv)
|
||||
-$(RM) DOCS/man/*/mpv.1
|
||||
-$(RM) version.h
|
||||
-$(RM) input/input_conf.h
|
||||
-$(RM) mpvcore/input/input_conf.h
|
||||
-$(RM) video/out/vdpau_template.c
|
||||
-$(RM) demux/ebml_types.h demux/ebml_defs.c
|
||||
-$(RM) video/out/gl_video_shaders.h
|
||||
-$(RM) video/out/x11_icon.inc
|
||||
-$(RM) sub/osd_font.h
|
||||
-$(RM) player/lua/defaults.inc
|
||||
-$(RM) player/lua/assdraw.inc
|
||||
-$(RM) player/lua/osc.inc
|
||||
-$(RM) player/lua/ytdl_hook.inc
|
||||
-$(RM) player/lua/options.inc
|
||||
-$(RM) mpvcore/lua/defaults.inc
|
||||
-$(RM) mpvcore/lua/assdraw.inc
|
||||
-$(RM) mpvcore/lua/osc.inc
|
||||
|
||||
distclean: clean
|
||||
-$(RM) config.log old_build/config.h old_build/config.mak Makefile
|
||||
-rmdir old_build/
|
||||
-$(RM) config.log config.mak config.h TAGS tags
|
||||
|
||||
TAGS:
|
||||
$(RM) $@; find . -name '*.[chS]' -o -name '*.asm' | xargs etags -a
|
||||
|
||||
tags:
|
||||
$(RM) $@; find . -name '*.[chS]' -o -name '*.asm' | xargs ctags -a
|
||||
|
||||
osxbundle:
|
||||
@TOOLS/osxbundle.py mpv
|
||||
|
||||
osxbundle-skip-deps:
|
||||
@TOOLS/osxbundle.py --skip-deps mpv
|
||||
|
||||
-include $(DEP_FILES)
|
||||
|
||||
@@ -3,8 +3,6 @@ mpv
|
||||
|
||||
[](https://travis-ci.org/mpv-player/mpv)
|
||||
|
||||
[](https://scan.coverity.com/projects/1232)
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
@@ -18,52 +16,25 @@ Compilation
|
||||
-----------
|
||||
|
||||
Compiling with full features requires development files for several
|
||||
external libraries. Below is a list of some important requirements.
|
||||
|
||||
The mpv build system uses *waf* but we don't store it in your source tree. The
|
||||
script './bootstrap.py' will download the latest version of waf that was tested
|
||||
with the build system.
|
||||
|
||||
For a list of the available build options use `./waf configure --help`. If
|
||||
you think you have support for some feature installed but configure fails to
|
||||
detect it, the file `build/config.log` may contain information about the
|
||||
reasons for the failure.
|
||||
|
||||
NOTE: To avoid cluttering the output with unreadable spam, `--help` only shows
|
||||
one of the two switches for each option. If the option is autodetected by
|
||||
default, the `--disable-***` switch is printed; if the option is disabled by
|
||||
default, the `--enable-***` switch is printed. Either way, you can use
|
||||
`--enable-***` or `--disable-**` regardless of what is printed by `--help`.
|
||||
|
||||
To build the software you can use `./waf build`: the result of the compilation
|
||||
will be located in `build/mpv`. You can use `./waf install` to install mpv
|
||||
to the *prefix* after it is compiled.
|
||||
|
||||
NOTE: Using the old build system (with `./old-configure`) should still work,
|
||||
but will be removed in a future version of mpv.
|
||||
external libraries. Below is a list of some important requirements. For
|
||||
more information see the output of `./configure --help` for a list of options,
|
||||
or look at the list of enabled and disabled features printed after running
|
||||
`./configure`. If you think you have support for some feature installed
|
||||
but configure fails to detect it, the file `config.log` may contain
|
||||
information about the reasons for the failure.
|
||||
|
||||
Essential dependencies (incomplete list):
|
||||
|
||||
- gcc or clang
|
||||
- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
|
||||
- Audio output development headers (libasound, pulseaudio)
|
||||
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavresample)
|
||||
At least FFmpeg 2.1.4 or Libav 10 is required.
|
||||
- libjpeg (for screenshots)
|
||||
- Lua (strictly speaking optional, but required for the OSC pseudo-GUI)
|
||||
|
||||
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)
|
||||
- libass
|
||||
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libpostproc)
|
||||
- libjpeg
|
||||
- libquvi if you want to play Youtube videos directly
|
||||
- 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
|
||||
@@ -76,32 +47,21 @@ If you are running Mac OSX and using homebrew we provide [homebrew-mpv][homebrew
|
||||
to date formula that compiles mpv with sensible dependencies and defaults for
|
||||
OSX.
|
||||
|
||||
FFmpeg vs. Libav
|
||||
----------------
|
||||
### configure `--enable-*` parameters
|
||||
|
||||
Generally, mpv should work with the latest release as well as the git version
|
||||
of both FFmpeg and Libav. But FFmpeg is preferred, and some mpv features work
|
||||
with FFmpeg only. See the [wiki article][ffmpeg_vs_libav] about the issue.
|
||||
The `--enable-*` parameters unconditionally force options on, completely
|
||||
skipping autodetection. This behavior is unlike what you may be used to from
|
||||
autoconf-based configure scripts that can decide to override you. This greater
|
||||
level of control comes at a price. You may have to provide the correct compiler
|
||||
and linker flags yourself.
|
||||
|
||||
If you used one of these options and experience a compilation or
|
||||
linking failure, make sure you have passed the necessary compiler/linker flags
|
||||
to configure.
|
||||
|
||||
Release cycle
|
||||
-------------
|
||||
|
||||
Every few months, a new release is cut off of the master branch and is assigned
|
||||
a 0.X.0 version number.
|
||||
|
||||
As part of the maintenance process, minor releases are made, which are assigned
|
||||
0.X.Y version numbers. Minor releases contain bug fixes only. They never merge
|
||||
the master branch, and no features are added to it. Only the latest release is
|
||||
maintained.
|
||||
|
||||
The goal of releases is to provide stability and an unchanged base for the sake
|
||||
of Linux distributions. If you want the newest features, just use the master
|
||||
branch, which is stable most of the time, except sometimes, when it's not.
|
||||
|
||||
Releases other than the latest release are unsupported and unmaintained.
|
||||
|
||||
See the [release policy document][release-policy] for more information.
|
||||
mpv's configure script is greedy and automatically enables features as a result
|
||||
of autodetection. The cases where you may want to use `--enable-*` are very
|
||||
limited.
|
||||
|
||||
Bug reports
|
||||
-----------
|
||||
@@ -116,26 +76,24 @@ For small changes you can just send us pull requests through GitHub. For bigger
|
||||
changes come and talk to us on IRC before you start working on them. It will
|
||||
make code review easier for both parties later on.
|
||||
|
||||
Contact
|
||||
-------
|
||||
Contacts
|
||||
--------
|
||||
|
||||
Most activity happens on the IRC channel and the github issue tracker. The
|
||||
mailing lists are mostly unused.
|
||||
These forms of contact are meant to ask questions about mpv usage, give
|
||||
feedback on mpv and discuss it's development.
|
||||
|
||||
- **Github issue tracker**: [issue tracker][issue-tracker] (report bugs here)
|
||||
- **User IRC Channel**: `#mpv-player` on `irc.freenode.net`
|
||||
- **Developer IRC Channel**: `#mpv-player-dev` on `irc.freenode.net`
|
||||
If possible, please avoid posting bugs here and use the [issue tracker][issue-tracker]
|
||||
instead.
|
||||
|
||||
- **Users IRC Channel**: `#mpv-player` on `irc.freenode.net`
|
||||
- **Users Mailing List**: `mpv-users@googlegroups.com` ([Archive / Subscribe][mpv-users]).
|
||||
- **Devel Mailing List**: `mpv-devel@googlegroups.com` ([Archive / Subscribe][mpv-devel])
|
||||
|
||||
To contact the `mpv` team in private write to `mpv-team@googlegroups.com`. Use
|
||||
only if discretion is required.
|
||||
To contact the `mpv` team in private write to `mpv-team@googlegroups.com`.
|
||||
|
||||
[changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/man/changes.rst
|
||||
[changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/man/en/changes.rst
|
||||
[mpv-build]: https://github.com/mpv-player/mpv-build
|
||||
[homebrew-mpv]: https://github.com/mpv-player/homebrew-mpv
|
||||
[issue-tracker]: https://github.com/mpv-player/mpv/issues
|
||||
[mpv-users]: https://groups.google.com/forum/?hl=en#!forum/mpv-users
|
||||
[mpv-devel]: https://groups.google.com/forum/?hl=en#!forum/mpv-devel
|
||||
[ffmpeg_vs_libav]: https://github.com/mpv-player/mpv/wiki/FFmpeg-versus-Libav
|
||||
[release-policy]: https://github.com/mpv-player/mpv/blob/master/DOCS/release-policy.md
|
||||
-135
@@ -1,135 +0,0 @@
|
||||
Release 0.7.2
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Give precedence to the DVD menu navigation keyboard bindings so that user
|
||||
defined LEFT/RIGHT/... bindings don't break DVD menu navigation.
|
||||
- Try to fallback to the "default" device if the selected device is busy in the
|
||||
alsa AO.
|
||||
- Don't create Dock icon for audio only files on OS X.
|
||||
- Save screenshots to desktop when using the app bundle on OS X.
|
||||
- Restore ab-loop settings with playback resume.
|
||||
- Bump required youtube-dl version to 2014.11.26 and enable the ytdl_hook Lua
|
||||
script by default (now playing videos from YouTube and the like will work
|
||||
out of the box without any configuration change needed).
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Don't signal an error if --stream-dump is used.
|
||||
- Fix removing key bindings from Lua scripts.
|
||||
- Reject channel descriptions with too many channels in the coreaudio AO.
|
||||
- Don't async redraw when waiting for VO redraw on OS X (this fixes the very
|
||||
annoying glitch where the black bars disappear for a single frame when going
|
||||
fullscreen).
|
||||
- Fix mono playback with the also AO.
|
||||
- Don't crash if framebuffers are not available in the opengl VO.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Try to handle multi-arc videos in the ytdl_hook Lua script.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 47ec404..c7d6b21`` in
|
||||
the git repository.
|
||||
|
||||
Release 0.7.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Don't show the volume neutral marker on the OSD if softvol is disabled.
|
||||
- Don't select a subtitle track when executing the sub_add input command in
|
||||
"auto" mode.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fix busy loop when seeking while paused (this fixes a problem with pulseaudio
|
||||
that caused mpv and the pulseaudio daemon to use 100% CPU).
|
||||
- Fix Lua function utils.subprocess() in Windows versions older than Vista.
|
||||
- Avoid creating a window bigger than the screen on Windows.
|
||||
- Don't ignore the last line in m3u playlists.
|
||||
- Don't crash if a codec could not be opened.
|
||||
- Dynamically allocate audio channel map entries (this should fix a crash in
|
||||
the alsa and coreaudio AOs with audio devices that support more than 20
|
||||
channel maps).
|
||||
- Ignore the "srgb" option in the opengl VO if hardware decoding is enabled.
|
||||
- Linearize non-RGB sRGB files correctly (eg. JPEG).
|
||||
- Fix opening reference URLs (.file/id=) on OS X.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 8d8b36d..6583ad6`` in
|
||||
the git repository.
|
||||
|
||||
Release 0.7.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Buffer partial log messages in the client API (the client API will now only
|
||||
pass full log messages to clients).
|
||||
- Remove ncurses/terminfo/termcap support (it was disabled by default and
|
||||
replaced by new code since v0.6.0).
|
||||
- Enable cdda:// support by default again (it was disabled since v0.6.0).
|
||||
- Cascade-load input.conf (if there are several input.confs in the set of valid
|
||||
config paths, load them all).
|
||||
- Draw the OSD twice in 3D mode (this fixes subtitles display in 3D mode).
|
||||
- Make wasapi the default AO on Windows again since many of its problems have
|
||||
been solved.
|
||||
- Use "site-functions" subdir to install the zsh completion script instead of
|
||||
the Debian-specific "vendor-completions" (also provide the --zshdir waf
|
||||
configure option for changing this value).
|
||||
- Improve synchronization between the Cocoa GUI and the player (this fixes some
|
||||
long standing deadlock issues on Mac OS X).
|
||||
- Remove --fs-missioncontrol option (only relevant to Mac OS X).
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Enable pitch correction by default when playing at higher speeds (this can be
|
||||
controlled with the --audio-pitch-correction option).
|
||||
- Open stream and demuxer asynchronously (this should avoid having the player
|
||||
get blocked on network streams).
|
||||
- Add cache-buffering-state property for querying the cache fill status until
|
||||
the player unpauses.
|
||||
- Add support for listing and selecting the audio device (note that it is not
|
||||
implemented for all AOs, see the --audio-device option for more information).
|
||||
- Add support for a JSON-based IPC mechanism (note that this is not currently
|
||||
supported on Windows, see the JSON IPC section in the manpage for more
|
||||
information).
|
||||
- Add Lua utility function for starting processes (see utils.subprocess() in the
|
||||
manpage).
|
||||
- Add Lua utility function for parsing JSON (see utils.parse_json() in the
|
||||
manpage).
|
||||
- Add field-dominance property (see --field-dominance option).
|
||||
- Add video-rotate property (see --video-rotate option).
|
||||
- Add playback-abort property for querying whether playback is stopped or is to
|
||||
be stopped.
|
||||
- Add cursor-autohide property (see --cursor-autohide option).
|
||||
- Add vo-configured property for querying whether a window is created.
|
||||
- Add support for dxva2 hardware acceleration on Windows.
|
||||
- Drop libquvi support (this has been replaced by a built-in Lua script that
|
||||
invokes the youtube-dl tool, which needs to be installed, see the --ytdl
|
||||
option).
|
||||
- Add support for loading chapters from an external file (see the
|
||||
--chapters-file option).
|
||||
- Add window-minimized property for querying whether the window is minimized
|
||||
(works for X11 only).
|
||||
- Make it possible to configure the OSC seekbar style (see the "seekbarstyle"
|
||||
OSC option).
|
||||
- Add support for libmpv on Mac OS X (it used to be broken, now it's fully
|
||||
functional and also provides support for embedding the mpv window inside a
|
||||
Cocoa/Qt application).
|
||||
- Try to use the audio channel map reported by ALSA in the alsa AO.
|
||||
- Add option to disable text OSD rendering completely (useful for working around
|
||||
certain fontconfig issues, see the --use-text-osd option).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 7759c18..9479daa`` in
|
||||
the git repository.
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from functools import partial
|
||||
|
||||
sys_re = re.compile("^/System")
|
||||
usr_re = re.compile("^/usr/lib/")
|
||||
exe_re = re.compile("@executable_path")
|
||||
|
||||
def is_user_lib(objfile, libname):
|
||||
return not sys_re.match(libname) and \
|
||||
not usr_re.match(libname) and \
|
||||
not exe_re.match(libname) and \
|
||||
not "libobjc." in libname and \
|
||||
not "libSystem." in libname and \
|
||||
not "libc." in libname and \
|
||||
not "libgcc." in libname and \
|
||||
not os.path.basename(objfile) in libname
|
||||
|
||||
def otool(objfile):
|
||||
command = "otool -L %s | grep -e '\t' | awk '{ print $1 }'" % objfile
|
||||
output = subprocess.check_output(command, shell = True)
|
||||
return filter(partial(is_user_lib, objfile), output.split())
|
||||
|
||||
def install_name_tool_change(old, new, objfile):
|
||||
subprocess.call(["install_name_tool", "-change", old, new, objfile])
|
||||
|
||||
def install_name_tool_id(name, objfile):
|
||||
subprocess.call(["install_name_tool", "-id", name, objfile])
|
||||
|
||||
def libraries(objfile, result = dict()):
|
||||
libs_list = otool(objfile)
|
||||
result[objfile] = set(libs_list)
|
||||
|
||||
for lib in libs_list:
|
||||
if lib not in result:
|
||||
libraries(lib, result)
|
||||
|
||||
return result
|
||||
|
||||
def leafs(libs_dict, processed = []):
|
||||
result = []
|
||||
processed = set(processed)
|
||||
|
||||
for objfile, libs in libs_dict.items():
|
||||
if libs <= processed:
|
||||
result.append(objfile)
|
||||
|
||||
return result
|
||||
|
||||
def lib_path(binary):
|
||||
return os.path.join(os.path.dirname(binary), 'lib')
|
||||
|
||||
def lib_name(lib):
|
||||
return os.path.join("@executable_path", "lib", os.path.basename(lib))
|
||||
|
||||
def process_libraries(libs_dict, binary, processed = []):
|
||||
ls = leafs(libs_dict, processed)
|
||||
diff = set(ls) - set(processed)
|
||||
if diff == set([binary]):
|
||||
return
|
||||
|
||||
for src in diff:
|
||||
name = lib_name(src)
|
||||
dst = os.path.join(lib_path(binary), os.path.basename(src))
|
||||
|
||||
shutil.copy(src, dst)
|
||||
os.chmod(dst, 0o755)
|
||||
install_name_tool_id(name, dst)
|
||||
|
||||
if src in libs_dict[binary]:
|
||||
install_name_tool_change(src, name, binary)
|
||||
|
||||
for p in processed:
|
||||
if p in libs_dict[src]:
|
||||
install_name_tool_change(p, lib_name(p), dst)
|
||||
|
||||
process_libraries(libs_dict, binary, ls)
|
||||
|
||||
def main():
|
||||
binary = os.path.abspath(sys.argv[1])
|
||||
if not os.path.exists(lib_path(binary)):
|
||||
os.makedirs(lib_path(binary))
|
||||
libs = libraries(binary)
|
||||
print(libs)
|
||||
process_libraries(libs, binary)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
conv() {
|
||||
echo
|
||||
identify -format "icon: %w %h" "$1"
|
||||
convert "$1" -depth 8 rgba:-
|
||||
identify -format "icon: %w %h" $1
|
||||
convert $1 -depth 8 rgba:-
|
||||
}
|
||||
|
||||
(echo "# File generated by gen-x11-icon.sh" ;
|
||||
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
: "${MPV:=mpv}"
|
||||
: "${ILDETECT_MPV:=$MPV}"
|
||||
: "${ILDETECT_MPVFLAGS:=--start=35% --length=35}"
|
||||
: "${ILDETECT_DRY_RUN:=}"
|
||||
: "${ILDETECT_QUIET:=}"
|
||||
: "${ILDETECT_RUN_INTERLACED_ONLY:=}"
|
||||
: "${ILDETECT_FORCE_RUN:=}"
|
||||
|
||||
# This script uses ffmpeg's "idet" filter for interlace detection. In the
|
||||
# long run this should replace ildetect.sh+ildetect.so.
|
||||
|
||||
# exit status:
|
||||
# 0 progressive
|
||||
# 1 telecine
|
||||
# 2 interlaced
|
||||
# 8 unknown
|
||||
# 16 detect fail
|
||||
# 17+ mpv's status | 16
|
||||
|
||||
testfun()
|
||||
{
|
||||
$ILDETECT_MPV "$@" \
|
||||
--vf-add=lavfi="[idet]" --msg-level ffmpeg=v \
|
||||
--o= --vo=null --no-audio --untimed \
|
||||
$ILDETECT_MPVFLAGS \
|
||||
| { if [ -n "$ILDETECT_QUIET" ]; then cat; else tee /dev/stderr; fi } \
|
||||
| grep "Parsed_idet_0: Multi frame detection: "
|
||||
}
|
||||
|
||||
judge()
|
||||
{
|
||||
tff=0
|
||||
bff=0
|
||||
progressive=0
|
||||
undetermined=0
|
||||
while read -r _ _ _ _ _ _ tff1 _ bff1 _ progressive1 _ undetermined1 _; do
|
||||
case "$tff1$bff1$progressive1$undetermined1" in
|
||||
*[!0-9]*)
|
||||
printf >&2 'ERROR: Unrecognized idet output: %s\n' "$out"
|
||||
exit 16
|
||||
;;
|
||||
esac
|
||||
tff=$((tff + tff1))
|
||||
bff=$((bff + bff1))
|
||||
progressive=$((progressive + progressive1))
|
||||
undetermined=$((undetermined + undetermined1))
|
||||
done <<EOF
|
||||
$(testfun "$@")
|
||||
EOF
|
||||
|
||||
interlaced=$((bff + tff))
|
||||
determined=$((interlaced + progressive))
|
||||
|
||||
if [ "$undetermined" -gt "$determined" ] || [ "$determined" -lt 250 ]; then
|
||||
echo >&2 "ERROR: Less than 50% or 250 frames are determined."
|
||||
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
|
||||
echo >&2 "Assuming interlacing."
|
||||
if [ "$tff" -gt $((bff * 10)) ]; then
|
||||
verdict="interlaced-tff"
|
||||
elif [ "$bff" -gt $((tff * 10)) ]; then
|
||||
verdict="interlaced-bff"
|
||||
else
|
||||
verdict="interlaced"
|
||||
fi
|
||||
elif [ $((interlaced * 20)) -gt "$progressive" ]; then
|
||||
# At least 5% of the frames are interlaced!
|
||||
if [ "$tff" -gt $((bff * 10)) ]; then
|
||||
verdict="interlaced-tff"
|
||||
elif [ "$bff" -gt $((tff * 10)) ]; then
|
||||
verdict="interlaced-bff"
|
||||
else
|
||||
echo >&2 "ERROR: Content is interlaced, but can't determine field order."
|
||||
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
|
||||
echo >&2 "Assuming interlacing with default field order."
|
||||
verdict="interlaced"
|
||||
fi
|
||||
else
|
||||
# Likely progressive
|
||||
verdict="progressive"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$verdict"
|
||||
}
|
||||
|
||||
judge "$@" --vf-clr
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
[ -n "$ILDETECT_RUN_INTERLACED_ONLY" ] || \
|
||||
$ILDETECT_MPV "$@"
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 0
|
||||
;;
|
||||
interlaced-tff)
|
||||
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=top
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=top
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=top
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
interlaced-bff)
|
||||
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=bottom
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=bottom
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=bottom
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
interlaced)
|
||||
judge "$@" --vf-clr --vf-pre=pullup
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo >&2 "ERROR: Internal error."
|
||||
exit 16
|
||||
;;
|
||||
esac
|
||||
@@ -232,7 +232,6 @@ sub define_matroska {
|
||||
elem('Block', 'a1', 'binary'),
|
||||
elem('BlockDuration', '9b', 'uint'),
|
||||
elem('ReferenceBlock*', 'fb', 'sint'),
|
||||
elem('DiscardPadding', '75A2', 'sint'),
|
||||
}),
|
||||
elem('SimpleBlock*', 'a3', 'binary'),
|
||||
}),
|
||||
@@ -257,8 +256,6 @@ sub define_matroska {
|
||||
elem('CodecPrivate', '63a2', 'binary'),
|
||||
elem('CodecName', '258688', 'str'),
|
||||
elem('CodecDecodeAll', 'aa', 'uint'),
|
||||
elem('CodecDelay', '56AA', 'uint'),
|
||||
elem('SeekPreRoll', '56BB', 'uint'),
|
||||
elem('Video', 'e0', {
|
||||
elem('FlagInterlaced', '9a', 'uint'),
|
||||
elem('PixelWidth', 'b0', 'uint'),
|
||||
@@ -268,7 +265,6 @@ sub define_matroska {
|
||||
elem('DisplayUnit', '54b2', 'uint'),
|
||||
elem('FrameRate', '2383e3', 'float'),
|
||||
elem('ColourSpace', '2eb524', 'binary'),
|
||||
elem('StereoMode', '53b8', 'uint'),
|
||||
}),
|
||||
elem('Audio', 'e1', {
|
||||
elem('SamplingFrequency', 'b5', 'float'),
|
||||
@@ -296,8 +292,6 @@ sub define_matroska {
|
||||
elem('CueTrackPositions*', 'b7', {
|
||||
elem('CueTrack', 'f7', 'uint'),
|
||||
elem('CueClusterPosition', 'f1', 'uint'),
|
||||
elem('CueRelativePosition','f0', 'uint'),
|
||||
elem('CueDuration', 'b2', 'uint'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -132,8 +132,7 @@ sub _bin2int($) {
|
||||
# creates a floating-point number with the given mantissa and exponent
|
||||
sub _ldexp {
|
||||
my ($mantissa, $exponent) = @_;
|
||||
my $r = new Math::BigRat($mantissa);
|
||||
return $r * Math::BigRat->new(2)**$exponent;
|
||||
return $mantissa * Math::BigRat->new(2)**$exponent;
|
||||
}
|
||||
|
||||
# NOTE: the read_* functions are hard to read because they're ports
|
||||
@@ -253,27 +252,25 @@ bytes from the internal filehandle.
|
||||
Only lengths C<4> and C<8> are supported (C C<float> and C<double>).
|
||||
|
||||
=cut
|
||||
{
|
||||
my $b1 = new Math::BigInt 1;
|
||||
sub read_float {
|
||||
my ($self, $length) = @_;
|
||||
my $i = $self->read_uint($length);
|
||||
my $f;
|
||||
|
||||
sub read_float {
|
||||
my ($self, $length) = @_;
|
||||
my $i = new Math::BigInt $self->read_uint($length)->bstr;
|
||||
my $f;
|
||||
use bigrat try => BIGINT_TRY;
|
||||
|
||||
# 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;
|
||||
# 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";
|
||||
}
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
=method read_ebml_id($length)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
mpv lua scripts
|
||||
===============
|
||||
|
||||
The lua scripts in this folder can be loaded on a one-time basis by
|
||||
adding the option
|
||||
|
||||
--lua=/path/to/script.lua
|
||||
|
||||
to mpv's command line.
|
||||
|
||||
Where appropriate, they may also be placed in ~/.config/mpv/lua from where
|
||||
they will be automatically loaded when mpv starts.
|
||||
@@ -1,113 +0,0 @@
|
||||
-- This script uses the lavfi cropdetect filter to automatically
|
||||
-- insert a crop filter with appropriate parameters for the currently
|
||||
-- playing video.
|
||||
--
|
||||
-- It registers the key-binding "C" (shift+c), which when pressed,
|
||||
-- inserts the filter vf=lavfi=cropdetect. After 1 second, it then
|
||||
-- inserts the filter vf=crop=w:h:x:y, where w,h,x,y are determined
|
||||
-- from the vf-metadata gathered by cropdetect. The cropdetect filter
|
||||
-- is removed immediately after the crop filter is inserted as it is
|
||||
-- no longer needed.
|
||||
--
|
||||
-- If the "C" key is pressed again, the crop filter is removed
|
||||
-- restoring playback to its original state.
|
||||
--
|
||||
-- Since the crop parameters are determined from the 1 second of video
|
||||
-- between inserting the cropdetect and crop filters, the "C" key
|
||||
-- should be pressed at a position in the video where the crop region
|
||||
-- is unambiguous (i.e., not a black frame, black background title
|
||||
-- card, or dark scene).
|
||||
--
|
||||
-- The default delay between insertion of the cropdetect and
|
||||
-- crop filters may be overridden by adding
|
||||
--
|
||||
-- --lua-opts=autocrop.detect_seconds=<number of seconds>
|
||||
--
|
||||
-- to mpv's arguments. This may be desirable to allow cropdetect more
|
||||
-- time to collect data.
|
||||
require "mp.msg"
|
||||
|
||||
script_name = mp.get_script_name()
|
||||
cropdetect_label = string.format("%s-cropdetect", script_name)
|
||||
crop_label = string.format("%s-crop", script_name)
|
||||
|
||||
-- number of seconds to gather cropdetect data
|
||||
detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name)))
|
||||
if not detect_seconds then
|
||||
detect_seconds = 1
|
||||
end
|
||||
|
||||
function del_filter_if_present(label)
|
||||
-- necessary because mp.command('vf del @label:filter') raises an
|
||||
-- error if the filter doesn't exist
|
||||
local vfs = mp.get_property_native("vf")
|
||||
for i,vf in pairs(vfs) do
|
||||
if vf["label"] == label then
|
||||
table.remove(vfs, i)
|
||||
mp.set_property_native("vf", vfs)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function autocrop_start()
|
||||
-- exit if cropdetection is already in progress
|
||||
if timer then
|
||||
mp.msg.warn("already cropdetecting!")
|
||||
return
|
||||
end
|
||||
|
||||
-- if there's a crop filter, remove it and exit
|
||||
if del_filter_if_present(crop_label) then
|
||||
return
|
||||
end
|
||||
|
||||
-- insert the cropdetect filter
|
||||
ret=mp.command(
|
||||
string.format(
|
||||
'vf add @%s:lavfi=graph="cropdetect=limit=24:round=2:reset=0"',
|
||||
cropdetect_label
|
||||
)
|
||||
)
|
||||
-- wait to gather data
|
||||
timer=mp.add_timeout(detect_seconds, do_crop)
|
||||
end
|
||||
|
||||
function do_crop()
|
||||
-- get the metadata
|
||||
local cropdetect_metadata = mp.get_property_native(
|
||||
string.format("vf-metadata/%s", cropdetect_label)
|
||||
)
|
||||
-- use it to crop if its valid
|
||||
if cropdetect_metadata then
|
||||
if cropdetect_metadata["lavfi.cropdetect.w"]
|
||||
and cropdetect_metadata["lavfi.cropdetect.h"]
|
||||
and cropdetect_metadata["lavfi.cropdetect.x"]
|
||||
and cropdetect_metadata["lavfi.cropdetect.y"]
|
||||
then
|
||||
mp.command(string.format("vf add @%s:crop=%s:%s:%s:%s",
|
||||
crop_label,
|
||||
cropdetect_metadata["lavfi.cropdetect.w"],
|
||||
cropdetect_metadata["lavfi.cropdetect.h"],
|
||||
cropdetect_metadata["lavfi.cropdetect.x"],
|
||||
cropdetect_metadata["lavfi.cropdetect.y"]))
|
||||
else
|
||||
mp.msg.error(
|
||||
"Got empty crop data. You might need to increase detect_seconds."
|
||||
)
|
||||
end
|
||||
else
|
||||
mp.msg.error(
|
||||
"No crop data. Was the cropdetect filter successfully inserted?"
|
||||
)
|
||||
mp.msg.error(
|
||||
"Does your version of ffmpeg/libav support AVFrame metadata?"
|
||||
)
|
||||
end
|
||||
-- remove the cropdetect filter
|
||||
del_filter_if_present(cropdetect_label)
|
||||
timer=nil
|
||||
end
|
||||
|
||||
mp.add_key_binding("C", "auto_crop", autocrop_start)
|
||||
@@ -1,164 +0,0 @@
|
||||
-- This script uses the lavfi idet filter to automatically insert the
|
||||
-- appropriate deinterlacing filter based on a short section of the
|
||||
-- currently playing video.
|
||||
--
|
||||
-- It registers the key-binding ctrl+d, which when pressed, inserts
|
||||
-- the filter vf=lavfi=idet. After 4 second, it examines the results
|
||||
-- to determine whether the content progressive or interlaced and the
|
||||
-- interlacing field dominance. It immediately sets the latter within
|
||||
-- mpv.
|
||||
--
|
||||
-- If the content is judged not to be progressive, it may be either
|
||||
-- interlaced or telecined. To determine which one, the video is
|
||||
-- seeked back to the detection starting point with the "pullup"
|
||||
-- filter inserted. After another four seconds, if idet thinks that
|
||||
-- the pulled up viedo is "progressive", then pullup is the correct
|
||||
-- filter and this script leaves it in places while removing the idet
|
||||
-- filter. Otherwise, the content plain interlaced and mpv's deinterlace
|
||||
-- property is set. This will usually insert the yadif deinterlacing filter.
|
||||
--
|
||||
-- The default detection time may be overridden by adding
|
||||
--
|
||||
-- --lua-opts=autodeint.detect_seconds=<number of seconds>
|
||||
--
|
||||
-- to mpv's arguments. This may be desirable to allow idet more
|
||||
-- time to collect data.
|
||||
--
|
||||
-- To see counts of the various types of frames for each detection phase,
|
||||
-- the verbosity can be increased with
|
||||
--
|
||||
-- --msg-level autodeint=v
|
||||
--
|
||||
-- This script requires a recent version of ffmpeg for which the idet
|
||||
-- filter adds the required metadata.
|
||||
|
||||
require "mp.msg"
|
||||
|
||||
script_name = mp.get_script_name()
|
||||
detect_label = string.format("%s-detect", script_name)
|
||||
pullup_label = string.format("%s", script_name)
|
||||
|
||||
-- number of seconds to gather cropdetect data
|
||||
detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name)))
|
||||
if not detect_seconds then
|
||||
detect_seconds = 4
|
||||
end
|
||||
|
||||
function del_filter_if_present(label)
|
||||
-- necessary because mp.command('vf del @label:filter') raises an
|
||||
-- error if the filter doesn't exist
|
||||
local vfs = mp.get_property_native("vf")
|
||||
|
||||
for i,vf in pairs(vfs) do
|
||||
if vf["label"] == label then
|
||||
table.remove(vfs, i)
|
||||
mp.set_property_native("vf", vfs)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function start_detect()
|
||||
-- exit if detection is already in progress
|
||||
if timer then
|
||||
mp.msg.warn("already detecting!")
|
||||
return
|
||||
end
|
||||
|
||||
mp.set_property("deinterlace","no")
|
||||
del_filter_if_present(pullup_label)
|
||||
|
||||
percent_pos = mp.get_property("percent-pos")
|
||||
|
||||
-- insert the detection filter
|
||||
local cmd = string.format('vf add @%s:lavfi=graph="idet"', detect_label)
|
||||
if not mp.command(cmd) then
|
||||
mp.msg.error("failed to insert detection filter")
|
||||
return
|
||||
end
|
||||
|
||||
-- wait to gather data
|
||||
timer = mp.add_timeout(detect_seconds, judge_field_dominance)
|
||||
end
|
||||
|
||||
function stop_detect()
|
||||
del_filter_if_present(detect_label)
|
||||
timer = nil
|
||||
mp.set_property("playback-pos", percent_pos)
|
||||
end
|
||||
|
||||
progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3, 4
|
||||
|
||||
function judge()
|
||||
-- get the metadata
|
||||
local result = mp.get_property_native(string.format("vf-metadata/%s", detect_label))
|
||||
num_tff = tonumber(result["lavfi.idet.multiple.tff"])
|
||||
num_bff = tonumber(result["lavfi.idet.multiple.bff"])
|
||||
num_progressive = tonumber(result["lavfi.idet.multiple.progressive"])
|
||||
num_undetermined = tonumber(result["lavfi.idet.multiple.undetermined"])
|
||||
num_interlaced = num_tff + num_bff
|
||||
num_determined = num_interlaced + num_progressive
|
||||
|
||||
mp.msg.verbose("progressive = "..num_progressive)
|
||||
mp.msg.verbose("interlaced-tff = "..num_tff)
|
||||
mp.msg.verbose("interlaced-bff = "..num_bff)
|
||||
mp.msg.verbose("undetermined = "..num_undetermined)
|
||||
|
||||
if num_determined < num_undetermined then
|
||||
mp.msg.warn("majority undetermined frames")
|
||||
end
|
||||
if num_progressive > 20*num_interlaced then
|
||||
return progressive
|
||||
elseif num_tff > 10*num_bff then
|
||||
return interlaced_tff
|
||||
elseif num_bff > 10*num_tff then
|
||||
return interlaced_bff
|
||||
else
|
||||
return interlaced
|
||||
end
|
||||
end
|
||||
|
||||
function judge_field_dominance()
|
||||
verdict = judge()
|
||||
if verdict == progressive then
|
||||
stop_detect()
|
||||
mp.msg.info("progressive: doing nothing")
|
||||
return
|
||||
elseif verdict == interlaced_tff then
|
||||
mp.set_property("field-dominance", "top")
|
||||
elseif verdict == interlaced_bff then
|
||||
mp.set_property("field-dominance", "bottom")
|
||||
elseif verdict == interlaced then
|
||||
mp.set_property("field-dominance", "auto")
|
||||
end
|
||||
|
||||
local cmd = string.format("vf pre @%s:pullup", pullup_label)
|
||||
if not mp.command(cmd) then
|
||||
mp.msg.error("failed to insert pullup filter")
|
||||
stop_detect()
|
||||
return
|
||||
end
|
||||
|
||||
-- redo the detection with the pullup filter to see if it's telecined
|
||||
mp.set_property("percent-pos", percent_pos)
|
||||
timer = mp.add_timeout(detect_seconds, judge_ivtc)
|
||||
end
|
||||
|
||||
function judge_ivtc()
|
||||
verdict = judge()
|
||||
|
||||
if verdict == progressive then
|
||||
mp.msg.info(string.format("telecinied with %s field dominance: using pullup", mp.get_property("field-dominance")))
|
||||
stop_detect()
|
||||
else
|
||||
mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", mp.get_property("field-dominance")))
|
||||
del_filter_if_present(pullup_label)
|
||||
mp.set_property("deinterlace","yes")
|
||||
stop_detect()
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
mp.add_key_binding("ctrl+d", script_name, start_detect)
|
||||
@@ -1,75 +0,0 @@
|
||||
-- This script automatically loads playlist entries before and after the
|
||||
-- the currently played file. It does so by scanning the directory a file is
|
||||
-- located in when starting playback. It sorts the directory entries
|
||||
-- alphabetically, and adds entries before and after the current file to
|
||||
-- the internal playlist. (It stops if the it would add an already existing
|
||||
-- playlist entry at the same position - this makes it "stable".)
|
||||
-- Add at most 5 * 2 files when starting a file (before + after).
|
||||
MAXENTRIES = 5
|
||||
|
||||
mputils = require 'mp.utils'
|
||||
|
||||
function add_files_at(index, files)
|
||||
index = index - 1
|
||||
local oldcount = mp.get_property_number("playlist-count", 1)
|
||||
for i = 1, #files do
|
||||
mp.commandv("loadfile", files[i], "append")
|
||||
mp.commandv("playlist_move", oldcount + i - 1, index + i - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function find_and_add_entries()
|
||||
local path = mp.get_property("path", "")
|
||||
local dir, filename = mputils.split_path(path)
|
||||
if #dir == 0 then
|
||||
return
|
||||
end
|
||||
local files = mputils.readdir(dir, "files")
|
||||
table.sort(files)
|
||||
if dir == "." then
|
||||
dir = ""
|
||||
end
|
||||
local pl = mp.get_property_native("playlist", {})
|
||||
local pl_current = mp.get_property_number("playlist-pos", 0) + 1
|
||||
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
|
||||
local current
|
||||
for i = 1, #files do
|
||||
if files[i] == filename then
|
||||
current = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if current == nil then
|
||||
return
|
||||
end
|
||||
local append = {[-1] = {}, [1] = {}}
|
||||
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
|
||||
for i = 1, MAXENTRIES do
|
||||
local file = files[current + i * direction]
|
||||
local pl_e = pl[pl_current + i * direction]
|
||||
if file == nil or file[1] == "." then
|
||||
break
|
||||
end
|
||||
local filepath = dir .. file
|
||||
if pl_e then
|
||||
-- If there's a playlist entry, and it's the same file, stop.
|
||||
if pl_e.filename == filepath then
|
||||
break
|
||||
end
|
||||
end
|
||||
if direction == -1 then
|
||||
if pl_current == 1 then -- never add additional entries in the middle
|
||||
mp.msg.info("Prepending " .. file)
|
||||
table.insert(append[-1], 1, filepath)
|
||||
end
|
||||
else
|
||||
mp.msg.info("Adding " .. file)
|
||||
table.insert(append[1], filepath)
|
||||
end
|
||||
end
|
||||
end
|
||||
add_files_at(pl_current + 1, append[1])
|
||||
add_files_at(pl_current, append[-1])
|
||||
end
|
||||
|
||||
mp.register_event("start-file", find_and_add_entries)
|
||||
@@ -1,56 +0,0 @@
|
||||
-- This script cycles between deinterlacing, pullup (inverse
|
||||
-- telecine), and both filters off. It uses the "deinterlace" property
|
||||
-- so that a hardware deinterlacer will be used if available.
|
||||
--
|
||||
-- It overrides the default deinterlace toggle keybinding "D"
|
||||
-- (shift+d), so that rather than merely cycling the "deinterlace" property
|
||||
-- between on and off, it adds a "pullup" step to the cycle.
|
||||
--
|
||||
-- It provides OSD feedback as to the actual state of the two filters
|
||||
-- after each cycle step/keypress.
|
||||
--
|
||||
-- Note: if hardware decoding is enabled, pullup filter will likely
|
||||
-- fail to insert.
|
||||
--
|
||||
-- TODO: It might make sense to use hardware assisted vdpaupp=pullup,
|
||||
-- if available, but I don't have hardware to test it. Patch welcome.
|
||||
|
||||
script_name = mp.get_script_name()
|
||||
pullup_label = string.format("%s-pullup", script_name)
|
||||
|
||||
function pullup_on()
|
||||
for i,vf in pairs(mp.get_property_native('vf')) do
|
||||
if vf['label'] == pullup_label then
|
||||
return "yes"
|
||||
end
|
||||
end
|
||||
return "no"
|
||||
end
|
||||
|
||||
function do_cycle()
|
||||
if pullup_on() == "yes" then
|
||||
-- if pullup is on remove it
|
||||
mp.command(string.format("vf del @%s:pullup", pullup_label))
|
||||
return
|
||||
elseif mp.get_property("deinterlace") == "yes" then
|
||||
-- if deinterlace is on, turn it off and insert pullup filter
|
||||
mp.set_property("deinterlace", "no")
|
||||
mp.command(string.format("vf add @%s:pullup", pullup_label))
|
||||
return
|
||||
else
|
||||
-- if neither is on, turn on deinterlace
|
||||
mp.set_property("deinterlace", "yes")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function cycle_deinterlace_pullup_handler()
|
||||
do_cycle()
|
||||
-- independently determine current state and give user feedback
|
||||
mp.osd_message(string.format("deinterlace: %s\n"..
|
||||
"pullup: %s",
|
||||
mp.get_property("deinterlace"),
|
||||
pullup_on()))
|
||||
end
|
||||
|
||||
mp.add_key_binding("D", "cycle-deinterlace-pullup", cycle_deinterlace_pullup_handler)
|
||||
@@ -1,99 +0,0 @@
|
||||
-- This script enables live control of the dynamic range compression
|
||||
-- (drc) audio filter while the video is playing back. This can be
|
||||
-- useful to avoid having to stop and restart mpv to adjust filter
|
||||
-- parameters. See the entry for "drc" under the "AUDIO FILTERS"
|
||||
-- section of the man page for a complete description of the filter.
|
||||
--
|
||||
-- This script registers the key-binding "\" to toggle the filter between
|
||||
--
|
||||
-- * off
|
||||
-- * method=1 (single-sample smoothing)
|
||||
-- * method=2 (multi-sample smoothing)
|
||||
--
|
||||
-- It registers the keybindings ctrl+9/ctrl+0 to decrease/increase the
|
||||
-- target ampltiude. These keys will insert the filter at the default
|
||||
-- target amplitude of 0.25 if it was not previously present.
|
||||
--
|
||||
-- OSD feedback of the current filter state is displayed on pressing
|
||||
-- each bound key.
|
||||
|
||||
script_name = mp.get_script_name()
|
||||
|
||||
function print_state(params)
|
||||
if params then
|
||||
mp.osd_message(script_name..":\n"
|
||||
.."method = "..params["method"].."\n"
|
||||
.."target = "..params["target"])
|
||||
else
|
||||
mp.osd_message(script_name..":\noff")
|
||||
end
|
||||
end
|
||||
|
||||
function get_index_of_drc(afs)
|
||||
for i,af in pairs(afs) do
|
||||
if af["label"] == script_name then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function append_drc(afs)
|
||||
afs[#afs+1] = {
|
||||
name = "drc",
|
||||
label = script_name,
|
||||
params = {
|
||||
method = "1",
|
||||
target = "0.25"
|
||||
}
|
||||
}
|
||||
print_state(afs[#afs]["params"])
|
||||
end
|
||||
|
||||
function modify_or_create_af(fun)
|
||||
afs = mp.get_property_native("af")
|
||||
i = get_index_of_drc(afs)
|
||||
if not i then
|
||||
append_drc(afs)
|
||||
else
|
||||
fun(afs, i)
|
||||
end
|
||||
mp.set_property_native("af", afs)
|
||||
end
|
||||
|
||||
function drc_toggle_method_handler()
|
||||
modify_or_create_af(
|
||||
function (afs, i)
|
||||
new_method=(afs[i]["params"]["method"]+1)%3
|
||||
if new_method == 0 then
|
||||
table.remove(afs, i)
|
||||
print_state(nil)
|
||||
else
|
||||
afs[i]["params"]["method"] = tostring((afs[i]["params"]["method"])%2+1)
|
||||
print_state(afs[i]["params"])
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function drc_scale_target(factor)
|
||||
modify_or_create_af(
|
||||
function (afs)
|
||||
afs[i]["params"]["target"] = tostring(afs[i]["params"]["target"]*factor)
|
||||
print_state(afs[i]["params"])
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function drc_louder_handler()
|
||||
drc_scale_target(2.0)
|
||||
end
|
||||
|
||||
function drc_quieter_handler()
|
||||
drc_scale_target(0.5)
|
||||
end
|
||||
|
||||
-- toggle between off, method 1 and method 2
|
||||
mp.add_key_binding("\\", "drc_toggle_method", drc_toggle_method_handler)
|
||||
-- increase or decrease target volume
|
||||
mp.add_key_binding("ctrl+9", "drc_quieter", drc_quieter_handler)
|
||||
mp.add_key_binding("ctrl+0", "drc_louder", drc_louder_handler)
|
||||
@@ -1,16 +0,0 @@
|
||||
-- Test script for property change notification mechanism.
|
||||
|
||||
function format_property_val(v)
|
||||
if type(v) == "table" then
|
||||
return mp.format_table(v) -- undocumented function; might be removed
|
||||
else
|
||||
return tostring(v)
|
||||
end
|
||||
end
|
||||
|
||||
for i,name in ipairs(mp.get_property_native("property-list")) do
|
||||
mp.observe_property(name, "native", function(name, val)
|
||||
print("property '" .. name .. "' changed to '" ..
|
||||
format_property_val(val) .. "'")
|
||||
end)
|
||||
end
|
||||
@@ -1,69 +0,0 @@
|
||||
-- Rebuild the terminal status line as a lua script
|
||||
-- Be aware that this will require more cpu power!
|
||||
|
||||
-- Add a string to the status line
|
||||
function atsl(s)
|
||||
newStatus = newStatus .. s
|
||||
end
|
||||
|
||||
function update_status_line()
|
||||
-- Reset the status line
|
||||
newStatus = ""
|
||||
|
||||
if mp.get_property_bool("pause") then
|
||||
atsl("(Paused) ")
|
||||
elseif mp.get_property_bool("paused-for-cache") then
|
||||
atsl("(Buffering) ")
|
||||
end
|
||||
|
||||
if mp.get_property("vid") ~= "no" then
|
||||
atsl("A")
|
||||
end
|
||||
if mp.get_property("aid") ~= "no" then
|
||||
atsl("V")
|
||||
end
|
||||
|
||||
atsl(": ")
|
||||
|
||||
atsl(mp.get_property_osd("time-pos"))
|
||||
|
||||
atsl(" / ");
|
||||
atsl(mp.get_property_osd("length"));
|
||||
|
||||
atsl(" (")
|
||||
atsl(mp.get_property_osd("percent-pos", -1))
|
||||
atsl("%)")
|
||||
|
||||
local r = mp.get_property_number("speed", -1)
|
||||
if r ~= 1 then
|
||||
atsl(string.format(" x%4.2f", r))
|
||||
end
|
||||
|
||||
r = mp.get_property_number("avsync", nil)
|
||||
if r ~= nil then
|
||||
atsl(string.format(" A-V: %7.3f", r))
|
||||
end
|
||||
|
||||
r = mp.get_property("total-avsync-change", 0)
|
||||
if math.abs(r) > 0.05 then
|
||||
atsl(string.format(" ct:%7.3f", r))
|
||||
end
|
||||
|
||||
r = mp.get_property_number("drop-frame-count", -1)
|
||||
if r > 0 then
|
||||
atsl(" Late: ")
|
||||
atsl(r)
|
||||
end
|
||||
|
||||
r = mp.get_property_number("cache", 0)
|
||||
if r > 0 then
|
||||
atsl(string.format(" Cache: %d%% ", r))
|
||||
end
|
||||
|
||||
-- Set the new status line
|
||||
mp.set_property("options/term-status-msg", newStatus)
|
||||
end
|
||||
|
||||
-- Register the event
|
||||
mp.register_event("tick", update_status_line)
|
||||
|
||||
+3
-3
@@ -56,7 +56,7 @@ sub generate_c_header {
|
||||
my $l = max(map { length $_->{valname} } values %{$el->{subelements}});
|
||||
|
||||
# Output each variable, with pointers for array (multiple) elements
|
||||
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
|
||||
for my $subel (values %{$el->{subelements}}) {
|
||||
printf " %-${l}s %s%s;\n",
|
||||
$subel->{valname}, $subel->{multiple}?'*':' ', $subel->{fieldname};
|
||||
}
|
||||
@@ -101,7 +101,7 @@ sub generate_c_definitions {
|
||||
|
||||
# define a field for each subelement
|
||||
# also does lots of macro magic, but doesn't open a scope
|
||||
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
|
||||
for my $subel (values %{$el->{subelements}}) {
|
||||
print "F($subel->{definename}, $subel->{fieldname}, ".
|
||||
($subel->{multiple}?'1':'0').")\n";
|
||||
}
|
||||
@@ -166,4 +166,4 @@ sub process_elem {
|
||||
} else {
|
||||
print "$elem->{type} ". $elem->get_value ."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
-127
@@ -17,134 +17,157 @@
|
||||
# When multiple files were specified, their info will be put into FOO_* for the
|
||||
# first file, FOO_1_* for the second file, FOO_2_* for the third file, etc.
|
||||
|
||||
__midentify__main() {
|
||||
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
[ -n "$1" ] && set -- '' "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
cat >&2 <<EOF
|
||||
Usage 1 (for humans only): $0 filename.mkv
|
||||
will print all property values.
|
||||
Note that this output really shouldn't be parsed, as the
|
||||
format is subject to change.
|
||||
|
||||
Usage 2 (for use by scripts): see top of this file
|
||||
|
||||
NOTE: for mkv with ordered chapters, this may
|
||||
not always identify the specified file, but the
|
||||
file providing the first chapter. Specify
|
||||
--no-ordered-chapters to prevent this.
|
||||
EOF
|
||||
return 2
|
||||
fi
|
||||
|
||||
local LF="
|
||||
"
|
||||
|
||||
local nextprefix="$1"
|
||||
shift
|
||||
|
||||
if [ -n "$nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
unset "${nextprefix}path"
|
||||
fi
|
||||
|
||||
local allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
video-aspect
|
||||
fps
|
||||
width
|
||||
height
|
||||
dwidth
|
||||
dheight
|
||||
|
||||
sub
|
||||
"
|
||||
# TODO add metadata support once mpv can do it
|
||||
|
||||
local propstr="X-MIDENTIFY-START:$LF"
|
||||
local key
|
||||
for key in $allprops; do
|
||||
propstr="${propstr}X-MIDENTIFY: $key \${=$key}$LF"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
|
||||
local fileindex=0
|
||||
local prefix=
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
X-MIDENTIFY-START:)
|
||||
if [ -n "$nextprefix" ]; then
|
||||
prefix="$nextprefix"
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
nextprefix="${prefix%${fileindex}_}"
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
nextprefix="${nextprefix}${fileindex}_"
|
||||
for key in $allprops; do
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
else
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
printf '\n'
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
fi
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
case "$1" in
|
||||
'')
|
||||
;;
|
||||
X-MIDENTIFY:\ *)
|
||||
local key="${line#X-MIDENTIFY: }"
|
||||
local value="${key#* }"
|
||||
key="${key%% *}"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
if [ -n "$nextprefix" ]; then
|
||||
if [ -z "$prefix" ]; then
|
||||
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
|
||||
elif [ -n "$value" ]; then
|
||||
eval "$prefix$key"='"$value"'
|
||||
fi
|
||||
else
|
||||
if [ -n "$value" ]; then
|
||||
printf '%s=%s\n' "$key" "$value"
|
||||
fi
|
||||
fi
|
||||
*)
|
||||
set -- '' "$@"
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$(${MPV:-mpv} --term-playing-msg="$propstr" --vo=null --ao=null \
|
||||
--frames=1 --quiet --no-cache --no-config -- "$@")
|
||||
EOF
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
__midentify__main "$@"
|
||||
if [ $# -lt 2 ]; then
|
||||
echo >&2 "Usage 1 (for humans only): $0 filename.mkv"
|
||||
echo >&2 "will print all property values."
|
||||
echo >&2 "Note that this output really shouldn't be parsed, as the"
|
||||
echo >&2 "format is subject to change."
|
||||
echo >&2
|
||||
echo >&2 "Usage 2 (for use by scripts): see top of this file"
|
||||
echo >&2
|
||||
echo >&2 "NOTE: for mkv with ordered chapters, this may"
|
||||
echo >&2 "not always identify the specified file, but the"
|
||||
echo >&2 "file providing the first chapter. Specify"
|
||||
echo >&2 "--no-ordered-chapters to prevent this."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MPV" ]; then
|
||||
MPV="mpv"
|
||||
fi
|
||||
|
||||
__midentify__LF="
|
||||
"
|
||||
|
||||
__midentify__nextprefix=$1
|
||||
shift
|
||||
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
eval unset $__midentify__nextprefix'path'
|
||||
fi
|
||||
|
||||
__midentify__allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
aspect
|
||||
fps
|
||||
width
|
||||
height
|
||||
dwidth
|
||||
dheight
|
||||
|
||||
sub
|
||||
"
|
||||
# TODO add metadata support once mpv can do it
|
||||
|
||||
__midentify__propstr="X-MIDENTIFY-START:$__midentify__LF"
|
||||
for __midentify__key in $__midentify__allprops; do
|
||||
__midentify__propstr=$__midentify__propstr"X-MIDENTIFY: $__midentify__key \${=$__midentify__key}$__midentify__LF"
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
eval unset $__midentify__nextprefix$__midentify__key
|
||||
done
|
||||
|
||||
__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet "$@"`
|
||||
__midentify__fileindex=0
|
||||
__midentify__prefix=
|
||||
while :; do
|
||||
case "$__midentify__output" in
|
||||
'')
|
||||
break
|
||||
;;
|
||||
*$__midentify__LF*)
|
||||
__midentify__line=${__midentify__output%%$__midentify__LF*}
|
||||
__midentify__output=${__midentify__output#*$__midentify__LF}
|
||||
;;
|
||||
*)
|
||||
__midentify__line=$__midentify__output
|
||||
__midentify__output=
|
||||
;;
|
||||
esac
|
||||
case "$__midentify__line" in
|
||||
X-MIDENTIFY-START:)
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
__midentify__prefix=$__midentify__nextprefix
|
||||
if [ $__midentify__fileindex -gt 0 ]; then
|
||||
__midentify__nextprefix=${__midentify__prefix%$__midentify__fileindex\_}
|
||||
fi
|
||||
__midentify__fileindex=$(($__midentify__fileindex+1))
|
||||
__midentify__nextprefix=$__midentify__nextprefix$__midentify__fileindex\_
|
||||
for __midentify__key in $__midentify__allprops; do
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
eval unset $__midentify__nextprefix$__midentify__key
|
||||
done
|
||||
else
|
||||
if [ $__midentify__fileindex -gt 0 ]; then
|
||||
echo
|
||||
fi
|
||||
__midentify__fileindex=$(($__midentify__fileindex+1))
|
||||
fi
|
||||
;;
|
||||
X-MIDENTIFY:\ *)
|
||||
__midentify__key=${__midentify__line#X-MIDENTIFY:\ }
|
||||
__midentify__value=${__midentify__key#* }
|
||||
__midentify__key=${__midentify__key%% *}
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
if [ -z "$__midentify__prefix" ]; then
|
||||
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
|
||||
elif [ -n "$__midentify__value" ]; then
|
||||
eval $__midentify__prefix$__midentify__key=\$__midentify__value
|
||||
fi
|
||||
else
|
||||
if [ -n "$__midentify__value" ]; then
|
||||
echo "$__midentify__key=$__midentify__value"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
unset __midentify__fileindex
|
||||
unset __midentify__allprops
|
||||
unset __midentify__key
|
||||
unset __midentify__LF
|
||||
unset __midentify__line
|
||||
unset __midentify__output
|
||||
unset __midentify__nextprefix
|
||||
unset __midentify__prefix
|
||||
unset __midentify__propstr
|
||||
unset __midentify__value
|
||||
+74
-13
@@ -1,44 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
from textwrap import dedent
|
||||
|
||||
def sh(command):
|
||||
return os.popen(command).read()
|
||||
|
||||
def bundle_path(binary_name):
|
||||
return "%s.app" % binary_name
|
||||
def dylib_lst(input_file):
|
||||
return sh("otool -L %s | grep -e '\t' | awk '{ print $1 }'" % input_file)
|
||||
|
||||
sys_re = re.compile("/System")
|
||||
exe_re = re.compile("@executable_path")
|
||||
binary_name = sys.argv[1]
|
||||
|
||||
def is_user_lib(libname, input_file):
|
||||
return not sys_re.match(libname) and \
|
||||
not exe_re.match(libname) and \
|
||||
not "libobjc" in libname and \
|
||||
not "libSystem" in libname and \
|
||||
not "libgcc" in libname and \
|
||||
not os.path.basename(input_file) in libname and \
|
||||
not libname == ''
|
||||
|
||||
def user_dylib_lst(input_file):
|
||||
return [lib for lib in dylib_lst(input_file).split("\n") if
|
||||
is_user_lib(lib, input_file)]
|
||||
|
||||
def bundle_name(binary_name):
|
||||
return os.path.basename(bundle_path(binary_name))
|
||||
return "%s.app" % binary_name
|
||||
|
||||
def target_plist(binary_name):
|
||||
return os.path.join(bundle_path(binary_name), 'Contents', 'Info.plist')
|
||||
return os.path.join(bundle_name(binary_name), 'Contents', 'Info.plist')
|
||||
|
||||
def target_directory(binary_name):
|
||||
return os.path.join(bundle_path(binary_name), 'Contents', 'MacOS')
|
||||
return os.path.join(bundle_name(binary_name), 'Contents', 'MacOS')
|
||||
|
||||
def target_binary(binary_name):
|
||||
return os.path.join(target_directory(binary_name),
|
||||
os.path.basename(binary_name))
|
||||
return os.path.join(target_directory(binary_name), binary_name)
|
||||
|
||||
def copy_bundle(binary_name):
|
||||
if os.path.isdir(bundle_path(binary_name)):
|
||||
shutil.rmtree(bundle_path(binary_name))
|
||||
if os.path.isdir(bundle_name(binary_name)):
|
||||
shutil.rmtree(bundle_name(binary_name))
|
||||
shutil.copytree(
|
||||
os.path.join('TOOLS', 'osxbundle', bundle_name(binary_name)),
|
||||
bundle_path(binary_name))
|
||||
bundle_name(binary_name))
|
||||
|
||||
def copy_binary(binary_name):
|
||||
shutil.copy(binary_name, target_binary(binary_name))
|
||||
|
||||
def run_install_name_tool(target_file, dylib_path, dest_dir, root=True):
|
||||
new_dylib_path = os.path.join("@executable_path", "lib",
|
||||
os.path.basename(dylib_path))
|
||||
|
||||
sh("install_name_tool -change %s %s %s" % \
|
||||
(dylib_path, new_dylib_path, target_file))
|
||||
if root:
|
||||
sh("install_name_tool -id %s %s" % \
|
||||
(new_dylib_path, os.path.join(dest_dir,
|
||||
os.path.basename(dylib_path))))
|
||||
|
||||
def cp_dylibs(target_file, dest_dir):
|
||||
for dylib_path in user_dylib_lst(target_file):
|
||||
dylib_dest_path = os.path.join(dest_dir, os.path.basename(dylib_path))
|
||||
|
||||
try:
|
||||
shutil.copy(dylib_path, dylib_dest_path)
|
||||
except IOError:
|
||||
if re.match("dylib$", target_file):
|
||||
reinstall_what = target_file
|
||||
else:
|
||||
reinstall_what = dylib_path
|
||||
|
||||
sys.exit(dedent("""\
|
||||
%s uses library %s which is not available anymore.
|
||||
This is most likely because you uninstalled %s.
|
||||
Please reinstall %s to fix it's dependencies.""" % \
|
||||
(target_file, dylib_path, dylib_path, reinstall_what) ))
|
||||
|
||||
os.chmod(dylib_dest_path, 0o755)
|
||||
cp_dylibs(dylib_dest_path, dest_dir)
|
||||
|
||||
def fix_dylibs_paths(target_file, dest_dir, root=True):
|
||||
for dylib_path in user_dylib_lst(target_file):
|
||||
dylib_dest_path = os.path.join(dest_dir, os.path.basename(dylib_path))
|
||||
run_install_name_tool(target_file, dylib_path, dest_dir, root)
|
||||
fix_dylibs_paths(dylib_dest_path, dest_dir, False)
|
||||
|
||||
def apply_plist_template(plist_file, version):
|
||||
sh("sed -i -e 's/${VERSION}/%s/g' %s" % (version, plist_file))
|
||||
sh("sed -i -e 's/{{VERSION}}/%s/g' %s" % (version, plist_file))
|
||||
|
||||
def bundle_dependencies(binary_name):
|
||||
lib_bundle_directory = os.path.join(target_directory(binary_name), "lib")
|
||||
cp_dylibs(binary_name, lib_bundle_directory)
|
||||
fix_dylibs_paths(target_binary(binary_name), lib_bundle_directory)
|
||||
|
||||
def main():
|
||||
version = sh("./version.sh --print").strip()
|
||||
version = sh("TOOLS/osxbundle/version.sh").strip()
|
||||
|
||||
usage = "usage: %prog [options] arg"
|
||||
parser = OptionParser(usage)
|
||||
@@ -63,7 +124,7 @@ def main():
|
||||
|
||||
if options.deps:
|
||||
print("> bundling dependencies")
|
||||
sh(" ".join(["TOOLS/dylib-unhell.py", target_binary(binary_name)]))
|
||||
bundle_dependencies(binary_name)
|
||||
|
||||
print("done.")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
@@ -183,7 +183,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${VERSION}</string>
|
||||
<string>{{VERSION}}</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -230,592 +230,5 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>AC3 Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.ac3</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://wiki.multimedia.cx/index.php?title=AC3</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ac3</string>
|
||||
<string>a52</string>
|
||||
<string>eac3</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>DTS Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.dts</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://wiki.multimedia.cx/index.php?title=DTS</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>dts</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Free Lossless Audio Codec</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.flac</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://flac.sourceforge.net/format.html</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>flac</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Matroska Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.mka</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.matroska.org</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mka</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Ogg Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.ogg-audio</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://xiph.org/ogg</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ogg</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>PCM Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.pcm</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/Pulse-code_modulation</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>pcm</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.audio</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Windows Media Audio</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.wma</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/Windows_Media_Audio</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>wma</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Audio Video Interleave</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.avi</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.the-labs.com/Video/odmlff2-avidef.pdf</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>avi</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>DIVX Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.divx</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.divx.com</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>divx</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>DV Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.dv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/DV</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>dv</string>
|
||||
<string>hdv</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Flash Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.flv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/Flash_Video</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>flv</string>
|
||||
<string>f4v</string>
|
||||
<string>f4p</string>
|
||||
<string>swf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>MPEG-2 Transport Stream</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.mts</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/.m2ts</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>trp</string>
|
||||
<string>m2t</string>
|
||||
<string>m2ts</string>
|
||||
<string>mts</string>
|
||||
<string>mtv</string>
|
||||
<string>ts</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Matroska Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.mkv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.matroska.org</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mkv</string>
|
||||
<string>webm</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Ogg Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.ogv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://xiph.org/ogg</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ogm</string>
|
||||
<string>ogv</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Real Media</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.rmvb</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.real.com</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>rmvb</string>
|
||||
<string>rm</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Video Object</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.vob</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/VOB</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>vob</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Windows Media Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.wmv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/Windows_Media_Video</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>wmv</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>XVID Video</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.xvid</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.xvid.org</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>xvid</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>AVC raw stream</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.h264</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.itu.int/rec/T-REC-H.264</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>264</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>HEVC raw stream</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.hevc</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://hevc.info</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>hevc</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>YUV stream</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.yuv</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/YUV</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>yuv</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.movie</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>YUV4MPEG2 stream</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.y4m</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://wiki.multimedia.cx/index.php?title=YUV4MPEG2</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>y4m</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>SubRip Subtitle</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.subrip</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/SubRip</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>srt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>MicroDVD Subtitle</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.sub</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/MicroDVD</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>sub</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>SubStation Alpha Subtitle</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.ass</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>https://github.com/libass/libass</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ass</string>
|
||||
<string>ssa</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>VobSub Subtitle</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.vobsub</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/DirectVobSub</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>idx</string>
|
||||
<string>sub</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>SAMI Subtitle</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>document.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.mpv.smi</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://en.wikipedia.org/wiki/SAMI</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>smi</string>
|
||||
<string>smil</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<dir>/System/Library/Fonts</dir>
|
||||
<dir>/Library/Fonts</dir>
|
||||
<dir>~/Library/Fonts</dir>
|
||||
<dir>/Network/Library/Fonts</dir>
|
||||
<cachedir>~/.fontconfig</cachedir>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'mono' alias, replacing it with 'monospace'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>mono</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>monospace</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'sans' alias, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<config>
|
||||
<!--
|
||||
These are the default Unicode chars that are expected to be blank
|
||||
in fonts. All other blank chars are assumed to be broken and
|
||||
won't appear in the resulting charsets
|
||||
-->
|
||||
<blank>
|
||||
<int>0x0020</int> <!-- SPACE -->
|
||||
<int>0x00A0</int> <!-- NO-BREAK SPACE -->
|
||||
<int>0x00AD</int> <!-- SOFT HYPHEN -->
|
||||
<int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
|
||||
<int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
|
||||
<int>0x0601</int> <!-- ARABIC SIGN SANAH -->
|
||||
<int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
|
||||
<int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
|
||||
<int>0x06DD</int> <!-- ARABIC END OF AYAH -->
|
||||
<int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
|
||||
<int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
|
||||
<int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
|
||||
<int>0x1680</int> <!-- OGHAM SPACE MARK -->
|
||||
<int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
|
||||
<int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
|
||||
<int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
|
||||
<int>0x2000</int> <!-- EN QUAD -->
|
||||
<int>0x2001</int> <!-- EM QUAD -->
|
||||
<int>0x2002</int> <!-- EN SPACE -->
|
||||
<int>0x2003</int> <!-- EM SPACE -->
|
||||
<int>0x2004</int> <!-- THREE-PER-EM SPACE -->
|
||||
<int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
|
||||
<int>0x2006</int> <!-- SIX-PER-EM SPACE -->
|
||||
<int>0x2007</int> <!-- FIGURE SPACE -->
|
||||
<int>0x2008</int> <!-- PUNCTUATION SPACE -->
|
||||
<int>0x2009</int> <!-- THIN SPACE -->
|
||||
<int>0x200A</int> <!-- HAIR SPACE -->
|
||||
<int>0x200B</int> <!-- ZERO WIDTH SPACE -->
|
||||
<int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
|
||||
<int>0x200D</int> <!-- ZERO WIDTH JOINER -->
|
||||
<int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
|
||||
<int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
|
||||
<int>0x2028</int> <!-- LINE SEPARATOR -->
|
||||
<int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
|
||||
<int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
|
||||
<int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
|
||||
<int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
|
||||
<int>0x202D</int> <!-- LEFT-TO-RIGHT OVERRIDE -->
|
||||
<int>0x202E</int> <!-- RIGHT-TO-LEFT OVERRIDE -->
|
||||
<int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
|
||||
<int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
|
||||
<int>0x2060</int> <!-- WORD JOINER -->
|
||||
<int>0x2061</int> <!-- FUNCTION APPLICATION -->
|
||||
<int>0x2062</int> <!-- INVISIBLE TIMES -->
|
||||
<int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
|
||||
<int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
|
||||
<int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
|
||||
<int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
|
||||
<int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
|
||||
<int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
|
||||
<int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
|
||||
<int>0x2800</int> <!-- BRAILLE PATTERN BLANK -->
|
||||
<int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
|
||||
<int>0x3164</int> <!-- HANGUL FILLER -->
|
||||
<int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
|
||||
<int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
|
||||
<int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
|
||||
<int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
|
||||
<int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
|
||||
</blank>
|
||||
<!--
|
||||
Rescan configuration every 30 seconds when FcFontSetList is called
|
||||
-->
|
||||
<rescan>
|
||||
<int>30</int>
|
||||
</rescan>
|
||||
</config>
|
||||
</fontconfig>
|
||||
@@ -1 +0,0 @@
|
||||
screenshot-template=~/Desktop/shot%n
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract revision number from file used by daily tarball snapshots
|
||||
# or from "git describe" output
|
||||
git_revision=$(cat snapshot_version 2> /dev/null)
|
||||
test $git_revision || test ! -d .git || \
|
||||
git_revision=`git describe --match "v[0-9]*" --always`
|
||||
git_revision=$(expr "$git_revision" : v*'\(.*\)')
|
||||
test $git_revision || git_revision=UNKNOWN
|
||||
|
||||
# releases extract the version number from the VERSION file
|
||||
version=$(cat VERSION 2> /dev/null)
|
||||
test $version || version=$git_revision
|
||||
|
||||
echo $version
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import matplotlib.pyplot as plot
|
||||
import sys
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
"""
|
||||
This script is meant to display stats written by mpv --dump-stats=filename.
|
||||
In general, each line in that file is an event of the form:
|
||||
|
||||
<timestamp in microseconds> <text> '#' <comment>
|
||||
|
||||
e.g.:
|
||||
|
||||
10474959 start flip #cplayer
|
||||
|
||||
<text> is what MP_STATS(log, "...") writes. The rest is added by msg.c.
|
||||
|
||||
Currently, the following event types are supported:
|
||||
|
||||
'start' <name> start of the named event
|
||||
'end' <name> end of the named event
|
||||
'value' <float> <name> a normal value (as opposed to event)
|
||||
'event-timed' <ts> <name> singular event at the given timestamp
|
||||
<name> singular event
|
||||
|
||||
"""
|
||||
|
||||
class G:
|
||||
events = {}
|
||||
sevents = [] # events, deterministically sorted
|
||||
start = None
|
||||
# http://matplotlib.org/api/markers_api.html#module-matplotlib.markers
|
||||
markers = ["o", "8", "s", "p", "*", "h", "+", "x", "D"]
|
||||
|
||||
def find_marker():
|
||||
if len(G.markers) == 0:
|
||||
return "o"
|
||||
m = G.markers[0]
|
||||
G.markers = G.markers[1:]
|
||||
return m
|
||||
|
||||
class Event:
|
||||
pass
|
||||
|
||||
def get_event(event, evtype):
|
||||
if event not in G.events:
|
||||
e = Event()
|
||||
G.events[event] = e
|
||||
e.name = event
|
||||
e.vals = []
|
||||
e.type = evtype
|
||||
e.marker = "o"
|
||||
e.numid = len(G.events)
|
||||
G.sevents = list(G.events.values())
|
||||
G.sevents.sort(key=lambda x: x.name)
|
||||
if e.type == "event-signal":
|
||||
e.marker = find_marker()
|
||||
return G.events[event]
|
||||
|
||||
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
ts, event = line.split(" ", 1)
|
||||
ts = int(ts) / 1000 # milliseconds
|
||||
if G.start is None:
|
||||
G.start = ts
|
||||
ts = ts - G.start
|
||||
if event.startswith("start "):
|
||||
e = get_event(event[6:], "event")
|
||||
e.vals.append((ts, 0))
|
||||
e.vals.append((ts, 1))
|
||||
elif event.startswith("end "):
|
||||
e = get_event(event[4:], "event")
|
||||
e.vals.append((ts, 1))
|
||||
e.vals.append((ts, 0))
|
||||
elif event.startswith("value "):
|
||||
_, val, name = event.split(" ", 2)
|
||||
val = float(val)
|
||||
e = get_event(name, "value")
|
||||
e.vals.append((ts, val))
|
||||
elif event.startswith("event-timed "):
|
||||
_, val, name = event.split(" ", 2)
|
||||
val = int(val) / 1000 - G.start
|
||||
e = get_event(name, "event-signal")
|
||||
e.vals.append((val, 1))
|
||||
else:
|
||||
e = get_event(event, "event-signal")
|
||||
e.vals.append((ts, 1))
|
||||
|
||||
for e in G.sevents:
|
||||
e.vals = [(x, y * e.numid / len(G.events)) for (x, y) in e.vals]
|
||||
|
||||
plot.hold(True)
|
||||
mainpl = plot.subplot(2, 1, 1)
|
||||
legend = []
|
||||
for e in G.sevents:
|
||||
if e.type == "value":
|
||||
plot.subplot(2, 1, 2, sharex=mainpl)
|
||||
else:
|
||||
plot.subplot(2, 1, 1)
|
||||
pl, = plot.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
|
||||
if e.type == "event-signal":
|
||||
plot.setp(pl, marker = e.marker, linestyle = "None")
|
||||
legend.append(pl)
|
||||
plot.subplot(2, 1, 1)
|
||||
plot.legend(legend, [pl.get_label() for pl in legend])
|
||||
plot.show()
|
||||
+35
-41
@@ -3,31 +3,32 @@
|
||||
"""
|
||||
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). Other mpv instances (not started
|
||||
by umpv) are ignored, and the script doesn't know about them.
|
||||
mpv (but only if that was started with umpv).
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
If mpv isn't running yet, this script will start mpv and let it control the
|
||||
current terminal. It will not write output to stdout/stderr, because this
|
||||
will typically just fill ~/.xsession-errors with garbage.
|
||||
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).
|
||||
|
||||
mpv will terminate if there are no more files to play, and running the umpv
|
||||
script after that will start a new mpv instance.
|
||||
|
||||
Note that you can control the mpv instance by writing to the command fifo:
|
||||
|
||||
echo "cycle fullscreen" > ~/.umpv-fifo
|
||||
echo "cycle fullscreen" > /tmp/umpv-fifo-*
|
||||
|
||||
Note: you can supply custom mpv path and options with the MPV environment
|
||||
variable. The environment variable will be split on whitespace, and the
|
||||
first item is used as path to mpv binary and the rest is passed as options
|
||||
_if_ the script starts mpv. If mpv is not started by the script (i.e. mpv
|
||||
is already running), this 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
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -36,28 +37,15 @@ import errno
|
||||
import subprocess
|
||||
import fcntl
|
||||
import stat
|
||||
import string
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("error: needs at least one file as argument.")
|
||||
sys.exit(1)
|
||||
files = sys.argv[1:]
|
||||
|
||||
# this is 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
|
||||
allowed_symbols = string.ascii_letters + string.digits + '_'
|
||||
prefix = parts[0]
|
||||
return all(map(lambda c: c in allowed_symbols, prefix))
|
||||
|
||||
# make them absolute; also makes them safe against interpretation as options
|
||||
def make_abs(filename):
|
||||
if not is_url(filename):
|
||||
return os.path.abspath(filename)
|
||||
return filename
|
||||
files = [make_abs(f) for f in files]
|
||||
files = [os.path.abspath(f) for f in files]
|
||||
|
||||
FIFO = os.path.join(os.getenv("HOME"), ".umpv_fifo")
|
||||
FIFO = "/tmp/umpv-fifo-" + os.getenv("USER")
|
||||
|
||||
fifo_fd = -1
|
||||
try:
|
||||
@@ -70,6 +58,14 @@ except OSError as e:
|
||||
else:
|
||||
raise e
|
||||
|
||||
if fifo_fd >= 0:
|
||||
st = os.fstat(fifo_fd)
|
||||
if (((st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)) != 0) or
|
||||
(not stat.S_ISFIFO(st.st_mode)) or
|
||||
(st.st_uid != os.getuid())):
|
||||
print("error: command FIFO is not a FIFO or has bogus permissions")
|
||||
sys.exit(1)
|
||||
|
||||
if fifo_fd >= 0:
|
||||
# Unhandled race condition: what if mpv is terminating right now?
|
||||
fcntl.fcntl(fifo_fd, fcntl.F_SETFL, 0) # set blocking mode
|
||||
@@ -86,12 +82,10 @@ else:
|
||||
try:
|
||||
os.unlink(FIFO)
|
||||
except OSError as e:
|
||||
pass
|
||||
os.mkfifo(FIFO, 0o600)
|
||||
if e.errno == errno.ENOENT:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
os.mkfifo(FIFO, 0600)
|
||||
|
||||
opts = (os.getenv("MPV") or "mpv").split()
|
||||
opts.extend(["--no-terminal", "--force-window", "--input-file=" + FIFO,
|
||||
"--"])
|
||||
opts.extend(files)
|
||||
|
||||
subprocess.check_call(opts)
|
||||
subprocess.check_call(["mpv", "--input-file=" + FIFO] + files)
|
||||
@@ -32,11 +32,11 @@
|
||||
/*
|
||||
* frame stepping filter
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/framestep.so:5
|
||||
* usage: -vf dlopen=./framestep.so:5
|
||||
*
|
||||
* outputs every 5th frame
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/framestep.so:5:3
|
||||
* usage: -vf dlopen=./framestep.so:5:3
|
||||
*
|
||||
* outputs every 5th frame, starting with frame index 3 (default: 0)
|
||||
*/
|
||||
@@ -90,7 +90,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (argc != 1 && argc != 2)
|
||||
return -1;
|
||||
|
||||
framestep_data_t *framestep = calloc(1,sizeof(framestep_data_t));
|
||||
framestep_data_t *framestep = malloc(sizeof(framestep_data_t));
|
||||
memset(framestep, 0, sizeof(*framestep));
|
||||
|
||||
framestep->step = atoi(argv[0]);
|
||||
framestep->pos = (argc >= 2) ? atoi(argv[1]) : 0;
|
||||
|
||||
@@ -267,7 +267,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
ildetect_data_t *il = calloc(1,sizeof(ildetect_data_t));
|
||||
ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
|
||||
memset(il, 0, sizeof(*il));
|
||||
|
||||
#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
|
||||
il->method = A(0, 0);
|
||||
@@ -283,7 +284,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
il->tc_decision_threshold = A(10, 0.2);
|
||||
|
||||
static struct vf_dlopen_formatpair map[] = {
|
||||
{ "gray", "gray" },
|
||||
{ "y8", "y8" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
ctx->format_mapping = map;
|
||||
|
||||
@@ -11,11 +11,11 @@ esac
|
||||
|
||||
: ${MPV:=mpv}
|
||||
: ${ILDETECT_MPV:=$MPV}
|
||||
: ${ILDETECT_MPV:=$MPV}
|
||||
: ${ILDETECT_MPVFLAGS:=--start=35% --length=35}
|
||||
: ${ILDETECT_DRY_RUN:=}
|
||||
: ${ILDETECT_QUIET:=}
|
||||
: ${ILDETECT_RUN_INTERLACED_ONLY:=}
|
||||
: ${ILDETECT_FORCE_RUN:=}
|
||||
: ${MAKE:=make}
|
||||
|
||||
# exit status:
|
||||
@@ -76,12 +76,7 @@ case "$out" in
|
||||
exit 2
|
||||
;;
|
||||
*"probably: "*)
|
||||
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" -vf-pre yadif
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
exit 0
|
||||
exit 8
|
||||
;;
|
||||
*)
|
||||
exit 16
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
/*
|
||||
* rectangle
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/rectangle.so
|
||||
* usage: -vf dlopen=./rectangle.so
|
||||
*
|
||||
* provides an editable rectangle
|
||||
* NOTE: unix only, and requires xterm to be installed. Don't ask.
|
||||
@@ -345,10 +345,11 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
static struct vf_dlopen_formatpair map[] = {
|
||||
{ "yuv420p", "yuv420p" },
|
||||
{ "yv12", "yv12" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
privdata *priv = calloc(1,sizeof(privdata));
|
||||
privdata *priv = malloc(sizeof(privdata));
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->step = 8;
|
||||
if(argc >= 1)
|
||||
priv->w = atoi(argv[0]);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
/*
|
||||
* qscale visualizer
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/showqscale.so
|
||||
* usage: -vf dlopen=./showqscale.so
|
||||
*
|
||||
* uses reddish colors for high QPs, and greenish colors for low QPs
|
||||
*/
|
||||
@@ -111,7 +111,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
static struct vf_dlopen_formatpair map[] = {
|
||||
{ "yuv420p", "yuv420p" },
|
||||
{ "yv12", "yv12" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
ctx->format_mapping = map;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
/*
|
||||
* telecine filter
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/telecine.so:t:23
|
||||
* usage: -vf dlopen=./telecine.so:t:23
|
||||
*
|
||||
* Parameter: first parameter is "t" for top field first, "b" for bottom field first
|
||||
* then digits (0-9) for how many fields a frame is to be displayed
|
||||
@@ -237,7 +237,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (!a0[0] || a0[1] || !a1[0] || argc > 2)
|
||||
return -1;
|
||||
|
||||
tc_data_t *tc = calloc(1,sizeof(tc_data_t));
|
||||
tc_data_t *tc = malloc(sizeof(tc_data_t));
|
||||
memset(tc, 0, sizeof(*tc));
|
||||
|
||||
if (a0[0] == 't')
|
||||
tc->firstfield = 0;
|
||||
|
||||
@@ -30,17 +30,17 @@
|
||||
/*
|
||||
* tile filter
|
||||
*
|
||||
* usage: --vf=dlopen=/path/to/tile.so:4:3
|
||||
* usage: -vf dlopen=./tile.so:4:3
|
||||
*
|
||||
* only supports rgb24 and yuv420p for now
|
||||
* only supports rgb24 and yv12 for now
|
||||
* in theory can support any format where rows are a multiple of bytes, and the
|
||||
* multiple is known
|
||||
*/
|
||||
|
||||
#define ALLFORMATS \
|
||||
/* format bytes xmul ymul */ \
|
||||
FORMAT("rgb24" , 3, 1, 1) \
|
||||
FORMAT("yuv420p", 1, 2, 2)
|
||||
/* format bytes xmul ymul */ \
|
||||
FORMAT("rgb24", 3, 1, 1) \
|
||||
FORMAT("yv12", 1, 2, 2)
|
||||
|
||||
typedef struct {
|
||||
int rows, cols;
|
||||
@@ -150,7 +150,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
|
||||
tile_data_t *tile = calloc(1,sizeof(tile_data_t));
|
||||
tile_data_t *tile = malloc(sizeof(tile_data_t));
|
||||
memset(tile, 0, sizeof(*tile));
|
||||
|
||||
tile->cols = atoi(argv[0]);
|
||||
tile->rows = atoi(argv[1]);
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Example of script for using mpv with youtube-dl
|
||||
set -e
|
||||
|
||||
cookies_dir="$(mktemp -d /tmp/youtube-dl_mpv.XXXX)"
|
||||
cookies_file="${cookies_dir}/cookies"
|
||||
user_agent="$(youtube-dl --dump-user-agent)" # or set whatever you want
|
||||
|
||||
video_url="$(youtube-dl \
|
||||
--user-agent="$user_agent" \
|
||||
--cookies="$cookies_file" \
|
||||
--get-url \
|
||||
"$@")"
|
||||
|
||||
set -f
|
||||
${MPV:-mpv} \
|
||||
--cookies \
|
||||
--cookies-file="$cookies_file" \
|
||||
--user-agent="$user_agent" \
|
||||
-- $video_url
|
||||
|
||||
rm -rf "$cookies_dir"
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Generate ZSH completion
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $mpv = $ARGV[0] || 'mpv';
|
||||
|
||||
my @opts = parse_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)', 1);
|
||||
|
||||
my @ao = parse_opts("$mpv --ao=help", '^ ([^\s\:]*)\s*: (.*)');
|
||||
my @vo = parse_opts("$mpv --vo=help", '^ ([^\s\:]*)\s*: (.*)');
|
||||
|
||||
my @af = parse_opts("$mpv --af=help", '^ ([^\s\:]*)\s*: (.*)');
|
||||
my @vf = parse_opts("$mpv --vf=help", '^ ([^\s\:]*)\s*: (.*)');
|
||||
|
||||
my @protos = parse_opts("$mpv --list-protocols", '^ ([^\s]*)');
|
||||
|
||||
my ($opts_str, $ao_str, $vo_str, $af_str, $vf_str, $protos_str);
|
||||
|
||||
$opts_str .= qq{ '$_' \\\n} foreach (@opts);
|
||||
chomp $opts_str;
|
||||
|
||||
$ao_str .= qq{ '$_' \\\n} foreach (@ao);
|
||||
chomp $ao_str;
|
||||
|
||||
$vo_str .= qq{ '$_' \\\n} foreach (@vo);
|
||||
chomp $vo_str;
|
||||
|
||||
$af_str .= qq{ '$_' \\\n} foreach (@af);
|
||||
chomp $af_str;
|
||||
|
||||
$vf_str .= qq{ '$_' \\\n} foreach (@vf);
|
||||
chomp $vf_str;
|
||||
|
||||
$protos_str .= qq{$_ } foreach (@protos);
|
||||
chomp $protos_str;
|
||||
|
||||
my $profile_comp = <<'EOS';
|
||||
local -a profiles
|
||||
local current
|
||||
for current in "${(@f)$($words[1] --profile=help)}"; do
|
||||
current=${current//\*/\\\*}
|
||||
current=${current//\:/\\\:}
|
||||
current=${current//\[/\\\[}
|
||||
current=${current//\]/\\\]}
|
||||
if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
|
||||
if [[ -n $match[2] ]]; then
|
||||
current="$match[1][$match[2]]"
|
||||
else
|
||||
current="$match[1]"
|
||||
fi
|
||||
profiles=($profiles $current)
|
||||
fi
|
||||
done
|
||||
if [[ $state == profile ]]; then
|
||||
# For --show-profile, only one allowed
|
||||
if (( ${#profiles} > 0 )); then
|
||||
_values 'profile' $profiles && rc=0
|
||||
fi
|
||||
else
|
||||
# For --profile, multiple allowed
|
||||
profiles=($profiles 'help[list profiles]')
|
||||
_values -s , 'profile(s)' $profiles && rc=0
|
||||
fi
|
||||
EOS
|
||||
chomp $profile_comp;
|
||||
|
||||
my $tmpl = <<"EOS";
|
||||
#compdef mpv
|
||||
|
||||
# mpv zsh completion
|
||||
|
||||
local curcontext="\$curcontext" state state_descr line
|
||||
typeset -A opt_args
|
||||
|
||||
local rc=1
|
||||
|
||||
_arguments -C -S \\
|
||||
$opts_str
|
||||
'*:files:->mfiles' && rc=0
|
||||
|
||||
case \$state in
|
||||
ao)
|
||||
local -a values
|
||||
values=(
|
||||
$ao_str
|
||||
)
|
||||
|
||||
_describe -t values 'audio outputs' values && rc=0
|
||||
;;
|
||||
|
||||
vo)
|
||||
local -a values
|
||||
values=(
|
||||
$vo_str
|
||||
)
|
||||
|
||||
_describe -t values 'video outputs' values && rc=0
|
||||
;;
|
||||
|
||||
af)
|
||||
local -a values
|
||||
values=(
|
||||
$af_str
|
||||
)
|
||||
|
||||
_describe -t values 'audio filters' values && rc=0
|
||||
;;
|
||||
|
||||
vf)
|
||||
local -a values
|
||||
values=(
|
||||
$vf_str
|
||||
)
|
||||
|
||||
_describe -t values 'video filters' values && rc=0
|
||||
;;
|
||||
|
||||
profile|profiles)
|
||||
$profile_comp
|
||||
;;
|
||||
|
||||
files)
|
||||
compset -P '*,'
|
||||
compset -S ',*'
|
||||
_files -r ',/ \\t\\n\\-' && rc=0
|
||||
;;
|
||||
|
||||
mfiles)
|
||||
local expl
|
||||
_tags files urls
|
||||
while _tags; do
|
||||
_requested files expl 'media file' _files -g \\
|
||||
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
|
||||
if _requested urls; then
|
||||
while _next_label urls expl URL; do
|
||||
_urls "\$expl[@]" && rc=0
|
||||
compadd -S '' "\$expl[@]" $protos_str && rc=0
|
||||
done
|
||||
fi
|
||||
(( rc )) || return 0
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
return rc
|
||||
EOS
|
||||
|
||||
print $tmpl;
|
||||
|
||||
sub parse_opts {
|
||||
my ($cmd, $regex, $parsing_main_options) = @_;
|
||||
|
||||
my @list;
|
||||
my @lines = split /\n/, `$cmd`;
|
||||
|
||||
foreach my $line (@lines) {
|
||||
if ($line !~ /^$regex/) {
|
||||
next;
|
||||
}
|
||||
|
||||
my $entry = $1;
|
||||
|
||||
if ($parsing_main_options) {
|
||||
$entry .= '=-';
|
||||
}
|
||||
|
||||
if (defined $2) {
|
||||
my $desc = $2;
|
||||
$desc =~ s/\:/\\:/g;
|
||||
|
||||
$entry .= ':' . $desc;
|
||||
}
|
||||
|
||||
if ($parsing_main_options) {
|
||||
$entry .= ':';
|
||||
|
||||
$entry .= '->ao' if ($1 eq '--ao');
|
||||
$entry .= '->vo' if ($1 eq '--vo');
|
||||
$entry .= '->af' if ($1 eq '--af');
|
||||
$entry .= '->vf' if ($1 eq '--vf');
|
||||
$entry .= '->profiles' if ($1 eq '--profile');
|
||||
$entry .= '->profile' if ($1 eq '--show-profile');
|
||||
$entry .= '->files' if ($line =~ /\[file\]/);
|
||||
}
|
||||
|
||||
push @list, $entry
|
||||
}
|
||||
|
||||
if ($parsing_main_options) {
|
||||
@list = sort {
|
||||
$a =~ /(.*?)\:/; my $ma = $1;
|
||||
$b =~ /(.*?)\:/; my $mb = $1;
|
||||
|
||||
length($mb) <=> length($ma)
|
||||
} @list;
|
||||
}
|
||||
|
||||
return @list;
|
||||
}
|
||||
+20
-316
@@ -15,68 +15,44 @@
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/buffer.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include <libavutil/version.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "common/common.h"
|
||||
#include "fmt-conversion.h"
|
||||
#include "mpvcore/mp_talloc.h"
|
||||
#include "audio.h"
|
||||
|
||||
static void update_redundant_info(struct mp_audio *mpa)
|
||||
{
|
||||
assert(mp_chmap_is_empty(&mpa->channels) ||
|
||||
mp_chmap_is_valid(&mpa->channels));
|
||||
mpa->nch = mpa->channels.num;
|
||||
mpa->bps = af_fmt2bps(mpa->format);
|
||||
if (af_fmt_is_planar(mpa->format)) {
|
||||
mpa->spf = 1;
|
||||
mpa->num_planes = mpa->nch;
|
||||
mpa->sstride = mpa->bps;
|
||||
} else {
|
||||
mpa->spf = mpa->nch;
|
||||
mpa->num_planes = 1;
|
||||
mpa->sstride = mpa->bps * mpa->nch;
|
||||
}
|
||||
}
|
||||
|
||||
void mp_audio_set_format(struct mp_audio *mpa, int format)
|
||||
{
|
||||
mpa->format = format;
|
||||
update_redundant_info(mpa);
|
||||
mpa->bps = af_fmt2bits(format) / 8;
|
||||
}
|
||||
|
||||
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
|
||||
{
|
||||
mp_chmap_from_channels(&mpa->channels, num_channels);
|
||||
update_redundant_info(mpa);
|
||||
struct mp_chmap map;
|
||||
mp_chmap_from_channels(&map, num_channels);
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
// Use old MPlayer/ALSA channel layout.
|
||||
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
|
||||
{
|
||||
mp_chmap_from_channels_alsa(&mpa->channels, num_channels);
|
||||
update_redundant_info(mpa);
|
||||
struct mp_chmap map;
|
||||
mp_chmap_from_channels_alsa(&map, num_channels);
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
|
||||
{
|
||||
assert(mp_chmap_is_empty(chmap) || mp_chmap_is_valid(chmap));
|
||||
mpa->channels = *chmap;
|
||||
update_redundant_info(mpa);
|
||||
mpa->nch = mpa->channels.num;
|
||||
}
|
||||
|
||||
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src)
|
||||
{
|
||||
dst->format = src->format;
|
||||
dst->channels = src->channels;
|
||||
mp_audio_set_format(dst, src->format);
|
||||
mp_audio_set_channels(dst, &src->channels);
|
||||
dst->rate = src->rate;
|
||||
update_redundant_info(dst);
|
||||
}
|
||||
|
||||
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
|
||||
@@ -85,288 +61,16 @@ bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
|
||||
mp_chmap_equals(&a->channels, &b->channels);
|
||||
}
|
||||
|
||||
bool mp_audio_config_valid(const struct mp_audio *mpa)
|
||||
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format)
|
||||
{
|
||||
return mp_chmap_is_valid(&mpa->channels) && af_fmt_is_valid(mpa->format)
|
||||
&& mpa->rate >= 1 && mpa->rate < 10000000;
|
||||
char *chstr = mp_chmap_to_str(chmap);
|
||||
char *res = talloc_asprintf(NULL, "%dHz %s %dch %s", srate, chstr,
|
||||
chmap->num, af_fmt2str_short(format));
|
||||
talloc_free(chstr);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa)
|
||||
char *mp_audio_config_to_str(struct mp_audio *mpa)
|
||||
{
|
||||
snprintf(buf, buf_sz, "%dHz %s %dch %s", mpa->rate,
|
||||
mp_chmap_to_str(&mpa->channels), mpa->channels.num,
|
||||
af_fmt_to_str(mpa->format));
|
||||
return buf;
|
||||
}
|
||||
|
||||
void mp_audio_force_interleaved_format(struct mp_audio *mpa)
|
||||
{
|
||||
if (af_fmt_is_planar(mpa->format))
|
||||
mp_audio_set_format(mpa, af_fmt_from_planar(mpa->format));
|
||||
}
|
||||
|
||||
// Return used size of a plane. (The size is the same for all planes.)
|
||||
int mp_audio_psize(struct mp_audio *mpa)
|
||||
{
|
||||
return mpa->samples * mpa->sstride;
|
||||
}
|
||||
|
||||
void mp_audio_set_null_data(struct mp_audio *mpa)
|
||||
{
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
|
||||
mpa->planes[n] = NULL;
|
||||
mpa->allocated[n] = NULL;
|
||||
}
|
||||
mpa->samples = 0;
|
||||
}
|
||||
|
||||
static int get_plane_size(const struct mp_audio *mpa, int samples)
|
||||
{
|
||||
if (samples < 0 || !mp_audio_config_valid(mpa))
|
||||
return -1;
|
||||
if (samples >= INT_MAX / mpa->sstride)
|
||||
return -1;
|
||||
return MPMAX(samples * mpa->sstride, 1);
|
||||
}
|
||||
|
||||
static void mp_audio_destructor(void *ptr)
|
||||
{
|
||||
struct mp_audio *mpa = ptr;
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
av_buffer_unref(&mpa->allocated[n]);
|
||||
}
|
||||
|
||||
/* Reallocate the data stored in mpa->planes[n] so that enough samples are
|
||||
* available on every plane. The previous data is kept (for the smallest
|
||||
* common number of samples before/after resize).
|
||||
*
|
||||
* mpa->samples is not set or used.
|
||||
*
|
||||
* This function is flexible enough to handle format and channel layout
|
||||
* changes. In these cases, all planes are reallocated as needed. Unused
|
||||
* planes are freed.
|
||||
*
|
||||
* mp_audio_realloc(mpa, 0) will still yield non-NULL for mpa->data[n].
|
||||
*
|
||||
* Allocated data is implicitly freed on talloc_free(mpa).
|
||||
*/
|
||||
void mp_audio_realloc(struct mp_audio *mpa, int samples)
|
||||
{
|
||||
int size = get_plane_size(mpa, samples);
|
||||
if (size < 0)
|
||||
abort(); // oom or invalid parameters
|
||||
for (int n = 0; n < mpa->num_planes; n++) {
|
||||
if (!mpa->allocated[n] || size != mpa->allocated[n]->size) {
|
||||
if (av_buffer_realloc(&mpa->allocated[n], size) < 0)
|
||||
abort(); // OOM
|
||||
mpa->planes[n] = mpa->allocated[n]->data;
|
||||
}
|
||||
}
|
||||
for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) {
|
||||
av_buffer_unref(&mpa->allocated[n]);
|
||||
mpa->planes[n] = NULL;
|
||||
}
|
||||
talloc_set_destructor(mpa, mp_audio_destructor);
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
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
|
||||
* size isn't on sample boundaries (e.g. after format changes), the returned
|
||||
* sample number is a rounded down value.
|
||||
*
|
||||
* Note that this only works in situations where mp_audio_realloc() also works!
|
||||
*/
|
||||
int mp_audio_get_allocated_size(struct mp_audio *mpa)
|
||||
{
|
||||
int size = 0;
|
||||
for (int n = 0; n < mpa->num_planes; n++) {
|
||||
int s = mpa->allocated[n] ? mpa->allocated[n]->size / mpa->sstride : 0;
|
||||
size = n == 0 ? s : MPMIN(size, s);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// Clear the samples [start, start + length) with silence.
|
||||
void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length)
|
||||
{
|
||||
assert(start >= 0 && length >= 0 && start + length <= mpa->samples);
|
||||
int offset = start * mpa->sstride;
|
||||
int size = length * mpa->sstride;
|
||||
for (int n = 0; n < mpa->num_planes; n++) {
|
||||
if (n > 0 && mpa->planes[n] == mpa->planes[0])
|
||||
continue; // silly optimization for special cases
|
||||
af_fill_silence((char *)mpa->planes[n] + offset, size, mpa->format);
|
||||
}
|
||||
}
|
||||
|
||||
// All integer parameters are in samples.
|
||||
// dst and src can overlap.
|
||||
void mp_audio_copy(struct mp_audio *dst, int dst_offset,
|
||||
struct mp_audio *src, int src_offset, int length)
|
||||
{
|
||||
assert(mp_audio_config_equals(dst, src));
|
||||
assert(length >= 0);
|
||||
assert(dst_offset >= 0 && dst_offset + length <= dst->samples);
|
||||
assert(src_offset >= 0 && src_offset + length <= src->samples);
|
||||
|
||||
for (int n = 0; n < dst->num_planes; n++) {
|
||||
memmove((char *)dst->planes[n] + dst_offset * dst->sstride,
|
||||
(char *)src->planes[n] + src_offset * src->sstride,
|
||||
length * dst->sstride);
|
||||
}
|
||||
}
|
||||
|
||||
// Set data to the audio after the given number of samples (i.e. slice it).
|
||||
void mp_audio_skip_samples(struct mp_audio *data, int samples)
|
||||
{
|
||||
assert(samples >= 0 && samples <= data->samples);
|
||||
|
||||
for (int n = 0; n < data->num_planes; n++)
|
||||
data->planes[n] = (uint8_t *)data->planes[n] + samples * data->sstride;
|
||||
|
||||
data->samples -= samples;
|
||||
}
|
||||
|
||||
// Make sure the frame owns the audio data, and if not, copy the data.
|
||||
// Return negative value on failure (which means it can't be made writeable).
|
||||
// Non-refcounted frames are always considered writeable.
|
||||
int mp_audio_make_writeable(struct mp_audio *data)
|
||||
{
|
||||
bool ok = true;
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
|
||||
if (data->allocated[n])
|
||||
ok &= av_buffer_is_writable(data->allocated[n]);
|
||||
}
|
||||
if (!ok) {
|
||||
struct mp_audio *new = talloc(NULL, struct mp_audio);
|
||||
*new = *data;
|
||||
mp_audio_set_null_data(new); // use format only
|
||||
mp_audio_realloc(new, data->samples);
|
||||
new->samples = data->samples;
|
||||
mp_audio_copy(new, 0, data, 0, data->samples);
|
||||
// "Transfer" the reference.
|
||||
mp_audio_destructor(data);
|
||||
*data = *new;
|
||||
talloc_set_destructor(new, NULL);
|
||||
talloc_free(new);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe)
|
||||
{
|
||||
AVFrame *tmp = NULL;
|
||||
struct mp_audio *new = talloc_zero(NULL, struct mp_audio);
|
||||
talloc_set_destructor(new, mp_audio_destructor);
|
||||
|
||||
mp_audio_set_format(new, af_from_avformat(avframe->format));
|
||||
|
||||
struct mp_chmap lavc_chmap;
|
||||
mp_chmap_from_lavc(&lavc_chmap, avframe->channel_layout);
|
||||
|
||||
#if LIBAVUTIL_VERSION_MICRO >= 100
|
||||
// FFmpeg being special again
|
||||
if (lavc_chmap.num != avframe->channels)
|
||||
mp_chmap_from_channels(&lavc_chmap, avframe->channels);
|
||||
#endif
|
||||
|
||||
new->rate = avframe->sample_rate;
|
||||
|
||||
mp_audio_set_channels(new, &lavc_chmap);
|
||||
|
||||
// Force refcounted frame.
|
||||
if (!avframe->buf[0]) {
|
||||
tmp = av_frame_alloc();
|
||||
if (!tmp)
|
||||
goto fail;
|
||||
if (av_frame_ref(tmp, avframe) < 0)
|
||||
goto fail;
|
||||
avframe = tmp;
|
||||
}
|
||||
|
||||
// If we can't handle the format (e.g. too many channels), bail out.
|
||||
if (!mp_audio_config_valid(new) || avframe->nb_extended_buf)
|
||||
goto fail;
|
||||
|
||||
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++) {
|
||||
if (!avframe->buf[n])
|
||||
break;
|
||||
if (n >= MP_NUM_CHANNELS)
|
||||
goto fail;
|
||||
new->allocated[n] = av_buffer_ref(avframe->buf[n]);
|
||||
if (!new->allocated[n])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (int n = 0; n < new->num_planes; n++)
|
||||
new->planes[n] = avframe->data[n];
|
||||
new->samples = avframe->nb_samples;
|
||||
|
||||
return new;
|
||||
|
||||
fail:
|
||||
talloc_free(new);
|
||||
av_frame_free(&tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mp_audio_pool {
|
||||
AVBufferPool *avpool;
|
||||
int element_size;
|
||||
};
|
||||
|
||||
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent)
|
||||
{
|
||||
return talloc_zero(ta_parent, struct mp_audio_pool);
|
||||
}
|
||||
|
||||
static void mp_audio_pool_destructor(void *p)
|
||||
{
|
||||
struct mp_audio_pool *pool = p;
|
||||
av_buffer_pool_uninit(&pool->avpool);
|
||||
}
|
||||
|
||||
// Allocate data using the given format and number of samples.
|
||||
// Returns NULL on error.
|
||||
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
|
||||
const struct mp_audio *fmt, int samples)
|
||||
{
|
||||
int size = get_plane_size(fmt, samples);
|
||||
if (size < 0)
|
||||
return NULL;
|
||||
if (!pool->avpool || size > pool->element_size) {
|
||||
size_t alloc = ta_calc_prealloc_elems(size);
|
||||
if (alloc >= INT_MAX)
|
||||
return NULL;
|
||||
av_buffer_pool_uninit(&pool->avpool);
|
||||
pool->avpool = av_buffer_pool_init(alloc, NULL);
|
||||
if (!pool->avpool)
|
||||
return NULL;
|
||||
talloc_set_destructor(pool, mp_audio_pool_destructor);
|
||||
}
|
||||
struct mp_audio *new = talloc_ptrtype(NULL, new);
|
||||
talloc_set_destructor(new, mp_audio_destructor);
|
||||
*new = *fmt;
|
||||
mp_audio_set_null_data(new);
|
||||
new->samples = samples;
|
||||
for (int n = 0; n < new->num_planes; n++) {
|
||||
new->allocated[n] = av_buffer_pool_get(pool->avpool);
|
||||
if (!new->allocated[n]) {
|
||||
talloc_free(new);
|
||||
return NULL;
|
||||
}
|
||||
new->planes[n] = new->allocated[n]->data;
|
||||
}
|
||||
return new;
|
||||
return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format);
|
||||
}
|
||||
+7
-41
@@ -23,22 +23,14 @@
|
||||
|
||||
// Audio data chunk
|
||||
struct mp_audio {
|
||||
int samples; // number of samples in data (per channel)
|
||||
void *planes[MP_NUM_CHANNELS]; // data buffer (one per plane)
|
||||
int rate; // sample rate
|
||||
void *audio; // data buffer
|
||||
int len; // buffer length (in bytes)
|
||||
int rate; // sample rate
|
||||
struct mp_chmap channels; // channel layout, use mp_audio_set_*() to set
|
||||
int format; // format (AF_FORMAT_...), use mp_audio_set_format() to set
|
||||
// Redundant fields, for convenience
|
||||
int sstride; // distance between 2 samples in bytes on a plane
|
||||
// interleaved: bps * nch
|
||||
// planar: bps
|
||||
int nch; // number of channels (redundant with chmap)
|
||||
int spf; // sub-samples per sample on each plane
|
||||
int num_planes; // number of planes
|
||||
int bps; // size of sub-samples (af_fmt2bps(format))
|
||||
|
||||
// private
|
||||
struct AVBufferRef *allocated[MP_NUM_CHANNELS];
|
||||
int nch; // number of channels (redundant with chmap)
|
||||
int bps; // bytes per sample (redundant with format)
|
||||
};
|
||||
|
||||
void mp_audio_set_format(struct mp_audio *mpa, int format);
|
||||
@@ -47,34 +39,8 @@ void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels);
|
||||
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap);
|
||||
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src);
|
||||
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b);
|
||||
bool mp_audio_config_valid(const struct mp_audio *mpa);
|
||||
|
||||
char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa);
|
||||
#define mp_audio_config_to_str(m) mp_audio_config_to_str_buf((char[64]){0}, 64, (m))
|
||||
|
||||
void mp_audio_force_interleaved_format(struct mp_audio *mpa);
|
||||
|
||||
int mp_audio_psize(struct mp_audio *mpa);
|
||||
|
||||
void mp_audio_set_null_data(struct mp_audio *mpa);
|
||||
|
||||
void mp_audio_realloc(struct mp_audio *mpa, int samples);
|
||||
void mp_audio_realloc_min(struct mp_audio *mpa, int samples);
|
||||
int mp_audio_get_allocated_size(struct mp_audio *mpa);
|
||||
|
||||
void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length);
|
||||
|
||||
void mp_audio_copy(struct mp_audio *dst, int dst_offset,
|
||||
struct mp_audio *src, int src_offset, int length);
|
||||
void mp_audio_skip_samples(struct mp_audio *data, int samples);
|
||||
|
||||
int mp_audio_make_writeable(struct mp_audio *data);
|
||||
|
||||
struct AVFrame;
|
||||
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe);
|
||||
|
||||
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
|
||||
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
|
||||
const struct mp_audio *fmt, int samples);
|
||||
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format);
|
||||
char *mp_audio_config_to_str(struct mp_audio *mpa);
|
||||
|
||||
#endif
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include "audio_buffer.h"
|
||||
#include "audio.h"
|
||||
#include "format.h"
|
||||
|
||||
struct mp_audio_buffer {
|
||||
struct mp_audio *buffer;
|
||||
};
|
||||
|
||||
struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx)
|
||||
{
|
||||
struct mp_audio_buffer *ab = talloc(talloc_ctx, struct mp_audio_buffer);
|
||||
*ab = (struct mp_audio_buffer) {
|
||||
.buffer = talloc_zero(ab, struct mp_audio),
|
||||
};
|
||||
return ab;
|
||||
}
|
||||
|
||||
// Reinitialize the buffer, set a new format, drop old data.
|
||||
// The audio data in fmt is not used, only the format.
|
||||
void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt)
|
||||
{
|
||||
mp_audio_copy_config(ab->buffer, fmt);
|
||||
mp_audio_realloc(ab->buffer, 1);
|
||||
ab->buffer->samples = 0;
|
||||
}
|
||||
|
||||
void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format,
|
||||
const struct mp_chmap *channels, int srate)
|
||||
{
|
||||
struct mp_audio mpa = {0};
|
||||
mp_audio_set_format(&mpa, format);
|
||||
mp_audio_set_channels(&mpa, channels);
|
||||
mpa.rate = srate;
|
||||
mp_audio_buffer_reinit(ab, &mpa);
|
||||
}
|
||||
|
||||
void mp_audio_buffer_get_format(struct mp_audio_buffer *ab,
|
||||
struct mp_audio *out_fmt)
|
||||
{
|
||||
*out_fmt = (struct mp_audio){0};
|
||||
mp_audio_copy_config(out_fmt, ab->buffer);
|
||||
}
|
||||
|
||||
// Make the total size of the internal buffer at least this number of samples.
|
||||
void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples)
|
||||
{
|
||||
mp_audio_realloc_min(ab->buffer, samples);
|
||||
}
|
||||
|
||||
// Get number of samples that can be written without forcing a resize of the
|
||||
// internal buffer.
|
||||
int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab)
|
||||
{
|
||||
return mp_audio_get_allocated_size(ab->buffer) - ab->buffer->samples;
|
||||
}
|
||||
|
||||
// Get a pointer to the end of the buffer (where writing would append). If the
|
||||
// internal buffer is too small for the given number of samples, it's resized.
|
||||
// After writing to the buffer, mp_audio_buffer_finish_write() has to be used
|
||||
// to make the written data part of the readable buffer.
|
||||
void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int samples,
|
||||
struct mp_audio *out_buffer)
|
||||
{
|
||||
assert(samples >= 0);
|
||||
mp_audio_realloc_min(ab->buffer, ab->buffer->samples + samples);
|
||||
*out_buffer = *ab->buffer;
|
||||
out_buffer->samples = ab->buffer->samples + samples;
|
||||
mp_audio_skip_samples(out_buffer, ab->buffer->samples);
|
||||
}
|
||||
|
||||
void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples)
|
||||
{
|
||||
assert(samples >= 0 && samples <= mp_audio_buffer_get_write_available(ab));
|
||||
ab->buffer->samples += samples;
|
||||
}
|
||||
|
||||
// Append data to the end of the buffer.
|
||||
// If the buffer is not large enough, it is transparently resized.
|
||||
// For now always copies the data.
|
||||
void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa)
|
||||
{
|
||||
int offset = ab->buffer->samples;
|
||||
ab->buffer->samples += mpa->samples;
|
||||
mp_audio_realloc_min(ab->buffer, ab->buffer->samples);
|
||||
mp_audio_copy(ab->buffer, offset, mpa, 0, mpa->samples);
|
||||
}
|
||||
|
||||
// Prepend silence to the start of the buffer.
|
||||
void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples)
|
||||
{
|
||||
assert(samples >= 0);
|
||||
int oldlen = ab->buffer->samples;
|
||||
ab->buffer->samples += samples;
|
||||
mp_audio_realloc_min(ab->buffer, ab->buffer->samples);
|
||||
mp_audio_copy(ab->buffer, samples, ab->buffer, 0, oldlen);
|
||||
mp_audio_fill_silence(ab->buffer, 0, samples);
|
||||
}
|
||||
|
||||
// Get the start of the current readable buffer.
|
||||
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa)
|
||||
{
|
||||
*out_mpa = *ab->buffer;
|
||||
}
|
||||
|
||||
// Skip leading samples. (Used with mp_audio_buffer_peek() to read data.)
|
||||
void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples)
|
||||
{
|
||||
assert(samples >= 0 && samples <= ab->buffer->samples);
|
||||
mp_audio_copy(ab->buffer, 0, ab->buffer, samples,
|
||||
ab->buffer->samples - samples);
|
||||
ab->buffer->samples -= samples;
|
||||
}
|
||||
|
||||
void mp_audio_buffer_clear(struct mp_audio_buffer *ab)
|
||||
{
|
||||
ab->buffer->samples = 0;
|
||||
}
|
||||
|
||||
// Return number of buffered audio samples
|
||||
int mp_audio_buffer_samples(struct mp_audio_buffer *ab)
|
||||
{
|
||||
return ab->buffer->samples;
|
||||
}
|
||||
|
||||
// Return amount of buffered audio in seconds.
|
||||
double mp_audio_buffer_seconds(struct mp_audio_buffer *ab)
|
||||
{
|
||||
return ab->buffer->samples / (double)ab->buffer->rate;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MP_AUDIO_BUFFER_H
|
||||
#define MP_AUDIO_BUFFER_H
|
||||
|
||||
struct mp_audio_buffer;
|
||||
struct mp_audio;
|
||||
struct mp_chmap;
|
||||
|
||||
struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx);
|
||||
void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt);
|
||||
void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format,
|
||||
const struct mp_chmap *channels, int srate);
|
||||
void mp_audio_buffer_get_format(struct mp_audio_buffer *ab,
|
||||
struct mp_audio *out_fmt);
|
||||
void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples);
|
||||
int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab);
|
||||
void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int minsamples,
|
||||
struct mp_audio *out_buffer);
|
||||
void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa);
|
||||
void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa);
|
||||
void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_clear(struct mp_audio_buffer *ab);
|
||||
int mp_audio_buffer_samples(struct mp_audio_buffer *ab);
|
||||
double mp_audio_buffer_seconds(struct mp_audio_buffer *ab);
|
||||
|
||||
#endif
|
||||
+24
-27
@@ -18,13 +18,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/msg.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
#include "chmap.h"
|
||||
|
||||
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
|
||||
// Use of these names is hard-coded in some places (e.g. ao_alsa.c)
|
||||
static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
|
||||
static const char *speaker_names[MP_SPEAKER_ID_COUNT][2] = {
|
||||
[MP_SPEAKER_ID_FL] = {"fl", "front left"},
|
||||
[MP_SPEAKER_ID_FR] = {"fr", "front right"},
|
||||
[MP_SPEAKER_ID_FC] = {"fc", "front center"},
|
||||
@@ -54,7 +53,7 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
|
||||
|
||||
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
|
||||
// Channel order corresponds to lavc/waveex, except for the alsa entries.
|
||||
static const char *const std_layout_names[][2] = {
|
||||
static const char *std_layout_names[][2] = {
|
||||
{"empty", ""}, // not in lavc
|
||||
{"mono", "fc"},
|
||||
{"stereo", "fl-fr"},
|
||||
@@ -87,7 +86,6 @@ static const char *const std_layout_names[][2] = {
|
||||
{"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
|
||||
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
|
||||
{"downmix", "dl-dr"},
|
||||
{"auto", ""}, // not in lavc
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -107,7 +105,7 @@ static const struct mp_chmap default_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
// channels. 3 and 7 channels were likely undefined (no ALSA support).
|
||||
// I'm not sure about the 4 channel case: ALSA uses "quad", while the ffmpeg
|
||||
// default layout is "4.0".
|
||||
static const char *const mplayer_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
static const char *mplayer_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
[1] = "mono",
|
||||
[2] = "stereo",
|
||||
[4] = "quad",
|
||||
@@ -377,35 +375,34 @@ void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
|
||||
// order, return that, e.g. "3.0" instead of "fl-fr-fc".
|
||||
// Unassigned but valid speakers get names like "sp28".
|
||||
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src)
|
||||
char *mp_chmap_to_str(const struct mp_chmap *src)
|
||||
{
|
||||
buf[0] = '\0';
|
||||
char *res = talloc_strdup(NULL, "");
|
||||
|
||||
if (mp_chmap_is_unknown(src)) {
|
||||
snprintf(buf, buf_size, "unknown%d", src->num);
|
||||
return buf;
|
||||
}
|
||||
if (mp_chmap_is_unknown(src))
|
||||
return talloc_asprintf_append_buffer(res, "unknown%d", src->num);
|
||||
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int sp = src->speaker[n];
|
||||
const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL;
|
||||
char sp_buf[10];
|
||||
char buf[10];
|
||||
if (!s) {
|
||||
snprintf(sp_buf, sizeof(sp_buf), "sp%d", sp);
|
||||
s = sp_buf;
|
||||
snprintf(buf, sizeof(buf), "sp%d", sp);
|
||||
s = buf;
|
||||
}
|
||||
mp_snprintf_cat(buf, buf_size, "%s%s", n > 0 ? "-" : "", s);
|
||||
res = talloc_asprintf_append_buffer(res, "%s%s", n > 0 ? "-" : "", s);
|
||||
}
|
||||
|
||||
// To standard layout name
|
||||
for (int n = 0; std_layout_names[n][0]; n++) {
|
||||
if (strcmp(buf, std_layout_names[n][1]) == 0) {
|
||||
snprintf(buf, buf_size, "%s", std_layout_names[n][0]);
|
||||
if (res && strcmp(res, std_layout_names[n][1]) == 0) {
|
||||
talloc_free(res);
|
||||
res = talloc_strdup(NULL, std_layout_names[n][0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
// If src can be parsed as channel map (as produced by mp_chmap_to_str()),
|
||||
@@ -474,19 +471,19 @@ bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_chmap_print_help(struct mp_log *log)
|
||||
void mp_chmap_print_help(int msgt, int msgl)
|
||||
{
|
||||
mp_info(log, "Speakers:\n");
|
||||
mp_msg(msgt, msgl, "Speakers:\n");
|
||||
for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
|
||||
if (speaker_names[n][0])
|
||||
mp_info(log, " %-16s (%s)\n",
|
||||
speaker_names[n][0], speaker_names[n][1]);
|
||||
mp_msg(msgt, msgl, " %-16s (%s)\n",
|
||||
speaker_names[n][0], speaker_names[n][1]);
|
||||
}
|
||||
mp_info(log, "Standard layouts:\n");
|
||||
mp_msg(msgt, msgl, "Standard layouts:\n");
|
||||
for (int n = 0; std_layout_names[n][0]; n++) {
|
||||
mp_info(log, " %-16s (%s)\n",
|
||||
std_layout_names[n][0], std_layout_names[n][1]);
|
||||
mp_msg(msgt, msgl, " %-16s (%s)\n",
|
||||
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 + 1);
|
||||
mp_msg(msgt, msgl, " unknown%d\n", n);
|
||||
}
|
||||
+5
-10
@@ -20,14 +20,14 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "misc/bstr.h"
|
||||
#include "mpvcore/bstr.h"
|
||||
|
||||
#define MP_NUM_CHANNELS 8
|
||||
|
||||
// Speaker a channel can be assigned to.
|
||||
// This corresponds to WAVEFORMATEXTENSIBLE channel mask bit indexes.
|
||||
// E.g. channel_mask = (1 << MP_SPEAKER_ID_FL) | ...
|
||||
enum mp_speaker_id {
|
||||
enum {
|
||||
// Official WAVEFORMATEXTENSIBLE (shortened names)
|
||||
MP_SPEAKER_ID_FL = 0, // FRONT_LEFT
|
||||
MP_SPEAKER_ID_FR, // FRONT_RIGHT
|
||||
@@ -61,8 +61,7 @@ enum mp_speaker_id {
|
||||
MP_SPEAKER_ID_UNKNOWN0 = 64,
|
||||
MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
|
||||
|
||||
// Including the unassigned IDs in between. This is not a valid ID anymore,
|
||||
// but is still within uint8_t.
|
||||
// Including the unassigned IDs in between. This is not a valid ID anymore.
|
||||
MP_SPEAKER_ID_COUNT,
|
||||
};
|
||||
|
||||
@@ -120,13 +119,9 @@ void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
|
||||
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
const struct mp_chmap *to);
|
||||
|
||||
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
|
||||
#define mp_chmap_to_str(m) mp_chmap_to_str_buf((char[64]){0}, 64, (m))
|
||||
|
||||
char *mp_chmap_to_str(const struct mp_chmap *src);
|
||||
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
|
||||
|
||||
struct mp_log;
|
||||
void mp_chmap_print_help(struct mp_log *log);
|
||||
void mp_chmap_print_help(int msgt, int msgl);
|
||||
|
||||
// Use these to avoid chaos in case lavc's definition should diverge from MS.
|
||||
#define mp_chmap_to_waveext mp_chmap_to_lavc
|
||||
|
||||
+21
-33
@@ -18,21 +18,21 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "chmap_sel.h"
|
||||
|
||||
static const struct mp_chmap speaker_replacements[][2] = {
|
||||
// 5.1 <-> 5.1 (side)
|
||||
{ MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR) },
|
||||
// 7.1 <-> 7.1 (rear ext)
|
||||
{ MP_CHMAP2(SL, SR), MP_CHMAP2(SDL, SDR) },
|
||||
// 5.1 and 5.1(side) are practically the same. It doesn't make much sense to
|
||||
// reject either of them.
|
||||
static const int replaceable_speakers[][2] = {
|
||||
{MP_SPEAKER_ID_SL, MP_SPEAKER_ID_BL},
|
||||
{MP_SPEAKER_ID_SR, MP_SPEAKER_ID_BR},
|
||||
{-1},
|
||||
};
|
||||
|
||||
// Try to replace speakers from the left of the list with the ones on the
|
||||
// right, or the other way around.
|
||||
static bool replace_speakers(struct mp_chmap *map, struct mp_chmap list[2])
|
||||
// list[] contains a list of speaker pairs, with each pair indicating how
|
||||
// a speaker can be swapped for another speaker. Try to replace speakers from
|
||||
// the left of the list with the ones on the right, or the other way around.
|
||||
static bool replace_speakers(struct mp_chmap *map, const int list[][2])
|
||||
{
|
||||
assert(list[0].num == list[1].num);
|
||||
if (!mp_chmap_is_valid(map))
|
||||
return false;
|
||||
for (int dir = 0; dir < 2; dir++) {
|
||||
@@ -41,9 +41,9 @@ static bool replace_speakers(struct mp_chmap *map, struct mp_chmap list[2])
|
||||
bool replaced = false;
|
||||
struct mp_chmap t = *map;
|
||||
for (int n = 0; n < t.num; n++) {
|
||||
for (int i = 0; i < list[0].num; i++) {
|
||||
if (t.speaker[n] == list[from].speaker[i]) {
|
||||
t.speaker[n] = list[to].speaker[i];
|
||||
for (int i = 0; list[i][0] != -1; i++) {
|
||||
if (t.speaker[n] == list[i][from]) {
|
||||
t.speaker[n] = list[i][to];
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
@@ -81,21 +81,14 @@ void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
|
||||
}
|
||||
}
|
||||
|
||||
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
// Add a channel map that should be allowed.
|
||||
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
|
||||
{
|
||||
if (!mp_chmap_is_valid(map))
|
||||
return;
|
||||
if (!s->chmaps)
|
||||
s->chmaps = s->chmaps_storage;
|
||||
if (s->num_chmaps == MP_ARRAY_SIZE(s->chmaps_storage)) {
|
||||
if (!s->tmp)
|
||||
return;
|
||||
s->chmaps = talloc_memdup(s->tmp, s->chmaps, sizeof(s->chmaps_storage));
|
||||
}
|
||||
if (s->chmaps != s->chmaps_storage)
|
||||
MP_TARRAY_GROW(s->tmp, s->chmaps, s->num_chmaps);
|
||||
s->chmaps[s->num_chmaps++] = *map;
|
||||
assert(s->num_chmaps < ARRAY_LEN(s->chmaps));
|
||||
if (mp_chmap_is_valid(map))
|
||||
s->chmaps[s->num_chmaps++] = *map;
|
||||
}
|
||||
|
||||
// Allow all waveext formats in default order.
|
||||
@@ -175,14 +168,9 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(speaker_replacements); i++) {
|
||||
struct mp_chmap t = *map;
|
||||
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
|
||||
if (replace_speakers(&t, r) && test_layout(s, &t)) {
|
||||
*map = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 5.1 <-> 5.1(side)
|
||||
if (replace_speakers(map, replaceable_speakers) && test_layout(s, map))
|
||||
return true;
|
||||
// Fallback to mono/stereo as last resort
|
||||
if (map->num == 1) {
|
||||
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
|
||||
|
||||
+1
-5
@@ -26,12 +26,8 @@ struct mp_chmap_sel {
|
||||
// should be considered opaque
|
||||
bool allow_any, allow_waveext;
|
||||
bool speakers[MP_SPEAKER_ID_COUNT];
|
||||
struct mp_chmap *chmaps;
|
||||
struct mp_chmap chmaps[20];
|
||||
int num_chmaps;
|
||||
|
||||
struct mp_chmap chmaps_storage[20];
|
||||
|
||||
void *tmp; // set to any talloc context to allow more chmaps entries
|
||||
};
|
||||
|
||||
void mp_chmap_sel_add_any(struct mp_chmap_sel *s);
|
||||
|
||||
+8
-8
@@ -19,13 +19,11 @@
|
||||
#ifndef MPLAYER_AD_H
|
||||
#define MPLAYER_AD_H
|
||||
|
||||
#include "common/codecs.h"
|
||||
#include "mpvcore/codecs.h"
|
||||
#include "demux/stheader.h"
|
||||
#include "demux/demux.h"
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
#include "dec_audio.h"
|
||||
|
||||
struct mp_decoder_list;
|
||||
|
||||
@@ -33,14 +31,16 @@ struct mp_decoder_list;
|
||||
struct ad_functions {
|
||||
const char *name;
|
||||
void (*add_decoders)(struct mp_decoder_list *list);
|
||||
int (*init)(struct dec_audio *da, const char *decoder);
|
||||
void (*uninit)(struct dec_audio *da);
|
||||
int (*control)(struct dec_audio *da, int cmd, void *arg);
|
||||
int (*decode_packet)(struct dec_audio *da, struct mp_audio **out);
|
||||
int (*preinit)(sh_audio_t *sh);
|
||||
int (*init)(sh_audio_t *sh, const char *decoder);
|
||||
void (*uninit)(sh_audio_t *sh);
|
||||
int (*control)(sh_audio_t *sh, int cmd, void *arg);
|
||||
int (*decode_audio)(sh_audio_t *sh, unsigned char *buffer, int minlen,
|
||||
int maxlen);
|
||||
};
|
||||
|
||||
enum ad_ctrl {
|
||||
ADCTRL_RESET = 1, // flush and reset state, e.g. after seeking
|
||||
ADCTRL_RESYNC_STREAM = 1, // resync, called after seeking
|
||||
};
|
||||
|
||||
#endif /* MPLAYER_AD_H */
|
||||
+344
-155
@@ -25,83 +25,220 @@
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "common/av_common.h"
|
||||
#include "common/codecs.h"
|
||||
#include "common/msg.h"
|
||||
#include "options/options.h"
|
||||
#include "mpvcore/av_common.h"
|
||||
#include "mpvcore/codecs.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
#include "mpvcore/options.h"
|
||||
#include "mpvcore/av_opts.h"
|
||||
|
||||
#include "ad.h"
|
||||
#include "audio/reorder_ch.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
#include "compat/mpbswap.h"
|
||||
#include "compat/libav.h"
|
||||
|
||||
struct priv {
|
||||
AVCodecContext *avctx;
|
||||
AVFrame *avframe;
|
||||
struct mp_audio frame;
|
||||
uint8_t *output;
|
||||
uint8_t *output_packed; // used by deplanarize to store packed audio samples
|
||||
int output_left;
|
||||
int unitsize;
|
||||
bool force_channel_map;
|
||||
struct demux_packet *packet;
|
||||
uint32_t skip_samples;
|
||||
};
|
||||
|
||||
static void uninit(struct dec_audio *da);
|
||||
static void uninit(sh_audio_t *sh);
|
||||
static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen);
|
||||
|
||||
#define OPT_BASE_STRUCT struct ad_lavc_params
|
||||
struct ad_lavc_params {
|
||||
float ac3drc;
|
||||
int downmix;
|
||||
int threads;
|
||||
char **avopts;
|
||||
#define OPT_BASE_STRUCT struct MPOpts
|
||||
|
||||
const m_option_t ad_lavc_decode_opts_conf[] = {
|
||||
OPT_FLOATRANGE("ac3drc", ad_lavc_param.ac3drc, 0, 0, 2),
|
||||
OPT_FLAG("downmix", ad_lavc_param.downmix, 0),
|
||||
OPT_STRING("o", ad_lavc_param.avopt, 0),
|
||||
{0}
|
||||
};
|
||||
|
||||
const struct m_sub_options ad_lavc_conf = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 2),
|
||||
OPT_FLAG("downmix", downmix, 0),
|
||||
OPT_INTRANGE("threads", threads, 0, 1, 16),
|
||||
OPT_KEYVALUELIST("o", avopts, 0),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct ad_lavc_params),
|
||||
.defaults = &(const struct ad_lavc_params){
|
||||
.ac3drc = 1.,
|
||||
.downmix = 1,
|
||||
.threads = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int init(struct dec_audio *da, const char *decoder)
|
||||
struct pcm_map
|
||||
{
|
||||
struct MPOpts *mpopts = da->opts;
|
||||
struct ad_lavc_params *opts = mpopts->ad_lavc_params;
|
||||
int tag;
|
||||
const char *codecs[5]; // {any, 1byte, 2bytes, 3bytes, 4bytes}
|
||||
};
|
||||
|
||||
// NOTE: some of these are needed to make rawaudio with demux_mkv and others
|
||||
// work. ffmpeg does similar mapping internally, not part of the public
|
||||
// API. Some of these might be dead leftovers for demux_mov support.
|
||||
static const struct pcm_map tag_map[] = {
|
||||
// Microsoft PCM
|
||||
{0x0, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
{0x1, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
// MS PCM, Extended
|
||||
{0xfffe, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
// IEEE float
|
||||
{0x3, {"pcm_f32le"}},
|
||||
// 'raw '
|
||||
{0x20776172, {"pcm_s16be", [1] = "pcm_u8"}},
|
||||
// 'twos'/'sowt'
|
||||
{0x736F7774, {"pcm_s16be", [1] = "pcm_s8"}},
|
||||
{0x74776F73, {"pcm_s16be", [1] = "pcm_s8"}},
|
||||
// 'fl32'/'FL32'
|
||||
{0x32336c66, {"pcm_f32be"}},
|
||||
{0x32334C46, {"pcm_f32be"}},
|
||||
// '23lf'/'lpcm'
|
||||
{0x666c3332, {"pcm_f32le"}},
|
||||
{0x6D63706C, {"pcm_f32le"}},
|
||||
// 'in24', bigendian int24
|
||||
{0x34326e69, {"pcm_s24be"}},
|
||||
// '42ni', little endian int24, MPlayer internal fourCC
|
||||
{0x696e3234, {"pcm_s24le"}},
|
||||
// 'in32', bigendian int32
|
||||
{0x32336e69, {"pcm_s32be"}},
|
||||
// '23ni', little endian int32, MPlayer internal fourCC
|
||||
{0x696e3332, {"pcm_s32le"}},
|
||||
{-1},
|
||||
};
|
||||
|
||||
// For demux_rawaudio.c; needed because ffmpeg doesn't have these sample
|
||||
// formats natively.
|
||||
static const struct pcm_map af_map[] = {
|
||||
{AF_FORMAT_U8, {"pcm_u8"}},
|
||||
{AF_FORMAT_S8, {"pcm_u8"}},
|
||||
{AF_FORMAT_U16_LE, {"pcm_u16le"}},
|
||||
{AF_FORMAT_U16_BE, {"pcm_u16be"}},
|
||||
{AF_FORMAT_S16_LE, {"pcm_s16le"}},
|
||||
{AF_FORMAT_S16_BE, {"pcm_s16be"}},
|
||||
{AF_FORMAT_U24_LE, {"pcm_u24le"}},
|
||||
{AF_FORMAT_U24_BE, {"pcm_u24be"}},
|
||||
{AF_FORMAT_S24_LE, {"pcm_s24le"}},
|
||||
{AF_FORMAT_S24_BE, {"pcm_s24be"}},
|
||||
{AF_FORMAT_U32_LE, {"pcm_u32le"}},
|
||||
{AF_FORMAT_U32_BE, {"pcm_u32be"}},
|
||||
{AF_FORMAT_S32_LE, {"pcm_s32le"}},
|
||||
{AF_FORMAT_S32_BE, {"pcm_s32be"}},
|
||||
{AF_FORMAT_FLOAT_LE, {"pcm_f32le"}},
|
||||
{AF_FORMAT_FLOAT_BE, {"pcm_f32be"}},
|
||||
{-1},
|
||||
};
|
||||
|
||||
static const char *find_pcm_decoder(const struct pcm_map *map, int format,
|
||||
int bits_per_sample)
|
||||
{
|
||||
int bytes = (bits_per_sample + 7) / 8;
|
||||
for (int n = 0; map[n].tag != -1; n++) {
|
||||
const struct pcm_map *entry = &map[n];
|
||||
if (entry->tag == format) {
|
||||
const char *dec = NULL;
|
||||
if (bytes >= 1 && bytes <= 4)
|
||||
dec = entry->codecs[bytes];
|
||||
if (!dec)
|
||||
dec = entry->codecs[0];
|
||||
if (dec)
|
||||
return dec;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int preinit(sh_audio_t *sh)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prefer playing audio with the samplerate given in container data
|
||||
* if available, but take number the number of channels and sample format
|
||||
* from the codec, since if the codec isn't using the correct values for
|
||||
* those everything breaks anyway.
|
||||
*/
|
||||
static int setup_format(sh_audio_t *sh_audio,
|
||||
const AVCodecContext *lavc_context)
|
||||
{
|
||||
struct priv *priv = sh_audio->context;
|
||||
int sample_format =
|
||||
af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
|
||||
int samplerate = lavc_context->sample_rate;
|
||||
// If not set, try container samplerate
|
||||
if (!samplerate && sh_audio->wf) {
|
||||
samplerate = sh_audio->wf->nSamplesPerSec;
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "ad_lavc: using container rate.\n");
|
||||
}
|
||||
|
||||
struct mp_chmap lavc_chmap;
|
||||
mp_chmap_from_lavc(&lavc_chmap, lavc_context->channel_layout);
|
||||
// No channel layout or layout disagrees with channel count
|
||||
if (lavc_chmap.num != lavc_context->channels)
|
||||
mp_chmap_from_channels(&lavc_chmap, lavc_context->channels);
|
||||
if (priv->force_channel_map) {
|
||||
if (lavc_chmap.num == sh_audio->channels.num)
|
||||
lavc_chmap = sh_audio->channels;
|
||||
}
|
||||
|
||||
if (!mp_chmap_equals(&lavc_chmap, &sh_audio->channels) ||
|
||||
samplerate != sh_audio->samplerate ||
|
||||
sample_format != sh_audio->sample_format) {
|
||||
sh_audio->channels = lavc_chmap;
|
||||
sh_audio->samplerate = samplerate;
|
||||
sh_audio->sample_format = sample_format;
|
||||
sh_audio->samplesize = af_fmt2bits(sh_audio->sample_format) / 8;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
|
||||
{
|
||||
avctx->channels = wf->nChannels;
|
||||
avctx->sample_rate = wf->nSamplesPerSec;
|
||||
avctx->bit_rate = wf->nAvgBytesPerSec * 8;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static int init(sh_audio_t *sh_audio, const char *decoder)
|
||||
{
|
||||
struct MPOpts *mpopts = sh_audio->opts;
|
||||
struct ad_lavc_param *opts = &mpopts->ad_lavc_param;
|
||||
AVCodecContext *lavc_context;
|
||||
AVCodec *lavc_codec;
|
||||
struct sh_stream *sh = da->header;
|
||||
struct sh_audio *sh_audio = sh->audio;
|
||||
|
||||
struct priv *ctx = talloc_zero(NULL, struct priv);
|
||||
da->priv = ctx;
|
||||
sh_audio->context = ctx;
|
||||
|
||||
ctx->force_channel_map = sh_audio->force_channels;
|
||||
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
|
||||
decoder = find_pcm_decoder(tag_map, sh_audio->format,
|
||||
sh_audio->wf->wBitsPerSample);
|
||||
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
|
||||
decoder = find_pcm_decoder(af_map, sh_audio->format, 0);
|
||||
ctx->force_channel_map = true;
|
||||
}
|
||||
|
||||
lavc_codec = avcodec_find_decoder_by_name(decoder);
|
||||
if (!lavc_codec) {
|
||||
MP_ERR(da, "Cannot find codec '%s' in libavcodec...\n", decoder);
|
||||
uninit(da);
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"Cannot find codec '%s' in libavcodec...\n", decoder);
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lavc_context = avcodec_alloc_context3(lavc_codec);
|
||||
ctx->avctx = lavc_context;
|
||||
ctx->avframe = av_frame_alloc();
|
||||
lavc_context->refcounted_frames = 1;
|
||||
ctx->avframe = avcodec_alloc_frame();
|
||||
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
lavc_context->codec_id = lavc_codec->id;
|
||||
|
||||
if (opts->downmix) {
|
||||
lavc_context->request_channels = mpopts->audio_output_channels.num;
|
||||
lavc_context->request_channel_layout =
|
||||
mp_chmap_to_lavc(&mpopts->audio_output_channels);
|
||||
}
|
||||
@@ -110,184 +247,236 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
|
||||
AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
#if HAVE_AVFRAME_SKIP_SAMPLES
|
||||
// Let decoder add AV_FRAME_DATA_SKIP_SAMPLES.
|
||||
av_opt_set(lavc_context, "flags2", "+skip_manual", AV_OPT_SEARCH_CHILDREN);
|
||||
#endif
|
||||
|
||||
mp_set_avopts(da->log, lavc_context, opts->avopts);
|
||||
|
||||
lavc_context->codec_tag = sh->format;
|
||||
lavc_context->sample_rate = sh_audio->samplerate;
|
||||
lavc_context->bit_rate = sh_audio->bitrate;
|
||||
lavc_context->block_align = sh_audio->block_align;
|
||||
lavc_context->bits_per_coded_sample = sh_audio->bits_per_coded_sample;
|
||||
lavc_context->channels = sh_audio->channels.num;
|
||||
if (!mp_chmap_is_unknown(&sh_audio->channels))
|
||||
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
|
||||
|
||||
// demux_mkv
|
||||
if (sh_audio->codecdata_len && sh_audio->codecdata &&
|
||||
!lavc_context->extradata) {
|
||||
mp_lavc_set_extradata(lavc_context, sh_audio->codecdata,
|
||||
sh_audio->codecdata_len);
|
||||
if (opts->avopt) {
|
||||
if (parse_avopts(lavc_context, opts->avopt) < 0) {
|
||||
mp_msg(MSGT_DECVIDEO, MSGL_ERR,
|
||||
"ad_lavc: setting AVOptions '%s' failed.\n", opts->avopt);
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sh->lav_headers)
|
||||
mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
|
||||
lavc_context->codec_tag = sh_audio->format;
|
||||
lavc_context->sample_rate = sh_audio->samplerate;
|
||||
lavc_context->bit_rate = sh_audio->i_bps * 8;
|
||||
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
|
||||
|
||||
mp_set_avcodec_threads(lavc_context, opts->threads);
|
||||
if (sh_audio->wf)
|
||||
set_from_wf(lavc_context, sh_audio->wf);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (sh_audio->gsh->lav_headers)
|
||||
mp_copy_lav_codec_headers(lavc_context, sh_audio->gsh->lav_headers);
|
||||
|
||||
/* open it */
|
||||
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
|
||||
MP_ERR(da, "Could not open codec.\n");
|
||||
uninit(da);
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Could not open codec.\n");
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "INFO: libavcodec \"%s\" init OK!\n",
|
||||
lavc_codec->name);
|
||||
|
||||
if (lavc_context->bit_rate != 0)
|
||||
da->bitrate = lavc_context->bit_rate;
|
||||
// Decode at least 1 byte: (to get header filled)
|
||||
for (int tries = 0;;) {
|
||||
int x = decode_audio(sh_audio, sh_audio->a_buffer, 1,
|
||||
sh_audio->a_buffer_size);
|
||||
if (x > 0) {
|
||||
sh_audio->a_buffer_len = x;
|
||||
break;
|
||||
}
|
||||
if (++tries >= 5) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"ad_lavc: initial decode failed\n");
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sh_audio->i_bps = lavc_context->bit_rate / 8;
|
||||
if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
|
||||
sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec;
|
||||
|
||||
int af_sample_fmt =
|
||||
af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
|
||||
if (af_sample_fmt == AF_FORMAT_UNKNOWN) {
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void uninit(struct dec_audio *da)
|
||||
static void uninit(sh_audio_t *sh)
|
||||
{
|
||||
struct priv *ctx = da->priv;
|
||||
struct priv *ctx = sh->context;
|
||||
if (!ctx)
|
||||
return;
|
||||
AVCodecContext *lavc_context = ctx->avctx;
|
||||
|
||||
if (lavc_context) {
|
||||
if (avcodec_close(lavc_context) < 0)
|
||||
MP_ERR(da, "Could not close codec.\n");
|
||||
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not close codec.\n");
|
||||
av_freep(&lavc_context->extradata);
|
||||
av_freep(&lavc_context);
|
||||
}
|
||||
av_frame_free(&ctx->avframe);
|
||||
avcodec_free_frame(&ctx->avframe);
|
||||
talloc_free(ctx);
|
||||
sh->context = NULL;
|
||||
}
|
||||
|
||||
static int control(struct dec_audio *da, int cmd, void *arg)
|
||||
static int control(sh_audio_t *sh, int cmd, void *arg)
|
||||
{
|
||||
struct priv *ctx = da->priv;
|
||||
struct priv *ctx = sh->context;
|
||||
switch (cmd) {
|
||||
case ADCTRL_RESET:
|
||||
case ADCTRL_RESYNC_STREAM:
|
||||
avcodec_flush_buffers(ctx->avctx);
|
||||
ctx->output_left = 0;
|
||||
talloc_free(ctx->packet);
|
||||
ctx->packet = NULL;
|
||||
ctx->skip_samples = 0;
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
static int decode_packet(struct dec_audio *da, struct mp_audio **out)
|
||||
static av_always_inline void deplanarize(struct sh_audio *sh)
|
||||
{
|
||||
struct priv *priv = da->priv;
|
||||
AVCodecContext *avctx = priv->avctx;
|
||||
struct priv *priv = sh->context;
|
||||
|
||||
uint8_t **planes = priv->avframe->extended_data;
|
||||
size_t bps = av_get_bytes_per_sample(priv->avctx->sample_fmt);
|
||||
size_t nb_samples = priv->avframe->nb_samples;
|
||||
size_t channels = priv->avctx->channels;
|
||||
size_t size = bps * nb_samples * channels;
|
||||
|
||||
if (talloc_get_size(priv->output_packed) != size)
|
||||
priv->output_packed =
|
||||
talloc_realloc_size(priv, priv->output_packed, size);
|
||||
|
||||
reorder_to_packed(priv->output_packed, planes, bps, channels, nb_samples);
|
||||
|
||||
priv->output = priv->output_packed;
|
||||
}
|
||||
|
||||
static int decode_new_packet(struct sh_audio *sh)
|
||||
{
|
||||
struct priv *priv = sh->context;
|
||||
AVCodecContext *avctx = priv->avctx;
|
||||
struct demux_packet *mpkt = priv->packet;
|
||||
if (!mpkt) {
|
||||
if (demux_read_packet_async(da->header, &mpkt) == 0)
|
||||
return AD_WAIT;
|
||||
}
|
||||
if (!mpkt)
|
||||
mpkt = demux_read_packet(sh->gsh);
|
||||
if (!mpkt)
|
||||
return -1; // error or EOF
|
||||
|
||||
priv->packet = talloc_steal(priv, mpkt);
|
||||
|
||||
int in_len = mpkt ? mpkt->len : 0;
|
||||
int in_len = mpkt->len;
|
||||
|
||||
AVPacket pkt;
|
||||
mp_set_av_packet(&pkt, mpkt, NULL);
|
||||
mp_set_av_packet(&pkt, mpkt);
|
||||
|
||||
// If we don't have a PTS yet, use the first packet PTS we can get.
|
||||
if (da->pts == MP_NOPTS_VALUE && mpkt && mpkt->pts != MP_NOPTS_VALUE) {
|
||||
da->pts = mpkt->pts;
|
||||
da->pts_offset = 0;
|
||||
if (mpkt->pts != MP_NOPTS_VALUE) {
|
||||
sh->pts = mpkt->pts;
|
||||
sh->pts_bytes = 0;
|
||||
}
|
||||
|
||||
int got_frame = 0;
|
||||
av_frame_unref(priv->avframe);
|
||||
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
|
||||
if (mpkt) {
|
||||
// At least "shorten" decodes sub-frames, instead of the whole packet.
|
||||
// At least "mpc8" can return 0 and wants the packet again next time.
|
||||
if (ret >= 0) {
|
||||
ret = FFMIN(ret, mpkt->len); // sanity check against decoder overreads
|
||||
mpkt->buffer += ret;
|
||||
mpkt->len -= ret;
|
||||
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
|
||||
}
|
||||
if (mpkt->len == 0 || ret < 0) {
|
||||
talloc_free(mpkt);
|
||||
priv->packet = NULL;
|
||||
}
|
||||
// LATM may need many packets to find mux info
|
||||
if (ret == AVERROR(EAGAIN))
|
||||
return AD_OK;
|
||||
// At least "shorten" decodes sub-frames, instead of the whole packet.
|
||||
// At least "mpc8" can return 0 and wants the packet again next time.
|
||||
if (ret >= 0) {
|
||||
ret = FFMIN(ret, mpkt->len); // sanity check against decoder overreads
|
||||
mpkt->buffer += ret;
|
||||
mpkt->len -= ret;
|
||||
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
|
||||
}
|
||||
if (mpkt->len == 0 || ret < 0) {
|
||||
talloc_free(mpkt);
|
||||
priv->packet = NULL;
|
||||
}
|
||||
// LATM may need many packets to find mux info
|
||||
if (ret == AVERROR(EAGAIN))
|
||||
return 0;
|
||||
if (ret < 0) {
|
||||
MP_ERR(da, "Error decoding audio.\n");
|
||||
return AD_ERR;
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n");
|
||||
return -1;
|
||||
}
|
||||
if (!got_frame)
|
||||
return mpkt ? AD_OK : AD_EOF;
|
||||
|
||||
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
|
||||
if (out_pts != MP_NOPTS_VALUE) {
|
||||
da->pts = out_pts;
|
||||
da->pts_offset = 0;
|
||||
return 0;
|
||||
uint64_t unitsize = (uint64_t)av_get_bytes_per_sample(avctx->sample_fmt) *
|
||||
avctx->channels;
|
||||
if (unitsize > 100000)
|
||||
abort();
|
||||
priv->unitsize = unitsize;
|
||||
uint64_t output_left = unitsize * priv->avframe->nb_samples;
|
||||
if (output_left > 500000000)
|
||||
abort();
|
||||
priv->output_left = output_left;
|
||||
if (av_sample_fmt_is_planar(avctx->sample_fmt) && avctx->channels > 1) {
|
||||
deplanarize(sh);
|
||||
} else {
|
||||
priv->output = priv->avframe->data[0];
|
||||
}
|
||||
|
||||
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
|
||||
if (!mpframe)
|
||||
return AD_ERR;
|
||||
|
||||
struct mp_chmap lavc_chmap = mpframe->channels;
|
||||
if (lavc_chmap.num != avctx->channels)
|
||||
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
|
||||
if (priv->force_channel_map) {
|
||||
struct sh_audio *sh_audio = da->header->audio;
|
||||
if (lavc_chmap.num == sh_audio->channels.num)
|
||||
lavc_chmap = sh_audio->channels;
|
||||
}
|
||||
mp_audio_set_channels(mpframe, &lavc_chmap);
|
||||
|
||||
#if HAVE_AVFRAME_SKIP_SAMPLES
|
||||
AVFrameSideData *sd =
|
||||
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
|
||||
if (sd && sd->size >= 10) {
|
||||
char *d = sd->data;
|
||||
priv->skip_samples += AV_RL32(d + 0);
|
||||
uint32_t pad = AV_RL32(d + 4);
|
||||
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
|
||||
if (skip) {
|
||||
mp_audio_skip_samples(mpframe, skip);
|
||||
da->pts_offset += skip;
|
||||
priv->skip_samples -= skip;
|
||||
}
|
||||
if (pad <= mpframe->samples)
|
||||
mpframe->samples -= pad;
|
||||
}
|
||||
#endif
|
||||
|
||||
*out = mpframe;
|
||||
|
||||
av_frame_unref(priv->avframe);
|
||||
|
||||
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, mpframe->samples);
|
||||
mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", in_len,
|
||||
priv->output_left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
|
||||
int maxlen)
|
||||
{
|
||||
struct priv *priv = sh_audio->context;
|
||||
AVCodecContext *avctx = priv->avctx;
|
||||
|
||||
int len = -1;
|
||||
while (len < minlen) {
|
||||
if (!priv->output_left) {
|
||||
if (decode_new_packet(sh_audio) < 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (setup_format(sh_audio, avctx))
|
||||
return len;
|
||||
int size = (minlen - len + priv->unitsize - 1);
|
||||
size -= size % priv->unitsize;
|
||||
size = FFMIN(size, priv->output_left);
|
||||
if (size > maxlen)
|
||||
abort();
|
||||
memcpy(buf, priv->output, size);
|
||||
priv->output += size;
|
||||
priv->output_left -= size;
|
||||
if (len < 0)
|
||||
len = size;
|
||||
else
|
||||
len += size;
|
||||
buf += size;
|
||||
maxlen -= size;
|
||||
sh_audio->pts_bytes += size;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void add_decoders(struct mp_decoder_list *list)
|
||||
{
|
||||
mp_add_lavc_decoders(list, AVMEDIA_TYPE_AUDIO);
|
||||
mp_add_decoder(list, "lavc", "pcm", "pcm", "Raw PCM");
|
||||
mp_add_decoder(list, "lavc", "mp-pcm", "mp-pcm", "Raw PCM");
|
||||
}
|
||||
|
||||
const struct ad_functions ad_lavc = {
|
||||
.name = "lavc",
|
||||
.add_decoders = add_decoders,
|
||||
.preinit = preinit,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.control = control,
|
||||
.decode_packet = decode_packet,
|
||||
.decode_audio = decode_audio,
|
||||
};
|
||||
+353
-147
@@ -25,39 +25,43 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "ad.h"
|
||||
#include "common/msg.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
|
||||
/* Reducing the ifdeffery to two main variants:
|
||||
* 1. most compatible to any libmpg123 version
|
||||
* 2. fastest variant with recent libmpg123 (>=1.14)
|
||||
* Running variant 2 on older libmpg123 versions may work in
|
||||
* principle, but is not supported.
|
||||
* So, please leave the check for MPG123_API_VERSION there, m-kay?
|
||||
*/
|
||||
#include <mpg123.h>
|
||||
|
||||
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION < 33)
|
||||
#error "This should not happen"
|
||||
/* Enable faster mode of operation with newer libmpg123, avoiding
|
||||
* unnecessary memcpy() calls. */
|
||||
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION >= 33)
|
||||
#define AD_MPG123_FRAMEWISE
|
||||
#endif
|
||||
|
||||
/* Switch for updating bitrate info of VBR files. Not essential. */
|
||||
#define AD_MPG123_MEAN_BITRATE
|
||||
|
||||
struct ad_mpg123_context {
|
||||
mpg123_handle *handle;
|
||||
int sample_size;
|
||||
char new_format;
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
/* Running mean for bit rate, stream length estimation. */
|
||||
float mean_rate;
|
||||
unsigned int mean_count;
|
||||
/* Time delay for updates. */
|
||||
short delay;
|
||||
#endif
|
||||
/* If the stream is actually VBR. */
|
||||
char vbr;
|
||||
struct mp_audio frame;
|
||||
};
|
||||
|
||||
static void uninit(struct dec_audio *da)
|
||||
{
|
||||
struct ad_mpg123_context *con = da->priv;
|
||||
|
||||
mpg123_close(con->handle);
|
||||
mpg123_delete(con->handle);
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
/* This initializes libmpg123 and prepares the handle, including funky
|
||||
* parameters. */
|
||||
static int init(struct dec_audio *da, const char *decoder)
|
||||
static int preinit(sh_audio_t *sh)
|
||||
{
|
||||
int err;
|
||||
struct ad_mpg123_context *con;
|
||||
@@ -68,8 +72,8 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
if (mpg123_init() != MPG123_OK)
|
||||
return 0;
|
||||
|
||||
da->priv = talloc_zero(NULL, struct ad_mpg123_context);
|
||||
con = da->priv;
|
||||
sh->context = malloc(sizeof(struct ad_mpg123_context));
|
||||
con = sh->context;
|
||||
/* Auto-choice of optimized decoder (first argument NULL). */
|
||||
con->handle = mpg123_new(NULL, &err);
|
||||
if (!con->handle)
|
||||
@@ -100,45 +104,33 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
/* Example for RVA choice (available since libmpg123 1.0.0):
|
||||
mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Prevent funky automatic resampling.
|
||||
* This way, we can be sure that one frame will never produce
|
||||
* more than 1152 stereo samples.
|
||||
* Background:
|
||||
* Going to decode directly to the output buffer. It is important to have
|
||||
* MPG123_AUTO_RESAMPLE disabled for the buffer size being an all-time
|
||||
* limit.
|
||||
* We need at least 1152 samples. dec_audio.c normally guarantees this. */
|
||||
* more than 1152 stereo samples. */
|
||||
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
|
||||
|
||||
err = mpg123_open_feed(con->handle);
|
||||
if (err != MPG123_OK)
|
||||
goto bad_end;
|
||||
#else
|
||||
/* Older mpg123 is vulnerable to concatenated streams when gapless cutting
|
||||
* is enabled (will only play the jingle of a badly constructed radio
|
||||
* stream). The versions using framewise decoding are fine with that. */
|
||||
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
|
||||
bad_end:
|
||||
if (!con->handle) {
|
||||
MP_ERR(da, "mpg123 preinit error: %s\n", mpg123_plain_strerror(err));
|
||||
} else {
|
||||
MP_ERR(da, "mpg123 preinit error: %s\n", mpg123_strerror(con->handle));
|
||||
}
|
||||
if (!con->handle)
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
|
||||
mpg123_plain_strerror(err));
|
||||
else
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
|
||||
uninit(da);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpg123_format_to_af(int mpg123_encoding)
|
||||
{
|
||||
/* Without external force, mpg123 will always choose signed encoding,
|
||||
* and non-16-bit only on builds that don't support it.
|
||||
* Be reminded that it doesn't matter to the MPEG file what encoding
|
||||
* is produced from it. */
|
||||
switch (mpg123_encoding) {
|
||||
case MPG123_ENC_SIGNED_8: return AF_FORMAT_S8;
|
||||
case MPG123_ENC_SIGNED_16: return AF_FORMAT_S16;
|
||||
case MPG123_ENC_SIGNED_32: return AF_FORMAT_S32;
|
||||
case MPG123_ENC_FLOAT_32: return AF_FORMAT_FLOAT;
|
||||
}
|
||||
if (con->handle)
|
||||
mpg123_delete(con->handle);
|
||||
mpg123_exit();
|
||||
free(sh->context);
|
||||
sh->context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -151,139 +143,352 @@ static int compute_bitrate(struct mpg123_frameinfo *i)
|
||||
{-1, 384, 1152, 576}, /* MPEG 2.5 */
|
||||
{-1, -1, -1, -1}, /* Unknown */
|
||||
};
|
||||
return (int) ((i->framesize + 4) * 8 * i->rate /
|
||||
return (int) ((i->framesize + 4) * 8 * i->rate * 0.001 /
|
||||
samples_per_frame[i->version][i->layer] + 0.5);
|
||||
}
|
||||
|
||||
/* Update mean bitrate. This could be dropped if accurate time display
|
||||
* on audio file playback is not desired. */
|
||||
static void update_info(struct dec_audio *da)
|
||||
/* Opted against the header printout from old mp3lib, too much
|
||||
* irrelevant info. This is modelled after the mpg123 app's
|
||||
* standard output line.
|
||||
* If more verbosity is demanded, one can add more detail and
|
||||
* also throw in ID3v2 info which libmpg123 collects anyway. */
|
||||
static void print_header_compact(struct mpg123_frameinfo *i)
|
||||
{
|
||||
struct ad_mpg123_context *con = da->priv;
|
||||
struct mpg123_frameinfo finfo;
|
||||
if (mpg123_info(con->handle, &finfo) != MPG123_OK)
|
||||
return;
|
||||
static const char *smodes[5] = {
|
||||
"stereo", "joint-stereo", "dual-channel", "mono", "invalid"
|
||||
};
|
||||
static const char *layers[4] = {
|
||||
"Unknown", "I", "II", "III"
|
||||
};
|
||||
static const char *versions[4] = {
|
||||
"1.0", "2.0", "2.5", "x.x"
|
||||
};
|
||||
|
||||
/* finfo.bitrate is expressed in kilobits */
|
||||
const int bitrate = finfo.bitrate * 1000;
|
||||
|
||||
if (finfo.vbr != MPG123_CBR) {
|
||||
if (--con->delay < 1) {
|
||||
if (++con->mean_count > ((unsigned int) -1) / 2)
|
||||
con->mean_count = ((unsigned int) -1) / 4;
|
||||
|
||||
/* Might not be numerically optimal, but works fine enough. */
|
||||
con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
|
||||
bitrate) / con->mean_count;
|
||||
da->bitrate = (int) (con->mean_rate + 0.5);
|
||||
|
||||
con->delay = 10;
|
||||
}
|
||||
} else {
|
||||
da->bitrate = bitrate ? bitrate : compute_bitrate(&finfo);
|
||||
con->delay = 1;
|
||||
con->mean_rate = 0.;
|
||||
con->mean_count = 0;
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "MPEG %s layer %s, ",
|
||||
versions[i->version], layers[i->layer]);
|
||||
switch (i->vbr) {
|
||||
case MPG123_CBR:
|
||||
if (i->bitrate)
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s", i->bitrate);
|
||||
else
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s (free format)",
|
||||
compute_bitrate(i));
|
||||
break;
|
||||
case MPG123_VBR:
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "VBR");
|
||||
break;
|
||||
case MPG123_ABR:
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s ABR", i->abr_rate);
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "???");
|
||||
}
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, ", %ld Hz %s\n", i->rate,
|
||||
smodes[i->mode]);
|
||||
}
|
||||
|
||||
/* libmpg123 has a new format ready; query and store, return return value
|
||||
of mpg123_getformat() */
|
||||
static int set_format(struct dec_audio *da)
|
||||
static int set_format(sh_audio_t *sh, struct ad_mpg123_context *con)
|
||||
{
|
||||
struct ad_mpg123_context *con = da->priv;
|
||||
int ret;
|
||||
long rate;
|
||||
int channels;
|
||||
int encoding;
|
||||
ret = mpg123_getformat(con->handle, &rate, &channels, &encoding);
|
||||
if (ret == MPG123_OK) {
|
||||
mp_audio_set_num_channels(&con->frame, channels);
|
||||
con->frame.rate = rate;
|
||||
int af = mpg123_format_to_af(encoding);
|
||||
if (!af) {
|
||||
if(ret == MPG123_OK) {
|
||||
mp_chmap_from_channels(&sh->channels, channels);
|
||||
sh->samplerate = rate;
|
||||
/* Without external force, mpg123 will always choose signed encoding,
|
||||
* and non-16-bit only on builds that don't support it.
|
||||
* Be reminded that it doesn't matter to the MPEG file what encoding
|
||||
* is produced from it. */
|
||||
switch (encoding) {
|
||||
case MPG123_ENC_SIGNED_8:
|
||||
sh->sample_format = AF_FORMAT_S8;
|
||||
sh->samplesize = 1;
|
||||
break;
|
||||
case MPG123_ENC_SIGNED_16:
|
||||
sh->sample_format = AF_FORMAT_S16_NE;
|
||||
sh->samplesize = 2;
|
||||
break;
|
||||
/* To stay compatible with the oldest libmpg123 headers, do not rely
|
||||
* on float and 32 bit encoding symbols being defined.
|
||||
* Those formats came later */
|
||||
case 0x1180: /* MPG123_ENC_SIGNED_32 */
|
||||
sh->sample_format = AF_FORMAT_S32_NE;
|
||||
sh->samplesize = 4;
|
||||
break;
|
||||
case 0x200: /* MPG123_ENC_FLOAT_32 */
|
||||
sh->sample_format = AF_FORMAT_FLOAT_NE;
|
||||
sh->samplesize = 4;
|
||||
break;
|
||||
default:
|
||||
/* This means we got a funny custom build of libmpg123 that only supports an unknown format. */
|
||||
MP_ERR(da, "Bad encoding from mpg123: %i.\n", encoding);
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"Bad encoding from mpg123: %i.\n", encoding);
|
||||
return MPG123_ERR;
|
||||
}
|
||||
mp_audio_set_format(&con->frame, af);
|
||||
con->sample_size = channels * af_fmt2bps(af);
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Going to decode directly to MPlayer's memory. It is important
|
||||
* to have MPG123_AUTO_RESAMPLE disabled for the buffer size
|
||||
* being an all-time limit. */
|
||||
sh->audio_out_minsize = 1152 * 2 * sh->samplesize;
|
||||
#endif
|
||||
con->new_format = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int decode_packet(struct dec_audio *da, struct mp_audio **out)
|
||||
/* This tries to extract a requested amount of decoded data.
|
||||
* Even when you request 0 bytes, it will feed enough input so that
|
||||
* the decoder _could_ have delivered something.
|
||||
* Returns byte count >= 0, -1 on error.
|
||||
*
|
||||
* Thoughts on exact pts keeping:
|
||||
* We have to assume that MPEG frames are cut in pieces by packet boundaries.
|
||||
* Also, it might be possible that the first packet does not contain enough
|
||||
* data to ensure initial stream sync... or re-sync on erroneous streams.
|
||||
* So we need something robust to relate the decoded byte count to the correct
|
||||
* time stamp. This is tricky, though. From the outside, you cannot tell if,
|
||||
* after having fed two packets until the first output arrives, one should
|
||||
* start counting from the first packet's pts or the second packet's.
|
||||
* So, let's just count from the last fed package's pts. If the packets are
|
||||
* exactly cut to MPEG frames, this will cause one frame mismatch in the
|
||||
* beginning (when mpg123 peeks ahead for the following header), but will
|
||||
* be corrected with the third frame already. One might add special code to
|
||||
* not increment the base pts past the first packet's after a resync before
|
||||
* the first decoded bytes arrived. */
|
||||
static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
|
||||
{
|
||||
struct ad_mpg123_context *con = da->priv;
|
||||
int ret;
|
||||
int ret = MPG123_OK;
|
||||
int got = 0;
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
|
||||
struct demux_packet *pkt;
|
||||
if (demux_read_packet_async(da->header, &pkt) == 0)
|
||||
return AD_WAIT;
|
||||
if (!pkt)
|
||||
return AD_EOF;
|
||||
/* There will be one MPG123_NEW_FORMAT message on first open.
|
||||
* This will be handled in init(). */
|
||||
do {
|
||||
size_t got_now = 0;
|
||||
/* Fetch new format now, after old data has been used. */
|
||||
if(con->new_format)
|
||||
ret = set_format(sh, con);
|
||||
|
||||
/* Next bytes from that presentation time. */
|
||||
if (pkt->pts != MP_NOPTS_VALUE) {
|
||||
da->pts = pkt->pts;
|
||||
da->pts_offset = 0;
|
||||
/* Feed the decoder. This will only fire from the second round on. */
|
||||
if (ret == MPG123_NEED_MORE) {
|
||||
/* Feed more input data. */
|
||||
struct demux_packet *pkt = demux_read_packet(sh->gsh);
|
||||
if (!pkt)
|
||||
break; /* Apparently that's it. EOF. */
|
||||
|
||||
/* Next bytes from that presentation time. */
|
||||
if (pkt->pts != MP_NOPTS_VALUE) {
|
||||
sh->pts = pkt->pts;
|
||||
sh->pts_bytes = 0;
|
||||
}
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Have to use mpg123_feed() to avoid decoding here. */
|
||||
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
|
||||
#else
|
||||
/* Do not use mpg123_feed(), added in later libmpg123 versions. */
|
||||
ret = mpg123_decode(con->handle, pkt->buffer, pkt->len, NULL, 0, NULL);
|
||||
#endif
|
||||
talloc_free(pkt);
|
||||
if (ret == MPG123_ERR)
|
||||
break;
|
||||
|
||||
/* Indication of format change is possible here (from mpg123_decode()). */
|
||||
if(ret == MPG123_NEW_FORMAT) {
|
||||
con->new_format = 1;
|
||||
if(got)
|
||||
break; /* Do not switch format during a chunk. */
|
||||
|
||||
ret = set_format(sh, con);
|
||||
}
|
||||
}
|
||||
/* Theoretically, mpg123 could return MPG123_DONE, so be prepared.
|
||||
* Should not happen in our usage, but it is a valid return code. */
|
||||
else if (ret == MPG123_ERR || ret == MPG123_DONE)
|
||||
break;
|
||||
|
||||
/* Try to decode a bit. This is the return value that counts
|
||||
* for the loop condition. */
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
if (!buf) { /* fake call just for feeding to get format */
|
||||
ret = set_format(sh, con);
|
||||
} else { /* This is the decoding. One frame at a time. */
|
||||
ret = mpg123_replace_buffer(con->handle, buf, count);
|
||||
if (ret == MPG123_OK)
|
||||
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
|
||||
}
|
||||
#else
|
||||
ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got,
|
||||
&got_now);
|
||||
#endif
|
||||
|
||||
got += got_now;
|
||||
sh->pts_bytes += got_now;
|
||||
|
||||
/* Indication of format change should happen here. */
|
||||
if(ret == MPG123_NEW_FORMAT) {
|
||||
con->new_format = 1;
|
||||
if(got)
|
||||
break; /* Do not switch format during a chunk. */
|
||||
|
||||
ret = set_format(sh, con);
|
||||
}
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
} while (ret == MPG123_NEED_MORE || (got == 0 && count != 0));
|
||||
#else
|
||||
} while (ret == MPG123_NEED_MORE || got < count);
|
||||
#endif
|
||||
|
||||
if (ret == MPG123_ERR) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 decoding failed: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
}
|
||||
|
||||
/* Have to use mpg123_feed() to avoid decoding here. */
|
||||
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
|
||||
talloc_free(pkt);
|
||||
|
||||
if (ret != MPG123_OK)
|
||||
goto mpg123_fail;
|
||||
|
||||
unsigned char *audio = NULL;
|
||||
size_t bytes = 0;
|
||||
ret = mpg123_decode_frame(con->handle, NULL, &audio, &bytes);
|
||||
|
||||
if (ret == MPG123_NEED_MORE)
|
||||
return 0;
|
||||
|
||||
if (ret != MPG123_OK && ret != MPG123_DONE && ret != MPG123_NEW_FORMAT)
|
||||
goto mpg123_fail;
|
||||
|
||||
ret = set_format(da);
|
||||
if (ret != MPG123_OK)
|
||||
goto mpg123_fail;
|
||||
|
||||
if (con->sample_size < 1) {
|
||||
MP_ERR(da, "no sample size\n");
|
||||
return AD_ERR;
|
||||
}
|
||||
|
||||
int got_samples = bytes / con->sample_size;
|
||||
*out = mp_audio_pool_get(da->pool, &con->frame, got_samples);
|
||||
if (!*out)
|
||||
return AD_ERR;
|
||||
|
||||
memcpy((*out)->planes[0], audio, bytes);
|
||||
|
||||
update_info(da);
|
||||
return 0;
|
||||
|
||||
mpg123_fail:
|
||||
MP_ERR(da, "mpg123 decoding error: %s\n", mpg123_strerror(con->handle));
|
||||
return AD_ERR;
|
||||
return got;
|
||||
}
|
||||
|
||||
static int control(struct dec_audio *da, int cmd, void *arg)
|
||||
/* Close, reopen stream. Feed data until we know the format of the stream.
|
||||
* 1 on success, 0 on error */
|
||||
static int reopen_stream(sh_audio_t *sh)
|
||||
{
|
||||
struct ad_mpg123_context *con = da->priv;
|
||||
struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
|
||||
|
||||
mpg123_close(con->handle);
|
||||
/* No resetting of the context:
|
||||
* We do not want to loose the mean bitrate data. */
|
||||
|
||||
/* Open and make sure we have fed enough data to get stream properties. */
|
||||
if (MPG123_OK == mpg123_open_feed(con->handle) &&
|
||||
/* Feed data until mpg123 is ready (has found stream beginning). */
|
||||
!decode_a_bit(sh, NULL, 0) &&
|
||||
set_format(sh, con) == MPG123_OK) { /* format setting again just for return value */
|
||||
return 1;
|
||||
} else {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"mpg123 failed to reopen stream: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we really start accessing some data and determining file format.
|
||||
* Format now is allowed to change on-the-fly. Here is the only point
|
||||
* that has MPlayer react to errors. We have to pray that exceptional
|
||||
* erros in other places simply cannot occur. */
|
||||
static int init(sh_audio_t *sh, const char *decoder)
|
||||
{
|
||||
mpg123_id3v2 *v2;
|
||||
struct mpg123_frameinfo finfo;
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
|
||||
con->new_format = 0;
|
||||
if (reopen_stream(sh) &&
|
||||
/* Get MPEG header info. */
|
||||
MPG123_OK == mpg123_info(con->handle, &finfo) &&
|
||||
/* Since we queried format, mpg123 should have read past ID3v2 tags.
|
||||
* We need to decide if printing of UTF-8 encoded text info is wanted. */
|
||||
MPG123_OK == mpg123_id3(con->handle, NULL, &v2)) {
|
||||
/* If we are here, we passed all hurdles. Yay! Extract the info. */
|
||||
print_header_compact(&finfo);
|
||||
/* Do we want to print out the UTF-8 Id3v2 info?
|
||||
if (v2)
|
||||
print_id3v2(v2); */
|
||||
|
||||
/* Have kb/s, want B/s
|
||||
* For VBR, the first frame will be a bad estimate. */
|
||||
sh->i_bps = (finfo.bitrate ? finfo.bitrate : compute_bitrate(&finfo))
|
||||
* 1000 / 8;
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
con->delay = 1;
|
||||
con->mean_rate = 0.;
|
||||
con->mean_count = 0;
|
||||
#endif
|
||||
con->vbr = (finfo.vbr != MPG123_CBR);
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 init error: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void uninit(sh_audio_t *sh)
|
||||
{
|
||||
struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
|
||||
|
||||
mpg123_close(con->handle);
|
||||
mpg123_delete(con->handle);
|
||||
free(sh->context);
|
||||
sh->context = NULL;
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
/* Update mean bitrate. This could be dropped if accurate time display
|
||||
* on audio file playback is not desired. */
|
||||
static void update_info(sh_audio_t *sh)
|
||||
{
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
if (con->vbr && --con->delay < 1) {
|
||||
struct mpg123_frameinfo finfo;
|
||||
if (MPG123_OK == mpg123_info(con->handle, &finfo)) {
|
||||
if (++con->mean_count > ((unsigned int) -1) / 2)
|
||||
con->mean_count = ((unsigned int) -1) / 4;
|
||||
|
||||
/* Might not be numerically optimal, but works fine enough. */
|
||||
con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
|
||||
finfo.bitrate) / con->mean_count;
|
||||
sh->i_bps = (int) (con->mean_rate * 1000 / 8);
|
||||
|
||||
con->delay = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int decode_audio(sh_audio_t *sh, unsigned char *buf, int minlen,
|
||||
int maxlen)
|
||||
{
|
||||
int bytes;
|
||||
|
||||
bytes = decode_a_bit(sh, buf, maxlen);
|
||||
/* This EOF is ignored, apparently, until input data is exhausted. */
|
||||
if (bytes == 0)
|
||||
return -1; /* EOF */
|
||||
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
update_info(sh);
|
||||
#endif
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int control(sh_audio_t *sh, int cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case ADCTRL_RESET:
|
||||
mpg123_close(con->handle);
|
||||
|
||||
if (mpg123_open_feed(con->handle) != MPG123_OK) {
|
||||
MP_ERR(da, "mpg123 failed to reopen stream: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
case ADCTRL_RESYNC_STREAM:
|
||||
/* Close/reopen the stream for mpg123 to make sure it doesn't
|
||||
* think that it still knows the exact stream position.
|
||||
* Otherwise, we would have funny effects from the gapless code.
|
||||
* Oh, and it helps to minimize artifacts from jumping in the stream. */
|
||||
if (reopen_stream(sh)) {
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
update_info(sh);
|
||||
#endif
|
||||
return CONTROL_TRUE;
|
||||
} else {
|
||||
/* MPlayer ignores this case! It just keeps on decoding.
|
||||
* So we have to make sure resync never fails ... */
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"mpg123 cannot reopen stream for resync.\n");
|
||||
return CONTROL_FALSE;
|
||||
}
|
||||
return CONTROL_TRUE;
|
||||
break;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -297,8 +502,9 @@ static void add_decoders(struct mp_decoder_list *list)
|
||||
const struct ad_functions ad_mpg123 = {
|
||||
.name = "mpg123",
|
||||
.add_decoders = add_decoders,
|
||||
.preinit = preinit,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.control = control,
|
||||
.decode_packet = decode_packet,
|
||||
.decode_audio = decode_audio,
|
||||
};
|
||||
+203
-181
@@ -19,214 +19,60 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/opt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/av_common.h"
|
||||
#include "options/options.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
#include "mpvcore/av_common.h"
|
||||
#include "mpvcore/options.h"
|
||||
#include "ad.h"
|
||||
|
||||
#define FILENAME_SPDIFENC "spdif"
|
||||
#define OUTBUF_SIZE 65536
|
||||
|
||||
struct spdifContext {
|
||||
struct mp_log *log;
|
||||
AVFormatContext *lavf_ctx;
|
||||
int iec61937_packet_size;
|
||||
int out_buffer_len;
|
||||
uint8_t out_buffer[OUTBUF_SIZE];
|
||||
bool need_close;
|
||||
struct mp_audio fmt;
|
||||
int out_buffer_size;
|
||||
uint8_t *out_buffer;
|
||||
uint8_t pb_buffer[OUTBUF_SIZE];
|
||||
};
|
||||
|
||||
static void uninit(sh_audio_t *sh);
|
||||
|
||||
static int read_packet(void *p, uint8_t *buf, int buf_size)
|
||||
{
|
||||
// spdifenc does not use read callback.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_packet(void *p, uint8_t *buf, int buf_size)
|
||||
{
|
||||
int len;
|
||||
struct spdifContext *ctx = p;
|
||||
|
||||
int buffer_left = OUTBUF_SIZE - ctx->out_buffer_len;
|
||||
if (buf_size > buffer_left) {
|
||||
MP_ERR(ctx, "spdif packet too large.\n");
|
||||
buf_size = buffer_left;
|
||||
}
|
||||
|
||||
memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, buf_size);
|
||||
ctx->out_buffer_len += buf_size;
|
||||
return buf_size;
|
||||
len = FFMIN(buf_size, ctx->out_buffer_size -ctx->out_buffer_len);
|
||||
memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, len);
|
||||
ctx->out_buffer_len += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void uninit(struct dec_audio *da)
|
||||
static int64_t seek(void *p, int64_t offset, int whence)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = da->priv;
|
||||
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
|
||||
|
||||
if (lavf_ctx) {
|
||||
if (spdif_ctx->need_close)
|
||||
av_write_trailer(lavf_ctx);
|
||||
if (lavf_ctx->pb)
|
||||
av_freep(&lavf_ctx->pb->buffer);
|
||||
av_freep(&lavf_ctx->pb);
|
||||
avformat_free_context(lavf_ctx);
|
||||
}
|
||||
// spdifenc does not use seek callback.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init(struct dec_audio *da, const char *decoder)
|
||||
static int preinit(sh_audio_t *sh)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = talloc_zero(NULL, struct spdifContext);
|
||||
da->priv = spdif_ctx;
|
||||
spdif_ctx->log = da->log;
|
||||
|
||||
AVFormatContext *lavf_ctx = avformat_alloc_context();
|
||||
if (!lavf_ctx)
|
||||
goto fail;
|
||||
|
||||
lavf_ctx->oformat = av_guess_format("spdif", NULL, NULL);
|
||||
if (!lavf_ctx->oformat)
|
||||
goto fail;
|
||||
|
||||
spdif_ctx->lavf_ctx = lavf_ctx;
|
||||
|
||||
void *buffer = av_mallocz(OUTBUF_SIZE);
|
||||
if (!buffer)
|
||||
abort();
|
||||
lavf_ctx->pb = avio_alloc_context(buffer, OUTBUF_SIZE, 1, spdif_ctx, NULL,
|
||||
write_packet, NULL);
|
||||
if (!lavf_ctx->pb) {
|
||||
av_free(buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Request minimal buffering (not available on Libav)
|
||||
#if LIBAVFORMAT_VERSION_MICRO >= 100
|
||||
lavf_ctx->pb->direct = 1;
|
||||
#endif
|
||||
|
||||
AVStream *stream = avformat_new_stream(lavf_ctx, 0);
|
||||
if (!stream)
|
||||
goto fail;
|
||||
|
||||
stream->codec->codec_id = mp_codec_to_av_codec_id(decoder);
|
||||
|
||||
AVDictionary *format_opts = NULL;
|
||||
|
||||
int num_channels = 0;
|
||||
int sample_format = 0;
|
||||
int samplerate = 0;
|
||||
switch (stream->codec->codec_id) {
|
||||
case AV_CODEC_ID_AAC:
|
||||
spdif_ctx->iec61937_packet_size = 16384;
|
||||
sample_format = AF_FORMAT_S_AAC;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_AC3:
|
||||
spdif_ctx->iec61937_packet_size = 6144;
|
||||
sample_format = AF_FORMAT_S_AC3;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_DTS:
|
||||
if (da->opts->dtshd) {
|
||||
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_S_DTSHD;
|
||||
samplerate = 192000;
|
||||
num_channels = 2*4;
|
||||
} else {
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_S_DTS;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
}
|
||||
break;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
spdif_ctx->iec61937_packet_size = 24576;
|
||||
sample_format = AF_FORMAT_S_EAC3;
|
||||
samplerate = 192000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_MP3:
|
||||
spdif_ctx->iec61937_packet_size = 4608;
|
||||
sample_format = AF_FORMAT_S_MP3;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_TRUEHD:
|
||||
spdif_ctx->iec61937_packet_size = 61440;
|
||||
sample_format = AF_FORMAT_S_TRUEHD;
|
||||
samplerate = 192000;
|
||||
num_channels = 8;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
mp_audio_set_num_channels(&spdif_ctx->fmt, num_channels);
|
||||
mp_audio_set_format(&spdif_ctx->fmt, sample_format);
|
||||
spdif_ctx->fmt.rate = samplerate;
|
||||
|
||||
if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
|
||||
MP_FATAL(da, "libavformat spdif initialization failed.\n");
|
||||
av_dict_free(&format_opts);
|
||||
goto fail;
|
||||
}
|
||||
av_dict_free(&format_opts);
|
||||
|
||||
spdif_ctx->need_close = true;
|
||||
|
||||
sh->samplesize = 2;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
uninit(da);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_packet(struct dec_audio *da, struct mp_audio **out)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = da->priv;
|
||||
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
|
||||
|
||||
spdif_ctx->out_buffer_len = 0;
|
||||
|
||||
struct demux_packet *mpkt;
|
||||
if (demux_read_packet_async(da->header, &mpkt) == 0)
|
||||
return AD_WAIT;
|
||||
|
||||
if (!mpkt)
|
||||
return AD_EOF;
|
||||
|
||||
AVPacket pkt;
|
||||
mp_set_av_packet(&pkt, mpkt, NULL);
|
||||
pkt.pts = pkt.dts = 0;
|
||||
MP_VERBOSE(da, "spdif packet, size=%d\n", pkt.size);
|
||||
if (mpkt->pts != MP_NOPTS_VALUE) {
|
||||
da->pts = mpkt->pts;
|
||||
da->pts_offset = 0;
|
||||
}
|
||||
int ret = av_write_frame(lavf_ctx, &pkt);
|
||||
talloc_free(mpkt);
|
||||
avio_flush(lavf_ctx->pb);
|
||||
if (ret < 0)
|
||||
return AD_ERR;
|
||||
|
||||
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
|
||||
*out = mp_audio_pool_get(da->pool, &spdif_ctx->fmt, samples);
|
||||
if (!*out)
|
||||
return AD_ERR;
|
||||
|
||||
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int control(struct dec_audio *da, int cmd, void *arg)
|
||||
{
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
static const int codecs[] = {
|
||||
static int codecs[] = {
|
||||
AV_CODEC_ID_AAC,
|
||||
AV_CODEC_ID_AC3,
|
||||
AV_CODEC_ID_DTS,
|
||||
@@ -236,6 +82,181 @@ static const int codecs[] = {
|
||||
AV_CODEC_ID_NONE
|
||||
};
|
||||
|
||||
static int init(sh_audio_t *sh, const char *decoder)
|
||||
{
|
||||
int srate, bps, *dtshd_rate;
|
||||
AVFormatContext *lavf_ctx = NULL;
|
||||
AVStream *stream = NULL;
|
||||
const AVOption *opt = NULL;
|
||||
struct spdifContext *spdif_ctx = NULL;
|
||||
|
||||
spdif_ctx = av_mallocz(sizeof(*spdif_ctx));
|
||||
if (!spdif_ctx)
|
||||
goto fail;
|
||||
spdif_ctx->lavf_ctx = avformat_alloc_context();
|
||||
if (!spdif_ctx->lavf_ctx)
|
||||
goto fail;
|
||||
|
||||
sh->context = spdif_ctx;
|
||||
lavf_ctx = spdif_ctx->lavf_ctx;
|
||||
|
||||
lavf_ctx->oformat = av_guess_format(FILENAME_SPDIFENC, NULL, NULL);
|
||||
if (!lavf_ctx->oformat)
|
||||
goto fail;
|
||||
lavf_ctx->priv_data = av_mallocz(lavf_ctx->oformat->priv_data_size);
|
||||
if (!lavf_ctx->priv_data)
|
||||
goto fail;
|
||||
lavf_ctx->pb = avio_alloc_context(spdif_ctx->pb_buffer, OUTBUF_SIZE, 1, spdif_ctx,
|
||||
read_packet, write_packet, seek);
|
||||
if (!lavf_ctx->pb)
|
||||
goto fail;
|
||||
stream = avformat_new_stream(lavf_ctx, 0);
|
||||
if (!stream)
|
||||
goto fail;
|
||||
lavf_ctx->duration = AV_NOPTS_VALUE;
|
||||
lavf_ctx->start_time = AV_NOPTS_VALUE;
|
||||
lavf_ctx->streams[0]->codec->codec_id = mp_codec_to_av_codec_id(decoder);
|
||||
lavf_ctx->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
|
||||
if (AVERROR_PATCHWELCOME == lavf_ctx->oformat->write_header(lavf_ctx)) {
|
||||
mp_msg(MSGT_DECAUDIO,MSGL_INFO,
|
||||
"This codec is not supported by spdifenc.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
srate = 48000; //fake value
|
||||
bps = 768000/8; //fake value
|
||||
|
||||
int num_channels = 0;
|
||||
switch (lavf_ctx->streams[0]->codec->codec_id) {
|
||||
case AV_CODEC_ID_AAC:
|
||||
spdif_ctx->iec61937_packet_size = 16384;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = srate;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
case AV_CODEC_ID_AC3:
|
||||
spdif_ctx->iec61937_packet_size = 6144;
|
||||
sh->sample_format = AF_FORMAT_AC3_LE;
|
||||
sh->samplerate = srate;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
case AV_CODEC_ID_DTS:
|
||||
if(sh->opts->dtshd) {
|
||||
opt = av_opt_find(&lavf_ctx->oformat->priv_class,
|
||||
"dtshd_rate", NULL, 0, 0);
|
||||
if (!opt)
|
||||
goto fail;
|
||||
dtshd_rate = (int*)(((uint8_t*)lavf_ctx->priv_data) +
|
||||
opt->offset);
|
||||
*dtshd_rate = 192000*4;
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = 192000; // DTS core require 48000
|
||||
num_channels = 2*4;
|
||||
sh->i_bps = bps;
|
||||
} else {
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sh->sample_format = AF_FORMAT_AC3_LE;
|
||||
sh->samplerate = srate;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
}
|
||||
break;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
spdif_ctx->iec61937_packet_size = 24576;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = 192000;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
case AV_CODEC_ID_MP3:
|
||||
spdif_ctx->iec61937_packet_size = 4608;
|
||||
sh->sample_format = AF_FORMAT_MPEG2;
|
||||
sh->samplerate = srate;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
case AV_CODEC_ID_TRUEHD:
|
||||
spdif_ctx->iec61937_packet_size = 61440;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = 192000;
|
||||
num_channels = 8;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (num_channels)
|
||||
mp_chmap_from_channels(&sh->channels, num_channels);
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
uninit(sh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_audio(sh_audio_t *sh, unsigned char *buf,
|
||||
int minlen, int maxlen)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = sh->context;
|
||||
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
|
||||
AVPacket pkt;
|
||||
|
||||
spdif_ctx->out_buffer_len = 0;
|
||||
spdif_ctx->out_buffer_size = maxlen;
|
||||
spdif_ctx->out_buffer = buf;
|
||||
while (spdif_ctx->out_buffer_len + spdif_ctx->iec61937_packet_size < maxlen
|
||||
&& spdif_ctx->out_buffer_len < minlen) {
|
||||
struct demux_packet *mpkt = demux_read_packet(sh->gsh);
|
||||
if (!mpkt)
|
||||
break;
|
||||
mp_set_av_packet(&pkt, mpkt);
|
||||
mp_msg(MSGT_DECAUDIO,MSGL_V, "pkt.data[%p] pkt.size[%d]\n",
|
||||
pkt.data, pkt.size);
|
||||
if (mpkt->pts != MP_NOPTS_VALUE) {
|
||||
sh->pts = mpkt->pts;
|
||||
sh->pts_bytes = 0;
|
||||
}
|
||||
int out_len = spdif_ctx->out_buffer_len;
|
||||
int ret = lavf_ctx->oformat->write_packet(lavf_ctx, &pkt);
|
||||
avio_flush(lavf_ctx->pb);
|
||||
sh->pts_bytes += spdif_ctx->out_buffer_len - out_len;
|
||||
talloc_free(mpkt);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
return spdif_ctx->out_buffer_len;
|
||||
}
|
||||
|
||||
static int control(sh_audio_t *sh, int cmd, void *arg)
|
||||
{
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
static void uninit(sh_audio_t *sh)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = sh->context;
|
||||
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
|
||||
|
||||
if (lavf_ctx) {
|
||||
if (lavf_ctx->oformat)
|
||||
lavf_ctx->oformat->write_trailer(lavf_ctx);
|
||||
av_freep(&lavf_ctx->pb);
|
||||
if (lavf_ctx->streams) {
|
||||
av_freep(&lavf_ctx->streams[0]->codec);
|
||||
av_freep(&lavf_ctx->streams[0]->info);
|
||||
av_freep(&lavf_ctx->streams[0]);
|
||||
}
|
||||
av_freep(&lavf_ctx->streams);
|
||||
av_freep(&lavf_ctx->priv_data);
|
||||
}
|
||||
av_freep(&lavf_ctx);
|
||||
av_freep(&spdif_ctx);
|
||||
}
|
||||
|
||||
static void add_decoders(struct mp_decoder_list *list)
|
||||
{
|
||||
for (int n = 0; codecs[n] != AV_CODEC_ID_NONE; n++) {
|
||||
@@ -250,8 +271,9 @@ static void add_decoders(struct mp_decoder_list *list)
|
||||
const struct ad_functions ad_spdif = {
|
||||
.name = "spdif",
|
||||
.add_decoders = add_decoders,
|
||||
.preinit = preinit,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.control = control,
|
||||
.decode_packet = decode_packet,
|
||||
.decode_audio = decode_audio,
|
||||
};
|
||||
+244
-120
@@ -26,9 +26,9 @@
|
||||
#include "demux/codec_tags.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "common/codecs.h"
|
||||
#include "common/msg.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "mpvcore/codecs.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
#include "mpvcore/bstr.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "demux/demux.h"
|
||||
@@ -38,8 +38,6 @@
|
||||
#include "dec_audio.h"
|
||||
#include "ad.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
|
||||
#include "audio/filter/af.h"
|
||||
|
||||
@@ -48,38 +46,59 @@ extern const struct ad_functions ad_lavc;
|
||||
extern const struct ad_functions ad_spdif;
|
||||
|
||||
static const struct ad_functions * const ad_drivers[] = {
|
||||
&ad_lavc,
|
||||
#if HAVE_MPG123
|
||||
#ifdef CONFIG_MPG123
|
||||
&ad_mpg123,
|
||||
#endif
|
||||
&ad_lavc,
|
||||
&ad_spdif,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void uninit_decoder(struct dec_audio *d_audio)
|
||||
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
|
||||
{
|
||||
if (d_audio->ad_driver) {
|
||||
MP_VERBOSE(d_audio, "Uninit audio decoder.\n");
|
||||
d_audio->ad_driver->uninit(d_audio);
|
||||
assert(!sh_audio->initialized);
|
||||
resync_audio_stream(sh_audio);
|
||||
sh_audio->samplesize = 4;
|
||||
sh_audio->sample_format = AF_FORMAT_FLOAT_NE;
|
||||
sh_audio->audio_out_minsize = 8192; // default, preinit() may change it
|
||||
if (!sh_audio->ad_driver->preinit(sh_audio)) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder preinit failed.\n");
|
||||
return 0;
|
||||
}
|
||||
d_audio->ad_driver = NULL;
|
||||
talloc_free(d_audio->priv);
|
||||
d_audio->priv = NULL;
|
||||
}
|
||||
|
||||
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
|
||||
{
|
||||
if (!d_audio->ad_driver->init(d_audio, decoder)) {
|
||||
MP_VERBOSE(d_audio, "Audio decoder init failed.\n");
|
||||
d_audio->ad_driver = NULL;
|
||||
uninit_decoder(d_audio);
|
||||
const int base_size = 65536;
|
||||
// At least 64 KiB plus rounding up to next decodable unit size
|
||||
sh_audio->a_buffer_size = base_size + sh_audio->audio_out_minsize;
|
||||
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
|
||||
"dec_audio: Allocating %d + %d = %d bytes for output buffer.\n",
|
||||
sh_audio->audio_out_minsize, base_size,
|
||||
sh_audio->a_buffer_size);
|
||||
|
||||
sh_audio->a_buffer = av_mallocz(sh_audio->a_buffer_size);
|
||||
if (!sh_audio->a_buffer)
|
||||
abort();
|
||||
sh_audio->a_buffer_len = 0;
|
||||
|
||||
if (!sh_audio->ad_driver->init(sh_audio, decoder)) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Audio decoder init failed.\n");
|
||||
uninit_audio(sh_audio); // free buffers
|
||||
return 0;
|
||||
}
|
||||
|
||||
sh_audio->initialized = 1;
|
||||
|
||||
if (mp_chmap_is_empty(&sh_audio->channels) || !sh_audio->samplerate) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder did not specify "
|
||||
"audio format!\n");
|
||||
uninit_audio(sh_audio); // free buffers
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct mp_decoder_list *audio_decoder_list(void)
|
||||
struct mp_decoder_list *mp_audio_decoder_list(void)
|
||||
{
|
||||
struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
|
||||
for (int i = 0; ad_drivers[i] != NULL; i++)
|
||||
@@ -87,10 +106,10 @@ struct mp_decoder_list *audio_decoder_list(void)
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct mp_decoder_list *audio_select_decoders(const char *codec,
|
||||
char *selection)
|
||||
static struct mp_decoder_list *mp_select_audio_decoders(const char *codec,
|
||||
char *selection)
|
||||
{
|
||||
struct mp_decoder_list *list = audio_decoder_list();
|
||||
struct mp_decoder_list *list = mp_audio_decoder_list();
|
||||
struct mp_decoder_list *new = mp_select_decoders(list, codec, selection);
|
||||
talloc_free(list);
|
||||
return new;
|
||||
@@ -105,139 +124,244 @@ static const struct ad_functions *find_driver(const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders)
|
||||
int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders)
|
||||
{
|
||||
assert(!d_audio->ad_driver);
|
||||
audio_reset_decoding(d_audio);
|
||||
assert(!sh_audio->initialized);
|
||||
|
||||
struct mp_decoder_entry *decoder = NULL;
|
||||
struct mp_decoder_list *list =
|
||||
audio_select_decoders(d_audio->header->codec, audio_decoders);
|
||||
mp_select_audio_decoders(sh_audio->gsh->codec, audio_decoders);
|
||||
|
||||
mp_print_decoders(d_audio->log, MSGL_V, "Codec list:", list);
|
||||
mp_print_decoders(MSGT_DECAUDIO, MSGL_V, "Codec list:", list);
|
||||
|
||||
for (int n = 0; n < list->num_entries; n++) {
|
||||
struct mp_decoder_entry *sel = &list->entries[n];
|
||||
const struct ad_functions *driver = find_driver(sel->family);
|
||||
if (!driver)
|
||||
continue;
|
||||
MP_VERBOSE(d_audio, "Opening audio decoder %s:%s\n",
|
||||
sel->family, sel->decoder);
|
||||
d_audio->ad_driver = driver;
|
||||
if (init_audio_codec(d_audio, sel->decoder)) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Opening audio decoder %s:%s\n",
|
||||
sel->family, sel->decoder);
|
||||
sh_audio->ad_driver = driver;
|
||||
if (init_audio_codec(sh_audio, sel->decoder)) {
|
||||
decoder = sel;
|
||||
break;
|
||||
}
|
||||
MP_WARN(d_audio, "Audio decoder init failed for "
|
||||
sh_audio->ad_driver = NULL;
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_WARN, "Audio decoder init failed for "
|
||||
"%s:%s\n", sel->family, sel->decoder);
|
||||
}
|
||||
|
||||
if (d_audio->ad_driver) {
|
||||
d_audio->decoder_desc =
|
||||
talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family,
|
||||
if (sh_audio->initialized) {
|
||||
sh_audio->gsh->decoder_desc =
|
||||
talloc_asprintf(NULL, "%s [%s:%s]", decoder->desc, decoder->family,
|
||||
decoder->decoder);
|
||||
MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc);
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_INFO, "Selected audio codec: %s\n",
|
||||
sh_audio->gsh->decoder_desc);
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V,
|
||||
"AUDIO: %d Hz, %d ch, %s\n",
|
||||
sh_audio->samplerate, sh_audio->channels.num,
|
||||
af_fmt2str_short(sh_audio->sample_format));
|
||||
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
||||
"ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n",
|
||||
sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels.num);
|
||||
} else {
|
||||
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
|
||||
d_audio->header->codec ? d_audio->header->codec : "<unknown>");
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"Failed to initialize an audio decoder for codec '%s'.\n",
|
||||
sh_audio->gsh->codec ? sh_audio->gsh->codec : "<unknown>");
|
||||
}
|
||||
|
||||
talloc_free(list);
|
||||
return !!d_audio->ad_driver;
|
||||
return sh_audio->initialized;
|
||||
}
|
||||
|
||||
void audio_uninit(struct dec_audio *d_audio)
|
||||
void uninit_audio(sh_audio_t *sh_audio)
|
||||
{
|
||||
if (!d_audio)
|
||||
return;
|
||||
MP_VERBOSE(d_audio, "Uninit audio filters...\n");
|
||||
af_destroy(d_audio->afilter);
|
||||
uninit_decoder(d_audio);
|
||||
talloc_free(d_audio);
|
||||
}
|
||||
|
||||
/* Decode packets until we know the audio format. Then reinit the buffer.
|
||||
* Returns AD_OK on success, negative AD_* code otherwise.
|
||||
* Also returns AD_OK if already initialized (and does nothing).
|
||||
*/
|
||||
int initial_audio_decode(struct dec_audio *da)
|
||||
{
|
||||
while (!da->waiting) {
|
||||
int ret = da->ad_driver->decode_packet(da, &da->waiting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (sh_audio->afilter) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
|
||||
af_destroy(sh_audio->afilter);
|
||||
sh_audio->afilter = NULL;
|
||||
}
|
||||
talloc_steal(da, da->waiting);
|
||||
da->decode_format = *da->waiting;
|
||||
return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
|
||||
if (sh_audio->initialized) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Uninit audio.\n");
|
||||
sh_audio->ad_driver->uninit(sh_audio);
|
||||
sh_audio->initialized = 0;
|
||||
}
|
||||
talloc_free(sh_audio->gsh->decoder_desc);
|
||||
sh_audio->gsh->decoder_desc = NULL;
|
||||
av_freep(&sh_audio->a_buffer);
|
||||
}
|
||||
|
||||
/* Try to get at least minsamples decoded+filtered samples in outbuf
|
||||
* (total length including possible existing data).
|
||||
* Return 0 on success, or negative AD_* error code.
|
||||
* In the former case outbuf has at least minsamples buffered on return.
|
||||
* In case of EOF/error it might or might not be. */
|
||||
int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf,
|
||||
int minsamples)
|
||||
|
||||
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
|
||||
int *out_samplerate, struct mp_chmap *out_channels,
|
||||
int *out_format)
|
||||
{
|
||||
struct af_stream *afs = da->afilter;
|
||||
if (afs->initialized < 1)
|
||||
return AD_ERR;
|
||||
if (!sh_audio->afilter)
|
||||
sh_audio->afilter = af_new(sh_audio->opts);
|
||||
struct af_stream *afs = sh_audio->afilter;
|
||||
|
||||
MP_STATS(da, "start audio");
|
||||
// input format: same as codec's output format:
|
||||
afs->input.rate = in_samplerate;
|
||||
mp_audio_set_channels(&afs->input, &sh_audio->channels);
|
||||
mp_audio_set_format(&afs->input, sh_audio->sample_format);
|
||||
|
||||
int res = 0;
|
||||
while (res >= 0 && minsamples >= 0) {
|
||||
int buffered = mp_audio_buffer_samples(outbuf);
|
||||
if (minsamples < buffered)
|
||||
// output format: same as ao driver's input format (if missing, fallback to input)
|
||||
afs->output.rate = *out_samplerate;
|
||||
mp_audio_set_channels(&afs->output, out_channels);
|
||||
mp_audio_set_format(&afs->output, *out_format);
|
||||
|
||||
char *s_from = mp_audio_config_to_str(&afs->input);
|
||||
char *s_to = mp_audio_config_to_str(&afs->output);
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
|
||||
"Building audio filter chain for %s -> %s...\n", s_from, s_to);
|
||||
talloc_free(s_from);
|
||||
talloc_free(s_to);
|
||||
|
||||
// let's autoprobe it!
|
||||
if (af_init(afs) != 0) {
|
||||
af_destroy(afs);
|
||||
sh_audio->afilter = NULL;
|
||||
return 0; // failed :(
|
||||
}
|
||||
|
||||
*out_samplerate = afs->output.rate;
|
||||
*out_channels = afs->output.channels;
|
||||
*out_format = afs->output.format;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void set_min_out_buffer_size(struct bstr *outbuf, int len)
|
||||
{
|
||||
size_t oldlen = talloc_get_size(outbuf->start);
|
||||
if (oldlen < len) {
|
||||
assert(outbuf->start); // talloc context should be already set
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size "
|
||||
"from %zd to %d\n", oldlen, len);
|
||||
outbuf->start = talloc_realloc_size(NULL, outbuf->start, len);
|
||||
}
|
||||
}
|
||||
|
||||
static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
|
||||
{
|
||||
assert(len - 1 + sh->audio_out_minsize <= sh->a_buffer_size);
|
||||
|
||||
int error = 0;
|
||||
|
||||
// Decode more bytes if needed
|
||||
int old_samplerate = sh->samplerate;
|
||||
struct mp_chmap old_channels = sh->channels;
|
||||
int old_sample_format = sh->sample_format;
|
||||
while (sh->a_buffer_len < len) {
|
||||
unsigned char *buf = sh->a_buffer + sh->a_buffer_len;
|
||||
int minlen = len - sh->a_buffer_len;
|
||||
int maxlen = sh->a_buffer_size - sh->a_buffer_len;
|
||||
int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen);
|
||||
int format_change = sh->samplerate != old_samplerate
|
||||
|| !mp_chmap_equals(&sh->channels, &old_channels)
|
||||
|| sh->sample_format != old_sample_format;
|
||||
if (ret <= 0 || format_change) {
|
||||
error = format_change ? -2 : -1;
|
||||
// samples from format-changing call get discarded too
|
||||
len = sh->a_buffer_len;
|
||||
break;
|
||||
|
||||
res = 0;
|
||||
|
||||
struct mp_audio *mpa = da->waiting;
|
||||
if (!mpa)
|
||||
res = da->ad_driver->decode_packet(da, &mpa);
|
||||
|
||||
if (res != AD_EOF) {
|
||||
if (res < 0)
|
||||
break;
|
||||
if (!mpa )
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mpa) {
|
||||
da->pts_offset += mpa->samples;
|
||||
da->decode_format = *mpa;
|
||||
mp_audio_set_null_data(&da->decode_format);
|
||||
// On format change, make sure to drain the filter chain.
|
||||
if (!mp_audio_config_equals(&afs->input, mpa)) {
|
||||
res = AD_NEW_FMT;
|
||||
da->waiting = talloc_steal(da, mpa);
|
||||
mpa = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (mpa)
|
||||
da->waiting = NULL;
|
||||
|
||||
if (af_filter(afs, mpa, outbuf) < 0)
|
||||
return AD_ERR;
|
||||
sh->a_buffer_len += ret;
|
||||
}
|
||||
|
||||
MP_STATS(da, "end audio");
|
||||
// Filter
|
||||
struct mp_audio filter_input = {
|
||||
.audio = sh->a_buffer,
|
||||
.len = len,
|
||||
.rate = sh->samplerate,
|
||||
};
|
||||
mp_audio_set_format(&filter_input, sh->sample_format);
|
||||
mp_audio_set_channels(&filter_input, &sh->channels);
|
||||
|
||||
return res;
|
||||
struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
|
||||
if (!filter_output)
|
||||
return -1;
|
||||
set_min_out_buffer_size(outbuf, outbuf->len + filter_output->len);
|
||||
memcpy(outbuf->start + outbuf->len, filter_output->audio,
|
||||
filter_output->len);
|
||||
outbuf->len += filter_output->len;
|
||||
|
||||
// remove processed data from decoder buffer:
|
||||
sh->a_buffer_len -= len;
|
||||
memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void audio_reset_decoding(struct dec_audio *d_audio)
|
||||
/* Try to get at least minlen decoded+filtered bytes in outbuf
|
||||
* (total length including possible existing data).
|
||||
* Return 0 on success, -1 on error/EOF (not distinguished).
|
||||
* In the former case outbuf->len is always >= minlen on return.
|
||||
* In case of EOF/error it might or might not be.
|
||||
* Outbuf.start must be talloc-allocated, and will be reallocated
|
||||
* if needed to fit all filter output. */
|
||||
int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen)
|
||||
{
|
||||
if (d_audio->ad_driver)
|
||||
d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
|
||||
af_control_all(d_audio->afilter, AF_CONTROL_RESET, NULL);
|
||||
d_audio->pts = MP_NOPTS_VALUE;
|
||||
d_audio->pts_offset = 0;
|
||||
if (d_audio->waiting) {
|
||||
talloc_free(d_audio->waiting);
|
||||
d_audio->waiting = NULL;
|
||||
// Indicates that a filter seems to be buffering large amounts of data
|
||||
int huge_filter_buffer = 0;
|
||||
// Decoded audio must be cut at boundaries of this many bytes
|
||||
int unitsize = sh_audio->channels.num * sh_audio->samplesize * 16;
|
||||
|
||||
/* Filter output size will be about filter_multiplier times input size.
|
||||
* If some filter buffers audio in big blocks this might only hold
|
||||
* as average over time. */
|
||||
double filter_multiplier = af_calc_filter_multiplier(sh_audio->afilter);
|
||||
|
||||
/* If the decoder set audio_out_minsize then it can do the equivalent of
|
||||
* "while (output_len < target_len) output_len += audio_out_minsize;",
|
||||
* so we must guarantee there is at least audio_out_minsize-1 bytes
|
||||
* more space in the output buffer than the minimum length we try to
|
||||
* decode. */
|
||||
int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize;
|
||||
if (!unitsize)
|
||||
return -1;
|
||||
max_decode_len -= max_decode_len % unitsize;
|
||||
|
||||
while (minlen >= 0 && outbuf->len < minlen) {
|
||||
// + some extra for possible filter buffering
|
||||
int declen = (minlen - outbuf->len) / filter_multiplier + (unitsize << 5);
|
||||
if (huge_filter_buffer)
|
||||
/* Some filter must be doing significant buffering if the estimated
|
||||
* input length didn't produce enough output from filters.
|
||||
* Feed the filters 2k bytes at a time until we have enough output.
|
||||
* Very small amounts could make filtering inefficient while large
|
||||
* amounts can make MPlayer demux the file unnecessarily far ahead
|
||||
* to get audio data and buffer video frames in memory while doing
|
||||
* so. However the performance impact of either is probably not too
|
||||
* significant as long as the value is not completely insane. */
|
||||
declen = 2000;
|
||||
declen -= declen % unitsize;
|
||||
if (declen > max_decode_len)
|
||||
declen = max_decode_len;
|
||||
else
|
||||
/* if this iteration does not fill buffer, we must have lots
|
||||
* of buffering in filters */
|
||||
huge_filter_buffer = 1;
|
||||
int res = filter_n_bytes(sh_audio, outbuf, declen);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte)
|
||||
{
|
||||
set_min_out_buffer_size(outbuf, outbuf->len + count);
|
||||
memmove(outbuf->start + count, outbuf->start, outbuf->len);
|
||||
memset(outbuf->start, byte, count);
|
||||
outbuf->len += count;
|
||||
}
|
||||
|
||||
|
||||
void resync_audio_stream(sh_audio_t *sh_audio)
|
||||
{
|
||||
sh_audio->pts = MP_NOPTS_VALUE;
|
||||
if (!sh_audio->initialized)
|
||||
return;
|
||||
sh_audio->ad_driver->control(sh_audio, ADCTRL_RESYNC_STREAM, NULL);
|
||||
}
|
||||
+11
-39
@@ -20,49 +20,21 @@
|
||||
#define MPLAYER_DEC_AUDIO_H
|
||||
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/audio.h"
|
||||
#include "demux/demux.h"
|
||||
#include "demux/stheader.h"
|
||||
|
||||
struct mp_audio_buffer;
|
||||
struct bstr;
|
||||
struct mp_decoder_list;
|
||||
|
||||
struct dec_audio {
|
||||
struct mp_log *log;
|
||||
struct MPOpts *opts;
|
||||
struct mpv_global *global;
|
||||
const struct ad_functions *ad_driver;
|
||||
struct sh_stream *header;
|
||||
struct af_stream *afilter;
|
||||
char *decoder_desc;
|
||||
int init_retries;
|
||||
struct mp_audio_pool *pool;
|
||||
struct mp_audio decode_format;
|
||||
struct mp_audio *waiting; // used on format-change
|
||||
// set by decoder
|
||||
int bitrate; // input bitrate, can change with VBR sources
|
||||
// last known pts value in output from decoder
|
||||
double pts;
|
||||
// number of samples output by decoder after last known pts
|
||||
int pts_offset;
|
||||
// For free use by the ad_driver
|
||||
void *priv;
|
||||
};
|
||||
struct mp_decoder_list *mp_audio_decoder_list(void);
|
||||
int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders);
|
||||
int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen);
|
||||
void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte);
|
||||
void resync_audio_stream(sh_audio_t *sh_audio);
|
||||
void skip_audio_frame(sh_audio_t *sh_audio);
|
||||
void uninit_audio(sh_audio_t *sh_audio);
|
||||
|
||||
enum {
|
||||
AD_OK = 0,
|
||||
AD_ERR = -1,
|
||||
AD_EOF = -2,
|
||||
AD_NEW_FMT = -3,
|
||||
AD_WAIT = -4,
|
||||
};
|
||||
|
||||
struct mp_decoder_list *audio_decoder_list(void);
|
||||
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders);
|
||||
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
|
||||
int minsamples);
|
||||
int initial_audio_decode(struct dec_audio *d_audio);
|
||||
void audio_reset_decoding(struct dec_audio *d_audio);
|
||||
void audio_uninit(struct dec_audio *d_audio);
|
||||
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
|
||||
int *out_samplerate, struct mp_chmap *out_channels,
|
||||
int *out_format);
|
||||
|
||||
#endif /* MPLAYER_DEC_AUDIO_H */
|
||||
+180
-283
@@ -22,55 +22,48 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/global.h"
|
||||
#include "mpvcore/m_option.h"
|
||||
#include "mpvcore/m_config.h"
|
||||
|
||||
#include "options/m_option.h"
|
||||
#include "options/m_config.h"
|
||||
|
||||
#include "audio/audio_buffer.h"
|
||||
#include "af.h"
|
||||
|
||||
// Static list of filters
|
||||
extern const struct af_info af_info_dummy;
|
||||
extern const struct af_info af_info_delay;
|
||||
extern const struct af_info af_info_channels;
|
||||
extern const struct af_info af_info_format;
|
||||
extern const struct af_info af_info_force;
|
||||
extern const struct af_info af_info_volume;
|
||||
extern const struct af_info af_info_equalizer;
|
||||
extern const struct af_info af_info_pan;
|
||||
extern const struct af_info af_info_surround;
|
||||
extern const struct af_info af_info_sub;
|
||||
extern const struct af_info af_info_export;
|
||||
extern const struct af_info af_info_drc;
|
||||
extern const struct af_info af_info_extrastereo;
|
||||
extern const struct af_info af_info_lavcac3enc;
|
||||
extern const struct af_info af_info_lavrresample;
|
||||
extern const struct af_info af_info_sweep;
|
||||
extern const struct af_info af_info_hrtf;
|
||||
extern const struct af_info af_info_ladspa;
|
||||
extern const struct af_info af_info_center;
|
||||
extern const struct af_info af_info_sinesuppress;
|
||||
extern const struct af_info af_info_karaoke;
|
||||
extern const struct af_info af_info_scaletempo;
|
||||
extern const struct af_info af_info_forcespeed;
|
||||
extern const struct af_info af_info_bs2b;
|
||||
extern const struct af_info af_info_lavfi;
|
||||
extern const struct af_info af_info_convert24;
|
||||
extern const struct af_info af_info_convertsignendian;
|
||||
extern struct af_info af_info_dummy;
|
||||
extern struct af_info af_info_delay;
|
||||
extern struct af_info af_info_channels;
|
||||
extern struct af_info af_info_format;
|
||||
extern struct af_info af_info_force;
|
||||
extern struct af_info af_info_volume;
|
||||
extern struct af_info af_info_equalizer;
|
||||
extern struct af_info af_info_pan;
|
||||
extern struct af_info af_info_surround;
|
||||
extern struct af_info af_info_sub;
|
||||
extern struct af_info af_info_export;
|
||||
extern struct af_info af_info_drc;
|
||||
extern struct af_info af_info_extrastereo;
|
||||
extern struct af_info af_info_lavcac3enc;
|
||||
extern struct af_info af_info_lavrresample;
|
||||
extern struct af_info af_info_sweep;
|
||||
extern struct af_info af_info_hrtf;
|
||||
extern struct af_info af_info_ladspa;
|
||||
extern struct af_info af_info_center;
|
||||
extern struct af_info af_info_sinesuppress;
|
||||
extern struct af_info af_info_karaoke;
|
||||
extern struct af_info af_info_scaletempo;
|
||||
extern struct af_info af_info_bs2b;
|
||||
extern struct af_info af_info_lavfi;
|
||||
|
||||
static const struct af_info *const filter_list[] = {
|
||||
static struct af_info* filter_list[] = {
|
||||
&af_info_dummy,
|
||||
&af_info_delay,
|
||||
&af_info_channels,
|
||||
&af_info_format,
|
||||
&af_info_force,
|
||||
&af_info_volume,
|
||||
&af_info_equalizer,
|
||||
&af_info_pan,
|
||||
&af_info_surround,
|
||||
&af_info_sub,
|
||||
#if HAVE_SYS_MMAN_H
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
&af_info_export,
|
||||
#endif
|
||||
&af_info_drc,
|
||||
@@ -79,23 +72,21 @@ static const struct af_info *const filter_list[] = {
|
||||
&af_info_lavrresample,
|
||||
&af_info_sweep,
|
||||
&af_info_hrtf,
|
||||
#if HAVE_LADSPA
|
||||
#ifdef CONFIG_LADSPA
|
||||
&af_info_ladspa,
|
||||
#endif
|
||||
&af_info_center,
|
||||
&af_info_sinesuppress,
|
||||
&af_info_karaoke,
|
||||
&af_info_forcespeed,
|
||||
&af_info_scaletempo,
|
||||
#if HAVE_LIBBS2B
|
||||
#ifdef CONFIG_LIBBS2B
|
||||
&af_info_bs2b,
|
||||
#endif
|
||||
#if HAVE_LIBAVFILTER
|
||||
#ifdef CONFIG_AF_LAVFI
|
||||
&af_info_lavfi,
|
||||
#endif
|
||||
// Must come last, because they're fallback format conversion filter
|
||||
&af_info_convert24,
|
||||
&af_info_convertsignendian,
|
||||
// Must come last, because it's the fallback format conversion filter
|
||||
&af_info_format,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -118,12 +109,16 @@ static bool get_desc(struct m_obj_desc *dst, int index)
|
||||
const struct m_obj_list af_obj_list = {
|
||||
.get_desc = get_desc,
|
||||
.description = "audio filters",
|
||||
.aliases = {
|
||||
{"force", "format"},
|
||||
{0}
|
||||
},
|
||||
.legacy_hacks = true, // many filters have custom option parsing
|
||||
};
|
||||
|
||||
static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
|
||||
{
|
||||
return a->format == b->format
|
||||
&& mp_chmap_equals(&a->channels, &b->channels)
|
||||
&& a->rate == b->rate;
|
||||
}
|
||||
|
||||
static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src)
|
||||
{
|
||||
if (dst->format == AF_FORMAT_UNKNOWN)
|
||||
@@ -138,7 +133,7 @@ static int input_control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT:
|
||||
assert(arg == &((struct af_stream *)af->priv)->input);
|
||||
assert(arg == &((struct af_stream *)af->setup)->input);
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
@@ -146,7 +141,7 @@ static int input_control(struct af_instance* af, int cmd, void* arg)
|
||||
|
||||
static int output_control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
struct af_stream *s = af->priv;
|
||||
struct af_stream *s = af->setup;
|
||||
struct mp_audio *output = &s->output;
|
||||
struct mp_audio *filter_output = &s->filter_output;
|
||||
|
||||
@@ -158,15 +153,15 @@ static int output_control(struct af_instance* af, int cmd, void* arg)
|
||||
*filter_output = *output;
|
||||
af_copy_unset_fields(filter_output, in);
|
||||
*in = *filter_output;
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
return af_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int dummy_filter(struct af_instance* af, struct mp_audio* data, int f)
|
||||
static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
return 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Function for creating a new filter of type name.The name may
|
||||
@@ -176,7 +171,8 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
|
||||
{
|
||||
struct m_obj_desc desc;
|
||||
if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) {
|
||||
MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
|
||||
mp_tmsg(MSGT_VFILTER, MSGL_ERR,
|
||||
"Couldn't find audio filter '%s'.\n", name);
|
||||
return NULL;
|
||||
}
|
||||
const struct af_info *info = desc.p;
|
||||
@@ -185,38 +181,40 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
|
||||
if (info->flags & AF_FLAGS_NOT_REENTRANT) {
|
||||
for (struct af_instance *cur = s->first; cur; cur = cur->next) {
|
||||
if (cur->info == info) {
|
||||
MP_ERR(s, "There can only be one "
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one "
|
||||
"instance of the filter '%s' in each stream\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_VERBOSE(s, "Adding filter %s \n", name);
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name);
|
||||
|
||||
struct af_instance *af = talloc_zero(NULL, struct af_instance);
|
||||
*af = (struct af_instance) {
|
||||
.info = info,
|
||||
.mul = 1,
|
||||
.data = talloc_zero(af, struct mp_audio),
|
||||
.log = mp_log_new(af, s->log, name),
|
||||
.replaygain_data = s->replaygain_data,
|
||||
};
|
||||
struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
|
||||
if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
|
||||
struct m_config *config = m_config_from_obj_desc(af, &desc);
|
||||
if (m_config_initialize_obj(config, &desc, &af->priv, &args) < 0)
|
||||
goto error;
|
||||
if (m_config_set_obj_params(config, args) < 0)
|
||||
goto error;
|
||||
af->priv = config->optstruct;
|
||||
|
||||
// Initialize the new filter
|
||||
if (af->info->open(af) != AF_OK)
|
||||
goto error;
|
||||
if (args && af->control) {
|
||||
// Single option string for old filters
|
||||
char *opts = (char *)args; // m_config_initialize_obj did this
|
||||
assert(!af->priv);
|
||||
if (af->control(af, AF_CONTROL_COMMAND_LINE, opts) <= AF_ERROR)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return af;
|
||||
|
||||
error:
|
||||
MP_ERR(s, "Couldn't create or open audio filter '%s'\n", name);
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR,
|
||||
"[libaf] Couldn't create or open audio filter '%s'\n", name);
|
||||
talloc_free(af);
|
||||
return NULL;
|
||||
}
|
||||
@@ -277,14 +275,17 @@ static void af_remove(struct af_stream *s, struct af_instance *af)
|
||||
return;
|
||||
|
||||
// Print friendly message
|
||||
MP_VERBOSE(s, "Removing filter %s \n", af->info->name);
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",
|
||||
af->info->name);
|
||||
|
||||
// Notify filter before changing anything
|
||||
af->control(af, AF_CONTROL_PRE_DESTROY, 0);
|
||||
|
||||
// Detach pointers
|
||||
af->prev->next = af->next;
|
||||
af->next->prev = af->prev;
|
||||
|
||||
if (af->uninit)
|
||||
af->uninit(af);
|
||||
af->uninit(af);
|
||||
talloc_free(af);
|
||||
}
|
||||
|
||||
@@ -302,22 +303,27 @@ repeat:
|
||||
static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
|
||||
int msg_level)
|
||||
{
|
||||
MP_MSG(s, msg_level, "Audio filter chain:\n");
|
||||
mp_msg(MSGT_AFILTER, msg_level, "Audio filter chain:\n");
|
||||
|
||||
struct af_instance *af = s->first;
|
||||
while (af) {
|
||||
char b[128] = {0};
|
||||
mp_snprintf_cat(b, sizeof(b), " [%s] ", af->info->name);
|
||||
if (af->data)
|
||||
mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data));
|
||||
mp_msg(MSGT_AFILTER, msg_level, " [%s] ", af->info->name);
|
||||
if (af->data) {
|
||||
char *info = mp_audio_config_to_str(af->data);
|
||||
mp_msg(MSGT_AFILTER, msg_level, "%s", info);
|
||||
talloc_free(info);
|
||||
}
|
||||
if (af == at)
|
||||
mp_snprintf_cat(b, sizeof(b), " <-");
|
||||
MP_MSG(s, msg_level, "%s\n", b);
|
||||
mp_msg(MSGT_AFILTER, msg_level, " <-");
|
||||
mp_msg(MSGT_AFILTER, msg_level, "\n");
|
||||
|
||||
af = af->next;
|
||||
}
|
||||
|
||||
MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output));
|
||||
mp_msg(MSGT_AFILTER, msg_level, " [ao] ");
|
||||
char *info = mp_audio_config_to_str(&s->output);
|
||||
mp_msg(MSGT_AFILTER, msg_level, "%s\n", info);
|
||||
talloc_free(info);
|
||||
}
|
||||
|
||||
static int af_count_filters(struct af_stream *s)
|
||||
@@ -328,76 +334,14 @@ static int af_count_filters(struct af_stream *s)
|
||||
return count;
|
||||
}
|
||||
|
||||
// Finds the first conversion filter on the way from srcfmt to dstfmt.
|
||||
// Conversions form a DAG: each node is a format/filter pair, and possible
|
||||
// conversions are edges. We search the DAG for the shortest path.
|
||||
// Some cases visit the same filter multiple times, but with different formats
|
||||
// (like u24le->s8), so one node per format or filter separate is not enough.
|
||||
// Returns the filter and dest. format for the first conversion step.
|
||||
// (So we know what conversion filter with what format to insert next.)
|
||||
static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
|
||||
static char *af_find_conversion_filter(int srcfmt, int dstfmt)
|
||||
{
|
||||
#define MAX_NODES (64 * 32)
|
||||
int num_fmt = 0, num_filt = 0;
|
||||
for (int n = 0; filter_list[n]; n++)
|
||||
num_filt = n + 1;
|
||||
for (int n = 0; af_fmtstr_table[n].format; n++)
|
||||
num_fmt = n + 1;
|
||||
|
||||
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)
|
||||
distance[n] = 0;
|
||||
for (int n = 0; filter_list[n]; n++) {
|
||||
struct af_info *af = filter_list[n];
|
||||
if (af->test_conversion && af->test_conversion(srcfmt, dstfmt))
|
||||
return (char *)af->name;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int next = -1;
|
||||
for (int n = 0; n < num_nodes; n++) {
|
||||
if (!visited[n] && (next < 0 || (distance[n] < distance[next])))
|
||||
next = n;
|
||||
}
|
||||
if (next < 0 || distance[next] == 255)
|
||||
return NULL;
|
||||
visited[next] = true;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int n = 0; filter_list[n]; n++) {
|
||||
const struct af_info *af = filter_list[n];
|
||||
if (!af->test_conversion)
|
||||
continue;
|
||||
for (int i = 0; af_fmtstr_table[i].format; i++) {
|
||||
if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format,
|
||||
af_fmtstr_table[i].format))
|
||||
{
|
||||
int other = n * num_fmt + i;
|
||||
int ndist = distance[next] + 1;
|
||||
if (ndist < distance[other]) {
|
||||
distance[other] = ndist;
|
||||
previous[other] = next + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
#undef MAX_NODES
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool af_is_conversion_filter(struct af_instance *af)
|
||||
@@ -408,7 +352,7 @@ static bool af_is_conversion_filter(struct af_instance *af)
|
||||
// in is what af can take as input - insert a conversion filter if the actual
|
||||
// input format doesn't match what af expects.
|
||||
// Returns:
|
||||
// AF_OK: must call af_reinit() or equivalent, format matches (or is closer)
|
||||
// AF_OK: must call af_reinit() or equivalent, format matches
|
||||
// AF_FALSE: nothing was changed, format matches
|
||||
// else: error
|
||||
static int af_fix_format_conversion(struct af_stream *s,
|
||||
@@ -421,21 +365,18 @@ static int af_fix_format_conversion(struct af_stream *s,
|
||||
struct mp_audio actual = *prev->data;
|
||||
if (actual.format == in.format)
|
||||
return AF_FALSE;
|
||||
int dstfmt = in.format;
|
||||
char *filter = af_find_conversion_filter(actual.format, &dstfmt);
|
||||
if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) {
|
||||
*p_af = prev;
|
||||
return AF_OK;
|
||||
}
|
||||
char *filter = af_find_conversion_filter(actual.format, in.format);
|
||||
if (!filter)
|
||||
return AF_ERROR;
|
||||
if (strcmp(filter, prev->info->name) == 0) {
|
||||
if (prev->control(prev, AF_CONTROL_SET_FORMAT, &dstfmt) == AF_OK) {
|
||||
*p_af = prev;
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
struct af_instance *new = af_prepend(s, af, filter, NULL);
|
||||
if (new == NULL)
|
||||
return AF_ERROR;
|
||||
new->auto_inserted = true;
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt)))
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
|
||||
return rv;
|
||||
*p_af = new;
|
||||
return AF_OK;
|
||||
@@ -451,7 +392,7 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
|
||||
struct mp_audio actual = *prev->data;
|
||||
if (mp_chmap_equals(&actual.channels, &in.channels))
|
||||
return AF_FALSE;
|
||||
if (prev->control(prev, AF_CONTROL_SET_CHANNELS, &in.channels) == AF_OK) {
|
||||
if (prev->control(prev, AF_CONTROL_CHANNELS, &in.channels) == AF_OK) {
|
||||
*p_af = prev;
|
||||
return AF_OK;
|
||||
}
|
||||
@@ -460,7 +401,7 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
|
||||
if (new == NULL)
|
||||
return AF_ERROR;
|
||||
new->auto_inserted = true;
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_CHANNELS, &in.channels)))
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.channels)))
|
||||
return rv;
|
||||
*p_af = new;
|
||||
return AF_OK;
|
||||
@@ -475,7 +416,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
|
||||
struct mp_audio actual = *prev->data;
|
||||
if (actual.rate == in.rate)
|
||||
return AF_FALSE;
|
||||
if (prev->control(prev, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
|
||||
if (prev->control(prev, AF_CONTROL_RESAMPLE_RATE, &in.rate) == AF_OK) {
|
||||
*p_af = prev;
|
||||
return AF_OK;
|
||||
}
|
||||
@@ -484,7 +425,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
|
||||
if (new == NULL)
|
||||
return AF_ERROR;
|
||||
new->auto_inserted = true;
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate)))
|
||||
if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate)))
|
||||
return rv;
|
||||
*p_af = new;
|
||||
return AF_OK;
|
||||
@@ -498,12 +439,10 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
|
||||
// In that case, you should always rebuild the filter chain, or abort.
|
||||
static int af_reinit(struct af_stream *s)
|
||||
{
|
||||
remove_auto_inserted_filters(s);
|
||||
// Start with the second filter, as the first filter is the special input
|
||||
// filter which needs no initialization.
|
||||
struct af_instance *af = s->first->next;
|
||||
// Up to 7 retries per filter (channel, rate, 5x format conversions)
|
||||
int max_retry = af_count_filters(s) * 7;
|
||||
int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter
|
||||
int retry = 0;
|
||||
while (af) {
|
||||
if (retry >= max_retry)
|
||||
@@ -512,18 +451,12 @@ static int af_reinit(struct af_stream *s)
|
||||
// Check if this is the first filter
|
||||
struct mp_audio in = *af->prev->data;
|
||||
// Reset just in case...
|
||||
mp_audio_set_null_data(&in);
|
||||
|
||||
if (!mp_audio_config_valid(&in))
|
||||
goto error;
|
||||
in.audio = NULL;
|
||||
in.len = 0;
|
||||
|
||||
int rv = af->control(af, AF_CONTROL_REINIT, &in);
|
||||
if (rv == AF_OK && !mp_audio_config_equals(&in, af->prev->data))
|
||||
rv = AF_FALSE; // conversion filter needed
|
||||
switch (rv) {
|
||||
case AF_OK:
|
||||
if (!mp_audio_config_valid(af->data))
|
||||
goto error;
|
||||
af = af->next;
|
||||
break;
|
||||
case AF_FALSE: { // Configuration filter is needed
|
||||
@@ -551,30 +484,26 @@ static int af_reinit(struct af_stream *s)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MP_ERR(s, "Reinitialization did not work, "
|
||||
"audio filter '%s' returned error code %i\n",
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not "
|
||||
"work, audio filter '%s' returned error code %i\n",
|
||||
af->info->name, rv);
|
||||
goto error;
|
||||
af_print_filter_chain(s, af, MSGL_ERR);
|
||||
return AF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
af_print_filter_chain(s, NULL, MSGL_V);
|
||||
|
||||
/* Set previously unset fields in s->output to those of the filter chain
|
||||
* output. This is used to make the output format fixed, and even if you
|
||||
* insert new filters or change the input format, the output format won't
|
||||
* change. (Audio outputs generally can't change format at runtime.) */
|
||||
af_copy_unset_fields(&s->output, &s->filter_output);
|
||||
if (mp_audio_config_equals(&s->output, &s->filter_output)) {
|
||||
s->initialized = 1;
|
||||
af_print_filter_chain(s, NULL, MSGL_V);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
goto error;
|
||||
return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
|
||||
|
||||
negotiate_error:
|
||||
MP_ERR(s, "Unable to convert audio input format to output format.\n");
|
||||
error:
|
||||
s->initialized = -1;
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
|
||||
"This error should never occur, please send a bug report.\n");
|
||||
af_print_filter_chain(s, af, MSGL_ERR);
|
||||
return AF_ERROR;
|
||||
}
|
||||
@@ -584,36 +513,34 @@ void af_uninit(struct af_stream *s)
|
||||
{
|
||||
while (s->first->next && s->first->next != s->last)
|
||||
af_remove(s, s->first->next);
|
||||
s->initialized = 0;
|
||||
}
|
||||
|
||||
struct af_stream *af_new(struct mpv_global *global)
|
||||
struct af_stream *af_new(struct MPOpts *opts)
|
||||
{
|
||||
struct af_stream *s = talloc_zero(NULL, struct af_stream);
|
||||
static const struct af_info in = { .name = "in" };
|
||||
static struct af_info in = { .name = "in" };
|
||||
s->first = talloc(s, struct af_instance);
|
||||
*s->first = (struct af_instance) {
|
||||
.info = &in,
|
||||
.control = input_control,
|
||||
.filter = dummy_filter,
|
||||
.priv = s,
|
||||
.play = dummy_play,
|
||||
.setup = s,
|
||||
.data = &s->input,
|
||||
.mul = 1.0,
|
||||
};
|
||||
static const struct af_info out = { .name = "out" };
|
||||
static struct af_info out = { .name = "out" };
|
||||
s->last = talloc(s, struct af_instance);
|
||||
*s->last = (struct af_instance) {
|
||||
.info = &out,
|
||||
.control = output_control,
|
||||
.filter = dummy_filter,
|
||||
.priv = s,
|
||||
.play = dummy_play,
|
||||
.setup = s,
|
||||
.data = &s->filter_output,
|
||||
.mul = 1.0,
|
||||
};
|
||||
s->first->next = s->last;
|
||||
s->last->prev = s->first;
|
||||
s->opts = global->opts;
|
||||
s->log = mp_log_new(s, global->log, "!af");
|
||||
s->opts = opts;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -626,7 +553,7 @@ void af_destroy(struct af_stream *s)
|
||||
/* Initialize the stream "s". This function creates a new filter list
|
||||
if necessary according to the values set in input and output. Input
|
||||
and output should contain the format of the current movie and the
|
||||
format of the preferred output respectively. The function is
|
||||
formate of the preferred output respectively. The function is
|
||||
reentrant i.e. if called with an already initialized stream the
|
||||
stream will be reinitialized.
|
||||
If one of the prefered output parameters is 0 the one that needs
|
||||
@@ -634,29 +561,32 @@ void af_destroy(struct af_stream *s)
|
||||
The return value is 0 if success and -1 if failure */
|
||||
int af_init(struct af_stream *s)
|
||||
{
|
||||
// Sanity check
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
// Precaution in case caller is misbehaving
|
||||
mp_audio_set_null_data(&s->input);
|
||||
mp_audio_set_null_data(&s->output);
|
||||
s->input.audio = s->output.audio = NULL;
|
||||
s->input.len = s->output.len = 0;
|
||||
|
||||
// Check if this is the first call
|
||||
if (s->first->next == s->last) {
|
||||
// Add all filters in the list (if there are any)
|
||||
struct m_obj_settings *list = s->opts->af_settings;
|
||||
for (int i = 0; list && list[i].name; i++) {
|
||||
struct af_instance *af =
|
||||
af_prepend(s, s->last, list[i].name, list[i].attribs);
|
||||
if (!af) {
|
||||
if (!af_prepend(s, s->last, list[i].name, list[i].attribs)) {
|
||||
af_uninit(s);
|
||||
s->initialized = -1;
|
||||
return -1;
|
||||
}
|
||||
af->label = talloc_strdup(af, list[i].label);
|
||||
}
|
||||
}
|
||||
|
||||
remove_auto_inserted_filters(s);
|
||||
|
||||
if (af_reinit(s) != AF_OK) {
|
||||
// Something is stuffed audio out will not work
|
||||
MP_ERR(s, "Could not create audio filter chain.\n");
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "Could not create audio filter chain.\n");
|
||||
af_uninit(s);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -669,6 +599,9 @@ int af_init(struct af_stream *s)
|
||||
struct af_instance *af_add(struct af_stream *s, char *name, char **args)
|
||||
{
|
||||
struct af_instance *new;
|
||||
// Sanity check
|
||||
if (!s || !s->first || !name)
|
||||
return NULL;
|
||||
// Insert the filter somewhere nice
|
||||
if (af_is_conversion_filter(s->first->next))
|
||||
new = af_append(s, s->first->next, name, args);
|
||||
@@ -679,93 +612,43 @@ struct af_instance *af_add(struct af_stream *s, char *name, char **args)
|
||||
|
||||
// Reinitalize the filter list
|
||||
if (af_reinit(s) != AF_OK) {
|
||||
af_remove(s, new);
|
||||
if (af_reinit(s) != AF_OK) {
|
||||
af_uninit(s);
|
||||
af_init(s);
|
||||
}
|
||||
af_uninit(s);
|
||||
af_init(s);
|
||||
return NULL;
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
struct af_instance *af_find_by_label(struct af_stream *s, char *label)
|
||||
{
|
||||
for (struct af_instance *af = s->first; af; af = af->next) {
|
||||
if (af->label && strcmp(af->label, label) == 0)
|
||||
return af;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the first filter that matches this name. Return number of filters
|
||||
* removed (0, 1), or a negative error code if reinit after removing failed.
|
||||
*/
|
||||
int af_remove_by_label(struct af_stream *s, char *label)
|
||||
{
|
||||
struct af_instance *af = af_find_by_label(s, label);
|
||||
if (!af)
|
||||
return 0;
|
||||
af_remove(s, af);
|
||||
if (af_reinit(s) != AF_OK) {
|
||||
af_uninit(s);
|
||||
af_init(s);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Feed "data" to the chain, and write results to output. "data" needs to be
|
||||
* a refcounted frame, although refcounting is not used yet.
|
||||
* data==NULL means EOF.
|
||||
*/
|
||||
int af_filter(struct af_stream *s, struct mp_audio *data,
|
||||
struct mp_audio_buffer *output)
|
||||
// Filter data chunk through the filters in the list
|
||||
struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data)
|
||||
{
|
||||
struct af_instance *af = s->first;
|
||||
assert(s->initialized > 0);
|
||||
int flags = 0;
|
||||
int r = 0;
|
||||
struct mp_audio tmp;
|
||||
char dummy[MP_NUM_CHANNELS];
|
||||
if (data) {
|
||||
assert(mp_audio_config_equals(af->data, data));
|
||||
r = mp_audio_make_writeable(data);
|
||||
} else {
|
||||
data = &tmp;
|
||||
*data = *(af->data);
|
||||
mp_audio_set_null_data(data);
|
||||
flags = AF_FILTER_FLAG_EOF;
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
data->planes[n] = &dummy[n];
|
||||
}
|
||||
if (r < 0)
|
||||
goto done;
|
||||
struct mp_audio frame = *data;
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
frame.allocated[n] = NULL;
|
||||
// Iterate through all filters
|
||||
while (af) {
|
||||
r = af->filter(af, &frame, flags);
|
||||
if (r < 0)
|
||||
goto done;
|
||||
assert(mp_audio_config_equals(af->data, &frame));
|
||||
do {
|
||||
if (data->len <= 0)
|
||||
break;
|
||||
data = af->play(af, data);
|
||||
af = af->next;
|
||||
}
|
||||
mp_audio_buffer_append(output, &frame);
|
||||
|
||||
done:
|
||||
if (data != &tmp)
|
||||
talloc_free(data);
|
||||
return r;
|
||||
} while (af && data);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Calculate average ratio of filter output samples to input samples.
|
||||
// e.g: num_output_samples = mul * num_input_samples
|
||||
/* Calculate the minimum output buffer size for given input data d
|
||||
* when using the RESIZE_LOCAL_BUFFER macro. The +t+1 part ensures the
|
||||
* value is >= len*mul rounded upwards to whole samples even if the
|
||||
* double 'mul' is inexact. */
|
||||
int af_lencalc(double mul, struct mp_audio *d)
|
||||
{
|
||||
int t = d->bps * d->nch;
|
||||
return d->len * mul + t + 1;
|
||||
}
|
||||
|
||||
// Calculate average ratio of filter output size to input size
|
||||
double af_calc_filter_multiplier(struct af_stream *s)
|
||||
{
|
||||
struct af_instance *af = s->first;
|
||||
double mul = 1;
|
||||
// Iterate through all filters and calculate total multiplication factor
|
||||
do {
|
||||
mul *= af->mul;
|
||||
af = af->next;
|
||||
@@ -774,20 +657,41 @@ double af_calc_filter_multiplier(struct af_stream *s)
|
||||
return mul;
|
||||
}
|
||||
|
||||
/* Calculate the total delay [seconds of output] caused by the filters */
|
||||
/* Calculate the total delay [bytes output] caused by the filters */
|
||||
double af_calc_delay(struct af_stream *s)
|
||||
{
|
||||
struct af_instance *af = s->first;
|
||||
double delay = 0.0;
|
||||
register double delay = 0.0;
|
||||
// Iterate through all filters
|
||||
while (af) {
|
||||
delay += af->delay;
|
||||
delay *= af->mul;
|
||||
af = af->next;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
/* Send control to all filters, starting with the last until one accepts the
|
||||
* command with AF_OK. Return the accepting filter. */
|
||||
/* Helper function called by the macro with the same name this
|
||||
function should not be called directly */
|
||||
int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
// Calculate new length
|
||||
register int len = af_lencalc(af->mul, data);
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
|
||||
"old len = %i, new len = %i\n", af->info->name, af->data->len, len);
|
||||
// If there is a buffer free it
|
||||
free(af->data->audio);
|
||||
// Create new buffer and check that it is OK
|
||||
af->data->audio = malloc(len);
|
||||
if (!af->data->audio) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
af->data->len = len;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
// documentation in af.h
|
||||
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
|
||||
{
|
||||
int res = AF_UNKNOWN;
|
||||
@@ -800,10 +704,3 @@ struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Send control to all filters. Never stop, even if a filter returns AF_OK. */
|
||||
void af_control_all(struct af_stream *s, int cmd, void *arg)
|
||||
{
|
||||
for (struct af_instance *af = s->first; af; af = af->next)
|
||||
af->control(af, cmd, arg);
|
||||
}
|
||||
+193
-60
@@ -21,16 +21,17 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "options/options.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "mpvcore/options.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/audio.h"
|
||||
#include "common/msg.h"
|
||||
#include "control.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
|
||||
struct af_instance;
|
||||
struct mpv_global;
|
||||
|
||||
// Number of channels
|
||||
#define AF_NCH MP_NUM_CHANNELS
|
||||
@@ -39,14 +40,13 @@ struct mpv_global;
|
||||
#define AF_FLAGS_REENTRANT 0x00000000
|
||||
#define AF_FLAGS_NOT_REENTRANT 0x00000001
|
||||
|
||||
// Flags for af->filter()
|
||||
#define AF_FILTER_FLAG_EOF 1
|
||||
|
||||
/* Audio filter information not specific for current instance, but for
|
||||
a specific filter */
|
||||
struct af_info {
|
||||
const char *info;
|
||||
const char *name;
|
||||
const char *author;
|
||||
const char *comment;
|
||||
const int flags;
|
||||
int (*open)(struct af_instance *vf);
|
||||
bool (*test_conversion)(int src_format, int dst_format);
|
||||
@@ -58,46 +58,40 @@ struct af_info {
|
||||
// Linked list of audio filters
|
||||
struct af_instance {
|
||||
const struct af_info *info;
|
||||
struct mp_log *log;
|
||||
struct replaygain_data *replaygain_data;
|
||||
int (*control)(struct af_instance *af, int cmd, void *arg);
|
||||
void (*uninit)(struct af_instance *af);
|
||||
/* flags is a bit mask of AF_FILTER_FLAG_* values
|
||||
* returns 0 on success, negative value on error
|
||||
*/
|
||||
int (*filter)(struct af_instance *af, struct mp_audio *data, int flags);
|
||||
struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data);
|
||||
void *setup; // old field for priv structs
|
||||
void *priv;
|
||||
struct mp_audio *data; // configuration and buffer for outgoing data stream
|
||||
struct mp_audio *data; // configuration for outgoing data stream
|
||||
struct af_instance *next;
|
||||
struct af_instance *prev;
|
||||
double delay; /* Delay caused by the filter, in seconds of audio consumed
|
||||
* without corresponding output */
|
||||
double delay; /* Delay caused by the filter, in units of bytes read without
|
||||
* corresponding output */
|
||||
double mul; /* length multiplier: how much does this instance change
|
||||
* the number of samples passed though. (Ratio of input
|
||||
* and output, e.g. mul=4 => 1 sample becomes 4 samples) .*/
|
||||
the length of the buffer. */
|
||||
bool auto_inserted; // inserted by af.c, such as conversion filters
|
||||
char *label;
|
||||
};
|
||||
|
||||
// Current audio stream
|
||||
struct af_stream {
|
||||
int initialized; // 0: no, 1: yes, -1: attempted to, but failed
|
||||
|
||||
// The first and last filter in the list
|
||||
struct af_instance *first;
|
||||
struct af_instance *last;
|
||||
// The user sets the input format (what the decoder outputs), and sets some
|
||||
// or all fields in output to the output format the AO accepts.
|
||||
// See fixup_output_format().
|
||||
struct mp_audio input;
|
||||
struct mp_audio output;
|
||||
struct mp_audio filter_output;
|
||||
|
||||
struct mp_log *log;
|
||||
struct MPOpts *opts;
|
||||
struct replaygain_data *replaygain_data;
|
||||
};
|
||||
|
||||
// Return values
|
||||
/*********************************************
|
||||
// Return values
|
||||
*/
|
||||
|
||||
#define AF_DETACH 2
|
||||
#define AF_OK 1
|
||||
#define AF_TRUE 1
|
||||
@@ -106,51 +100,190 @@ struct af_stream {
|
||||
#define AF_ERROR -2
|
||||
#define AF_FATAL -3
|
||||
|
||||
// Parameters for af_control_*
|
||||
enum af_control {
|
||||
AF_CONTROL_REINIT = 1,
|
||||
AF_CONTROL_RESET,
|
||||
AF_CONTROL_SET_RESAMPLE_RATE,
|
||||
AF_CONTROL_SET_FORMAT,
|
||||
AF_CONTROL_SET_CHANNELS,
|
||||
AF_CONTROL_SET_VOLUME,
|
||||
AF_CONTROL_GET_VOLUME,
|
||||
AF_CONTROL_SET_PAN_LEVEL,
|
||||
AF_CONTROL_SET_PAN_NOUT,
|
||||
AF_CONTROL_SET_PAN_BALANCE,
|
||||
AF_CONTROL_GET_PAN_BALANCE,
|
||||
AF_CONTROL_SET_PLAYBACK_SPEED,
|
||||
AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE,
|
||||
};
|
||||
|
||||
// Argument for AF_CONTROL_SET_PAN_LEVEL
|
||||
typedef struct af_control_ext_s {
|
||||
void* arg; // Argument
|
||||
int ch; // Chanel number
|
||||
} af_control_ext_t;
|
||||
|
||||
struct af_stream *af_new(struct mpv_global *global);
|
||||
/*********************************************
|
||||
// Export functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup af_chain Audio filter chain functions
|
||||
* \{
|
||||
* \param s filter chain
|
||||
*/
|
||||
|
||||
struct af_stream *af_new(struct MPOpts *opts);
|
||||
void af_destroy(struct af_stream *s);
|
||||
int af_init(struct af_stream *s);
|
||||
void af_uninit(struct af_stream *s);
|
||||
struct af_instance *af_add(struct af_stream *s, char *name, char **args);
|
||||
int af_remove_by_label(struct af_stream *s, char *label);
|
||||
struct af_instance *af_find_by_label(struct af_stream *s, char *label);
|
||||
struct mp_audio_buffer;
|
||||
int af_filter(struct af_stream *s, struct mp_audio *data,
|
||||
struct mp_audio_buffer *output);
|
||||
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
|
||||
void af_control_all(struct af_stream *s, int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \brief Initialize the stream "s".
|
||||
* \return 0 on success, -1 on failure
|
||||
*
|
||||
* This function creates a new filter list if necessary, according
|
||||
* to the values set in input and output. Input and output should contain
|
||||
* the format of the current movie and the format of the preferred output
|
||||
* respectively.
|
||||
* Filters to convert to the preferred output format are inserted
|
||||
* automatically, except when they are set to 0.
|
||||
* The function is reentrant i.e. if called with an already initialized
|
||||
* stream the stream will be reinitialized.
|
||||
*/
|
||||
int af_init(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief Uninit and remove all filters from audio filter chain
|
||||
*/
|
||||
void af_uninit(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief This function adds the filter "name" to the stream s.
|
||||
* \param name name of filter to add
|
||||
* \return pointer to the new filter, NULL if insert failed
|
||||
*
|
||||
* The filter will be inserted somewhere nice in the
|
||||
* list of filters (i.e. at the beginning unless the
|
||||
* first filter is the format filter (why??).
|
||||
*/
|
||||
struct af_instance *af_add(struct af_stream *s, char *name, char **args);
|
||||
|
||||
/**
|
||||
* \brief filter data chunk through the filters in the list
|
||||
* \param data data to play
|
||||
* \return resulting data
|
||||
* \ingroup af_chain
|
||||
*/
|
||||
struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data);
|
||||
|
||||
/**
|
||||
* \brief send control to all filters, starting with the last until
|
||||
* one accepts the command with AF_OK.
|
||||
* \param cmd filter control command
|
||||
* \param arg argument for filter command
|
||||
* \return the accepting filter or NULL if none was found
|
||||
*/
|
||||
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* \brief calculate average ratio of filter output lenth to input length
|
||||
* \return the ratio
|
||||
*/
|
||||
double af_calc_filter_multiplier(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief Calculate the total delay caused by the filters
|
||||
* \return delay in bytes of "missing" output
|
||||
*/
|
||||
double af_calc_delay(struct af_stream *s);
|
||||
|
||||
/** \} */ // end of af_chain group
|
||||
|
||||
// Helper functions and macros used inside the audio filters
|
||||
|
||||
/**
|
||||
* \defgroup af_filter Audio filter helper functions
|
||||
* \{
|
||||
*/
|
||||
|
||||
/* Helper function called by the macro with the same name only to be
|
||||
called from inside filters */
|
||||
int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data);
|
||||
|
||||
/* Helper function used to calculate the exact buffer length needed
|
||||
when buffers are resized. The returned length is >= than what is
|
||||
needed */
|
||||
int af_lencalc(double mul, struct mp_audio *data);
|
||||
|
||||
/**
|
||||
* \brief convert dB to gain value
|
||||
* \param n number of values to convert
|
||||
* \param in [in] values in dB, <= -200 will become 0 gain
|
||||
* \param out [out] gain values
|
||||
* \param k input values are divided by this
|
||||
* \param mi minimum dB value, input will be clamped to this
|
||||
* \param ma maximum dB value, input will be clamped to this
|
||||
* \return AF_ERROR on error, AF_OK otherwise
|
||||
*/
|
||||
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
|
||||
|
||||
/**
|
||||
* \brief convert gain value to dB
|
||||
* \param n number of values to convert
|
||||
* \param in [in] gain values, 0 wil become -200 dB
|
||||
* \param out [out] values in dB
|
||||
* \param k output values will be multiplied by this
|
||||
* \return AF_ERROR on error, AF_OK otherwise
|
||||
*/
|
||||
int af_to_dB(int n, float *in, float *out, float k);
|
||||
|
||||
/**
|
||||
* \brief convert milliseconds to sample time
|
||||
* \param n number of values to convert
|
||||
* \param in [in] values in milliseconds
|
||||
* \param out [out] sample time values
|
||||
* \param rate sample rate
|
||||
* \param mi minimum ms value, input will be clamped to this
|
||||
* \param ma maximum ms value, input will be clamped to this
|
||||
* \return AF_ERROR on error, AF_OK otherwise
|
||||
*/
|
||||
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
|
||||
|
||||
/**
|
||||
* \brief convert sample time to milliseconds
|
||||
* \param n number of values to convert
|
||||
* \param in [in] sample time values
|
||||
* \param out [out] values in milliseconds
|
||||
* \param rate sample rate
|
||||
* \return AF_ERROR on error, AF_OK otherwise
|
||||
*/
|
||||
int af_to_ms(int n, int *in, float *out, int rate);
|
||||
|
||||
/**
|
||||
* \brief test if output format matches
|
||||
* \param af audio filter
|
||||
* \param out needed format, will be overwritten by available
|
||||
* format if they do not match
|
||||
* \return AF_FALSE if formats do not match, AF_OK if they match
|
||||
*
|
||||
* compares the format, bps, rate and nch values of af->data with out
|
||||
*/
|
||||
int af_test_output(struct af_instance *af, struct mp_audio *out);
|
||||
|
||||
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
|
||||
int af_to_dB(int n, float *in, float *out, float k);
|
||||
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
|
||||
int af_to_ms(int n, int *in, float *out, int rate);
|
||||
/**
|
||||
* \brief soft clipping function using sin()
|
||||
* \param a input value
|
||||
* \return clipped value
|
||||
*/
|
||||
float af_softclip(float a);
|
||||
|
||||
/** \} */ // end of af_filter group, but more functions of this group below
|
||||
|
||||
/** Memory reallocation macro: if a local buffer is used (i.e. if the
|
||||
filter doesn't operate on the incoming buffer this macro must be
|
||||
called to ensure the buffer is big enough.
|
||||
* \ingroup af_filter
|
||||
*/
|
||||
#define RESIZE_LOCAL_BUFFER(a, d) \
|
||||
((a->data->len < af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
|
||||
|
||||
/* Some other useful macro definitions*/
|
||||
#ifndef min
|
||||
#define min(a, b)(((a) > (b)) ? (b) : (a))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a, b)(((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef clamp
|
||||
#define clamp(a, min, max) (((a) > (max)) ? (max) : (((a) < (min)) ? (min) : (a)))
|
||||
#endif
|
||||
|
||||
#ifndef sign
|
||||
#define sign(a) (((a) > 0) ? (1) : (-1))
|
||||
#endif
|
||||
|
||||
#ifndef lrnd
|
||||
#define lrnd(a, b) ((b)((a) >= 0.0 ? (a) + 0.5 : (a) - 0.5))
|
||||
#endif
|
||||
|
||||
#endif /* MPLAYER_AF_H */
|
||||
+79
-40
@@ -27,7 +27,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "af.h"
|
||||
#include "options/m_option.h"
|
||||
#include "mpvcore/m_option.h"
|
||||
|
||||
/// Internal specific data of the filter
|
||||
struct af_bs2b {
|
||||
@@ -37,25 +37,33 @@ struct af_bs2b {
|
||||
t_bs2bdp filter; ///< instance of a library filter
|
||||
};
|
||||
|
||||
#define FILTER(name, type) \
|
||||
static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
|
||||
#define PLAY(name, type) \
|
||||
static struct mp_audio *play_##name(struct af_instance *af, struct mp_audio *data) \
|
||||
{ \
|
||||
/* filter is called for all pairs of samples available in the buffer */ \
|
||||
bs2b_cross_feed_##name(((struct af_bs2b*)(af->priv))->filter, \
|
||||
(type*)(data->planes[0]), data->samples); \
|
||||
(type*)(data->audio), data->len/data->bps/2); \
|
||||
\
|
||||
return 0; \
|
||||
return data; \
|
||||
}
|
||||
|
||||
FILTER(f, float)
|
||||
FILTER(s32, int32_t)
|
||||
FILTER(u32, uint32_t)
|
||||
FILTER(s24, bs2b_int24_t)
|
||||
FILTER(u24, bs2b_uint24_t)
|
||||
FILTER(s16, int16_t)
|
||||
FILTER(u16, uint16_t)
|
||||
FILTER(s8, int8_t)
|
||||
FILTER(u8, uint8_t)
|
||||
PLAY(f, float)
|
||||
PLAY(fbe, float)
|
||||
PLAY(fle, float)
|
||||
PLAY(s32be, int32_t)
|
||||
PLAY(u32be, uint32_t)
|
||||
PLAY(s32le, int32_t)
|
||||
PLAY(u32le, uint32_t)
|
||||
PLAY(s24be, bs2b_int24_t)
|
||||
PLAY(u24be, bs2b_uint24_t)
|
||||
PLAY(s24le, bs2b_int24_t)
|
||||
PLAY(u24le, bs2b_uint24_t)
|
||||
PLAY(s16be, int16_t)
|
||||
PLAY(u16be, uint16_t)
|
||||
PLAY(s16le, int16_t)
|
||||
PLAY(u16le, uint16_t)
|
||||
PLAY(s8, int8_t)
|
||||
PLAY(u8, uint8_t)
|
||||
|
||||
|
||||
/// Initialization and runtime control
|
||||
@@ -66,6 +74,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT: {
|
||||
int format;
|
||||
char buf[256];
|
||||
// Sanity check
|
||||
if (!arg) return AF_ERROR;
|
||||
|
||||
@@ -77,49 +86,71 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
/* check for formats supported by libbs2b
|
||||
and assign corresponding handlers */
|
||||
switch (format) {
|
||||
case AF_FORMAT_FLOAT:
|
||||
af->filter = filter_f;
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
af->play = play_fbe;
|
||||
break;
|
||||
case AF_FORMAT_S32:
|
||||
af->filter = filter_s32;
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
af->play = play_fle;
|
||||
break;
|
||||
case AF_FORMAT_U32:
|
||||
af->filter = filter_u32;
|
||||
case AF_FORMAT_S32_BE:
|
||||
af->play = play_s32be;
|
||||
break;
|
||||
case AF_FORMAT_S24:
|
||||
af->filter = filter_s24;
|
||||
case AF_FORMAT_U32_BE:
|
||||
af->play = play_u32be;
|
||||
break;
|
||||
case AF_FORMAT_U24:
|
||||
af->filter = filter_u24;
|
||||
case AF_FORMAT_S32_LE:
|
||||
af->play = play_s32le;
|
||||
break;
|
||||
case AF_FORMAT_S16:
|
||||
af->filter = filter_s16;
|
||||
case AF_FORMAT_U32_LE:
|
||||
af->play = play_u32le;
|
||||
break;
|
||||
case AF_FORMAT_U16:
|
||||
af->filter = filter_u16;
|
||||
case AF_FORMAT_S24_BE:
|
||||
af->play = play_s24be;
|
||||
break;
|
||||
case AF_FORMAT_U24_BE:
|
||||
af->play = play_u24be;
|
||||
break;
|
||||
case AF_FORMAT_S24_LE:
|
||||
af->play = play_s24le;
|
||||
break;
|
||||
case AF_FORMAT_U24_LE:
|
||||
af->play = play_u24le;
|
||||
break;
|
||||
case AF_FORMAT_S16_BE:
|
||||
af->play = play_s16be;
|
||||
break;
|
||||
case AF_FORMAT_U16_BE:
|
||||
af->play = play_u16be;
|
||||
break;
|
||||
case AF_FORMAT_S16_LE:
|
||||
af->play = play_s16le;
|
||||
break;
|
||||
case AF_FORMAT_U16_LE:
|
||||
af->play = play_u16le;
|
||||
break;
|
||||
case AF_FORMAT_S8:
|
||||
af->filter = filter_s8;
|
||||
af->play = play_s8;
|
||||
break;
|
||||
case AF_FORMAT_U8:
|
||||
af->filter = filter_u8;
|
||||
af->play = play_u8;
|
||||
break;
|
||||
default:
|
||||
af->filter = filter_f;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
af->play = play_f;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
break;
|
||||
}
|
||||
|
||||
// bs2b have srate limits, try to resample if needed
|
||||
if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) {
|
||||
af->data->rate = BS2B_DEFAULT_SRATE;
|
||||
MP_WARN(af, "Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
|
||||
"Trying to resample to %d Hz.\n",
|
||||
mp_msg(MSGT_AFILTER, MSGL_WARN,
|
||||
"[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
|
||||
"[bs2b] Trying to resample to %d Hz.\n",
|
||||
af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE);
|
||||
}
|
||||
bs2b_set_srate(s->filter, (long)af->data->rate);
|
||||
MP_VERBOSE(af, "using format %s\n",
|
||||
af_fmt_to_str(af->data->format));
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[bs2b] using format %s\n",
|
||||
af_fmt2str(af->data->format,buf,256));
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
@@ -131,6 +162,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
static void uninit(struct af_instance *af)
|
||||
{
|
||||
struct af_bs2b *s = af->priv;
|
||||
free(af->data);
|
||||
if (s->filter)
|
||||
bs2b_close(s->filter);
|
||||
}
|
||||
@@ -141,9 +173,13 @@ static int af_open(struct af_instance *af)
|
||||
struct af_bs2b *s = af->priv;
|
||||
af->control = control;
|
||||
af->uninit = uninit;
|
||||
af->mul = 1;
|
||||
if (!(af->data = calloc(1, sizeof(struct mp_audio))))
|
||||
return AF_ERROR;
|
||||
|
||||
// NULL means failed initialization
|
||||
if (!(s->filter = bs2b_open())) {
|
||||
free(af->data);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
@@ -160,10 +196,13 @@ static int af_open(struct af_instance *af)
|
||||
#define OPT_BASE_STRUCT struct af_bs2b
|
||||
|
||||
/// Description of this filter
|
||||
const struct af_info af_info_bs2b = {
|
||||
.info = "Bauer stereophonic-to-binaural audio filter",
|
||||
.name = "bs2b",
|
||||
.open = af_open,
|
||||
struct af_info af_info_bs2b = {
|
||||
"Bauer stereophonic-to-binaural audio filter",
|
||||
"bs2b",
|
||||
"Andrew Savchenko",
|
||||
"",
|
||||
AF_FLAGS_REENTRANT,
|
||||
af_open,
|
||||
.priv_size = sizeof(struct af_bs2b),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
|
||||
|
||||
+55
-26
@@ -29,19 +29,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_center_s
|
||||
{
|
||||
int ch; // Channel number which to insert the filtered data
|
||||
int ch; // Channel number which to insert the filtered data
|
||||
}af_center_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_center_t* s = af->priv;
|
||||
af_center_t* s = af->setup;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
@@ -49,24 +48,48 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
mp_audio_set_channels_old(af->data, max(s->ch+1,((struct mp_audio*)arg)->nch));
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
int ch=1;
|
||||
sscanf(arg,"%i", &ch);
|
||||
return control(af,AF_CONTROL_CENTER_CH | AF_CONTROL_SET, &ch);
|
||||
}
|
||||
case AF_CONTROL_CENTER_CH | AF_CONTROL_SET: // Requires reinit
|
||||
// Sanity check
|
||||
if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Center channel number must be between "
|
||||
" 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg);
|
||||
return AF_ERROR;
|
||||
}
|
||||
s->ch = *(int*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_CENTER_CH | AF_CONTROL_GET:
|
||||
*(int*)arg = s->ch;
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
// Deallocate memory
|
||||
static void uninit(struct af_instance* af)
|
||||
{
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_center_t* s = af->priv; // Setup for this instance
|
||||
float* a = c->planes[0]; // Audio data
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = c->samples*c->nch; // Number of samples in current audio block
|
||||
int ch = s->ch; // Channel in which to insert the center audio
|
||||
free(af->data);
|
||||
free(af->setup);
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_center_t* s = af->setup; // Setup for this instance
|
||||
float* a = c->audio; // Audio data
|
||||
int len = c->len/4; // Number of samples in current audio block
|
||||
int nch = c->nch; // Number of channels
|
||||
int ch = s->ch; // Channel in which to insert the center audio
|
||||
register int i;
|
||||
|
||||
// Run filter
|
||||
@@ -75,25 +98,31 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
a[i+ch] = (a[i]/2) + (a[i+1]/2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af_center_t* s;
|
||||
af->control=control;
|
||||
af->filter=filter;
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
af->mul=1;
|
||||
af->data=calloc(1,sizeof(struct mp_audio));
|
||||
af->setup=s=calloc(1,sizeof(af_center_t));
|
||||
if(af->data == NULL || af->setup == NULL)
|
||||
return AF_ERROR;
|
||||
// Set default values
|
||||
s->ch = 1; // Channel nr 2
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_center_t
|
||||
const struct af_info af_info_center = {
|
||||
.info = "Audio filter for adding a center channel",
|
||||
.name = "center",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_center_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("channel", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(1)),
|
||||
{0}
|
||||
},
|
||||
// Description of this filter
|
||||
struct af_info af_info_center = {
|
||||
"Audio filter for adding a center channel",
|
||||
"center",
|
||||
"Alex Beregszaszi",
|
||||
"",
|
||||
AF_FLAGS_NOT_REENTRANT,
|
||||
af_open
|
||||
};
|
||||
+104
-84
@@ -3,8 +3,6 @@
|
||||
* command line parameter channels. It is stupid and can only add
|
||||
* silence or copy channels, not mix or filter.
|
||||
*
|
||||
* Original author: Anders
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
@@ -27,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
#define FR 0
|
||||
@@ -35,14 +32,12 @@
|
||||
|
||||
typedef struct af_channels_s{
|
||||
int route[AF_NCH][2];
|
||||
int nch, nr;
|
||||
int nr;
|
||||
int router;
|
||||
char *routes;
|
||||
}af_channels_t;
|
||||
|
||||
// Local function for copying data
|
||||
static void copy(struct af_instance *af, void* in, void* out,
|
||||
int ins, int inos,int outs, int outos, int len, int bps)
|
||||
static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps)
|
||||
{
|
||||
switch(bps){
|
||||
case 1:{
|
||||
@@ -113,25 +108,24 @@ static void copy(struct af_instance *af, void* in, void* out,
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MP_ERR(af, "Unsupported number of bytes/sample: %i"
|
||||
" please report this error on the MPlayer mailing list. \n",bps);
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Unsupported number of bytes/sample: %i"
|
||||
" please report this error on the MPlayer mailing list. \n",bps);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the routes are sane
|
||||
static int check_routes(struct af_instance *af, int nin, int nout)
|
||||
static int check_routes(af_channels_t* s, int nin, int nout)
|
||||
{
|
||||
af_channels_t* s = af->priv;
|
||||
int i;
|
||||
if((s->nr < 1) || (s->nr > AF_NCH)){
|
||||
MP_ERR(af, "The number of routing pairs must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
for(i=0;i<s->nr;i++){
|
||||
if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){
|
||||
MP_ERR(af, "Invalid routing in pair nr. %i.\n", i);
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i);
|
||||
return AF_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -141,111 +135,137 @@ static int check_routes(struct af_instance *af, int nin, int nout)
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_channels_t* s = af->priv;
|
||||
af_channels_t* s = af->setup;
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT: ;
|
||||
|
||||
struct mp_chmap chmap;
|
||||
mp_chmap_set_unknown(&chmap, s->nch);
|
||||
mp_audio_set_channels(af->data, &chmap);
|
||||
case AF_CONTROL_REINIT:
|
||||
|
||||
// Set default channel assignment
|
||||
if(!s->router){
|
||||
int i;
|
||||
// Make sure this filter isn't redundant
|
||||
if(af->data->nch == ((struct mp_audio*)arg)->nch)
|
||||
return AF_DETACH;
|
||||
return AF_DETACH;
|
||||
|
||||
// If mono: fake stereo
|
||||
if(((struct mp_audio*)arg)->nch == 1){
|
||||
s->nr = MPMIN(af->data->nch,2);
|
||||
for(i=0;i<s->nr;i++){
|
||||
s->route[i][FR] = 0;
|
||||
s->route[i][TO] = i;
|
||||
}
|
||||
s->nr = min(af->data->nch,2);
|
||||
for(i=0;i<s->nr;i++){
|
||||
s->route[i][FR] = 0;
|
||||
s->route[i][TO] = i;
|
||||
}
|
||||
}
|
||||
else{
|
||||
s->nr = MPMIN(af->data->nch, ((struct mp_audio*)arg)->nch);
|
||||
for(i=0;i<s->nr;i++){
|
||||
s->route[i][FR] = i;
|
||||
s->route[i][TO] = i;
|
||||
}
|
||||
s->nr = min(af->data->nch, ((struct mp_audio*)arg)->nch);
|
||||
for(i=0;i<s->nr;i++){
|
||||
s->route[i][FR] = i;
|
||||
s->route[i][TO] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_force_interleaved_format((struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, ((struct mp_audio*)arg)->format);
|
||||
return check_routes(af,((struct mp_audio*)arg)->nch,af->data->nch);
|
||||
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
|
||||
return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch);
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
int nch = 0;
|
||||
int n = 0;
|
||||
// Check number of channels and number of routing pairs
|
||||
sscanf(arg, "%i:%i%n", &nch, &s->nr, &n);
|
||||
|
||||
// If router scan commandline for routing pairs
|
||||
if(s->nr){
|
||||
char* cp = &((char*)arg)[n];
|
||||
int ch = 0;
|
||||
// Sanity check
|
||||
if((s->nr < 1) || (s->nr > AF_NCH)){
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
|
||||
}
|
||||
s->router = 1;
|
||||
// Scan for pairs on commandline
|
||||
while((*cp == ':') && (ch < s->nr)){
|
||||
sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Routing from channel %i to"
|
||||
" channel %i\n",s->route[ch][FR],s->route[ch][TO]);
|
||||
cp = &cp[n];
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
|
||||
struct mp_chmap chmap;
|
||||
mp_chmap_from_channels(&chmap, nch);
|
||||
if (AF_OK != af->control(af, AF_CONTROL_CHANNELS | AF_CONTROL_SET, &chmap))
|
||||
return AF_ERROR;
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_CHANNELS | AF_CONTROL_SET:
|
||||
// Reinit must be called after this function has been called
|
||||
|
||||
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
|
||||
if(!s->router)
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels"
|
||||
" to %i\n",af->data->nch);
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
// Deallocate memory
|
||||
static void uninit(struct af_instance* af)
|
||||
{
|
||||
struct mp_audio* c = data; // Current working data
|
||||
struct mp_audio* l = af->data; // Local data
|
||||
af_channels_t* s = af->priv;
|
||||
int i;
|
||||
free(af->setup);
|
||||
if (af->data)
|
||||
free(af->data->audio);
|
||||
free(af->data);
|
||||
}
|
||||
|
||||
mp_audio_realloc_min(af->data, data->samples);
|
||||
// Filter data through filter
|
||||
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
struct mp_audio* c = data; // Current working data
|
||||
struct mp_audio* l = af->data; // Local data
|
||||
af_channels_t* s = af->setup;
|
||||
int i;
|
||||
|
||||
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
|
||||
return NULL;
|
||||
|
||||
// Reset unused channels
|
||||
memset(l->planes[0],0,mp_audio_psize(c) / c->nch * l->nch);
|
||||
memset(l->audio,0,c->len / c->nch * l->nch);
|
||||
|
||||
if(AF_OK == check_routes(af,c->nch,l->nch))
|
||||
if(AF_OK == check_routes(s,c->nch,l->nch))
|
||||
for(i=0;i<s->nr;i++)
|
||||
copy(af, c->planes[0],l->planes[0],c->nch,s->route[i][FR],
|
||||
l->nch,s->route[i][TO],mp_audio_psize(c),c->bps);
|
||||
copy(c->audio,l->audio,c->nch,s->route[i][FR],
|
||||
l->nch,s->route[i][TO],c->len,c->bps);
|
||||
|
||||
// Set output data
|
||||
c->planes[0] = l->planes[0];
|
||||
c->audio = l->audio;
|
||||
c->len = c->len / c->nch * l->nch;
|
||||
mp_audio_set_channels(c, &l->channels);
|
||||
|
||||
return 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter=filter;
|
||||
af_channels_t *s = af->priv;
|
||||
|
||||
// If router scan commandline for routing pairs
|
||||
if(s->routes && s->routes[0]){
|
||||
char* cp = s->routes;
|
||||
int ch = 0;
|
||||
// Scan for pairs on commandline
|
||||
do {
|
||||
int n = 0;
|
||||
if (ch >= AF_NCH) {
|
||||
MP_FATAL(af, "Can't have more than %d routes.\n", AF_NCH);
|
||||
return AF_ERROR;
|
||||
}
|
||||
sscanf(cp, "%i-%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
|
||||
MP_VERBOSE(af, "Routing from channel %i to"
|
||||
" channel %i\n",s->route[ch][FR],s->route[ch][TO]);
|
||||
cp = &cp[n];
|
||||
ch++;
|
||||
} while(*cp == ',' && *(cp++));
|
||||
s->nr = ch;
|
||||
if (s->nr > 0)
|
||||
s->router = 1;
|
||||
}
|
||||
|
||||
return AF_OK;
|
||||
af->control=control;
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
af->mul=1;
|
||||
af->data=calloc(1,sizeof(struct mp_audio));
|
||||
af->setup=calloc(1,sizeof(af_channels_t));
|
||||
if((af->data == NULL) || (af->setup == NULL))
|
||||
return AF_ERROR;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_channels_t
|
||||
const struct af_info af_info_channels = {
|
||||
.info = "Insert or remove channels",
|
||||
.name = "channels",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_channels_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("nch", nch, 0, 1, AF_NCH, OPTDEF_INT(2)),
|
||||
OPT_STRING("routes", routes, 0),
|
||||
{0}
|
||||
},
|
||||
// Description of this filter
|
||||
struct af_info af_info_channels = {
|
||||
"Insert or remove channels",
|
||||
"channels",
|
||||
"Anders",
|
||||
"",
|
||||
AF_FLAGS_REENTRANT,
|
||||
af_open
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "af.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
return (src_format == AF_FORMAT_U24 && dst_format == AF_FORMAT_U32) ||
|
||||
(src_format == AF_FORMAT_S24 && dst_format == AF_FORMAT_S32) ||
|
||||
(src_format == AF_FORMAT_U32 && dst_format == AF_FORMAT_U24) ||
|
||||
(src_format == AF_FORMAT_S32 && dst_format == AF_FORMAT_S24);
|
||||
}
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT: {
|
||||
struct mp_audio *in = arg;
|
||||
struct mp_audio orig_in = *in;
|
||||
struct mp_audio *out = af->data;
|
||||
|
||||
if (!test_conversion(in->format, out->format))
|
||||
return AF_DETACH;
|
||||
|
||||
if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_24BIT) {
|
||||
mp_audio_set_format(out, af_fmt_change_bits(in->format, 32));
|
||||
} else if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_32BIT) {
|
||||
mp_audio_set_format(out, af_fmt_change_bits(in->format, 24));
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
out->rate = in->rate;
|
||||
mp_audio_set_channels(out, &in->channels);
|
||||
|
||||
assert(test_conversion(in->format, out->format));
|
||||
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
}
|
||||
case AF_CONTROL_SET_FORMAT: {
|
||||
mp_audio_set_format(af->data, *(int*)arg);
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// The LSB is always ignored.
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define SHIFT(x) ((3-(x))*8)
|
||||
#else
|
||||
#define SHIFT(x) (((x)+1)*8)
|
||||
#endif
|
||||
|
||||
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
{
|
||||
mp_audio_realloc_min(af->data, data->samples);
|
||||
|
||||
struct mp_audio *out = af->data;
|
||||
size_t len = mp_audio_psize(data) / data->bps;
|
||||
|
||||
if (data->bps == 4) {
|
||||
for (int s = 0; s < len; s++) {
|
||||
uint32_t val = *((uint32_t *)data->planes[0] + s);
|
||||
uint8_t *ptr = (uint8_t *)out->planes[0] + s * 3;
|
||||
ptr[0] = val >> SHIFT(0);
|
||||
ptr[1] = val >> SHIFT(1);
|
||||
ptr[2] = val >> SHIFT(2);
|
||||
}
|
||||
mp_audio_set_format(data, af_fmt_change_bits(data->format, 24));
|
||||
} else {
|
||||
for (int s = 0; s < len; s++) {
|
||||
uint8_t *ptr = (uint8_t *)data->planes[0] + s * 3;
|
||||
uint32_t val = ptr[0] << SHIFT(0)
|
||||
| ptr[1] << SHIFT(1)
|
||||
| ptr[2] << SHIFT(2);
|
||||
*((uint32_t *)out->planes[0] + s) = val;
|
||||
}
|
||||
mp_audio_set_format(data, af_fmt_change_bits(data->format, 32));
|
||||
}
|
||||
|
||||
data->planes[0] = out->planes[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
af->control = control;
|
||||
af->filter = filter;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
const struct af_info af_info_convert24 = {
|
||||
.info = "Convert between 24 and 32 bit sample format",
|
||||
.name = "convert24",
|
||||
.open = af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "af.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
if ((src_format & AF_FORMAT_PLANAR) ||
|
||||
(dst_format & AF_FORMAT_PLANAR))
|
||||
return false;
|
||||
if (((src_format & ~AF_FORMAT_SIGN_MASK) ==
|
||||
(dst_format & ~AF_FORMAT_SIGN_MASK)) &&
|
||||
((src_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_I))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT: {
|
||||
struct mp_audio *in = arg;
|
||||
struct mp_audio orig_in = *in;
|
||||
struct mp_audio *out = af->data;
|
||||
|
||||
if (!test_conversion(in->format, out->format))
|
||||
return AF_DETACH;
|
||||
|
||||
out->rate = in->rate;
|
||||
mp_audio_set_channels(out, &in->channels);
|
||||
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
}
|
||||
case AF_CONTROL_SET_FORMAT: {
|
||||
mp_audio_set_format(af->data, *(int*)arg);
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static void si2us(void *data, int len, int bps)
|
||||
{
|
||||
ptrdiff_t i = -(len * bps);
|
||||
uint8_t *p = &((uint8_t *)data)[len * bps];
|
||||
if (BYTE_ORDER == LITTLE_ENDIAN && bps > 1)
|
||||
p += bps - 1;
|
||||
if (len <= 0)
|
||||
return;
|
||||
do {
|
||||
p[i] ^= 0x80;
|
||||
} while (i += bps);
|
||||
}
|
||||
|
||||
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
{
|
||||
int infmt = data->format;
|
||||
int outfmt = af->data->format;
|
||||
size_t len = data->samples * data->nch;
|
||||
|
||||
if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK))
|
||||
si2us(data->planes[0], len, data->bps);
|
||||
|
||||
mp_audio_set_format(data, outfmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
af->control = control;
|
||||
af->filter = filter;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
const struct af_info af_info_convertsignendian = {
|
||||
.info = "Convert between sample format sign",
|
||||
.name = "convertsign",
|
||||
.open = af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
+82
-75
@@ -3,8 +3,6 @@
|
||||
* channels and can be used for simple position panning.
|
||||
* An extension for this filter would be a reverb.
|
||||
*
|
||||
* Original author: Anders
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
@@ -27,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
#define L 65536
|
||||
@@ -37,52 +34,71 @@
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_delay_s
|
||||
{
|
||||
void* q[AF_NCH]; // Circular queues used for delaying audio signal
|
||||
int wi[AF_NCH]; // Write index
|
||||
int ri; // Read index
|
||||
float d[AF_NCH]; // Delay [ms]
|
||||
char *delaystr;
|
||||
void* q[AF_NCH]; // Circular queues used for delaying audio signal
|
||||
int wi[AF_NCH]; // Write index
|
||||
int ri; // Read index
|
||||
float d[AF_NCH]; // Delay [ms]
|
||||
}af_delay_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_delay_t* s = af->priv;
|
||||
af_delay_t* s = af->setup;
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
int i;
|
||||
struct mp_audio *in = arg;
|
||||
|
||||
if (in->bps != 1 && in->bps != 2 && in->bps != 4) {
|
||||
MP_FATAL(af, "Sample format not supported\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
// Free prevous delay queues
|
||||
for(i=0;i<af->data->nch;i++)
|
||||
free(s->q[i]);
|
||||
|
||||
mp_audio_force_interleaved_format(in);
|
||||
mp_audio_copy_config(af->data, in);
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
|
||||
// Allocate new delay queues
|
||||
for(i=0;i<af->data->nch;i++){
|
||||
s->q[i] = calloc(L,af->data->bps);
|
||||
if(NULL == s->q[i])
|
||||
MP_FATAL(af, "Out of memory\n");
|
||||
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
|
||||
}
|
||||
|
||||
if(AF_OK != af_from_ms(AF_NCH, s->d, s->wi, af->data->rate, 0.0, 1000.0))
|
||||
return control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,s->d);
|
||||
}
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
int n = 1;
|
||||
int i = 0;
|
||||
char* cl = arg;
|
||||
while(n && i < AF_NCH ){
|
||||
sscanf(cl,"%f:%n",&s->d[i],&n);
|
||||
if(n==0 || cl[n-1] == '\0')
|
||||
break;
|
||||
cl=&cl[n];
|
||||
i++;
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_DELAY_LEN | AF_CONTROL_SET:{
|
||||
int i;
|
||||
if(AF_OK != af_from_ms(AF_NCH, arg, s->wi, af->data->rate, 0.0, 1000.0))
|
||||
return AF_ERROR;
|
||||
s->ri = 0;
|
||||
for(i=0;i<AF_NCH;i++){
|
||||
MP_DBG(af, "Channel %i delayed by %0.3fms\n",
|
||||
i,MPCLAMP(s->d[i],0.0,1000.0));
|
||||
MP_TRACE(af, "Channel %i delayed by %i samples\n",
|
||||
i,s->wi[i]);
|
||||
mp_msg(MSGT_AFILTER, MSGL_DBG2, "[delay] Channel %i delayed by %0.3fms\n",
|
||||
i,clamp(s->d[i],0.0,1000.0));
|
||||
mp_msg(MSGT_AFILTER, MSGL_DBG3, "[delay] Channel %i delayed by %i samples\n",
|
||||
i,s->wi[i]);
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_DELAY_LEN | AF_CONTROL_GET:{
|
||||
int i;
|
||||
for(i=0;i<AF_NCH;i++){
|
||||
if(s->ri > s->wi[i])
|
||||
s->wi[i] = L - (s->ri - s->wi[i]);
|
||||
else
|
||||
s->wi[i] = s->wi[i] - s->ri;
|
||||
}
|
||||
return af_to_ms(AF_NCH, s->wi, arg, af->data->rate);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
@@ -92,59 +108,61 @@ static void uninit(struct af_instance* af)
|
||||
{
|
||||
int i;
|
||||
|
||||
free(af->data);
|
||||
for(i=0;i<AF_NCH;i++)
|
||||
free(((af_delay_t*)(af->priv))->q[i]);
|
||||
free(((af_delay_t*)(af->setup))->q[i]);
|
||||
free(af->setup);
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_delay_t* s = af->priv; // Setup for this instance
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = mp_audio_psize(c)/c->bps; // Number of sample in data chunk
|
||||
int ri = 0;
|
||||
int ch,i;
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_delay_t* s = af->setup; // Setup for this instance
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = c->len/c->bps; // Number of sample in data chunk
|
||||
int ri = 0;
|
||||
int ch,i;
|
||||
for(ch=0;ch<nch;ch++){
|
||||
switch(c->bps){
|
||||
case 1:{
|
||||
int8_t* a = c->planes[0];
|
||||
int8_t* a = c->audio;
|
||||
int8_t* q = s->q[ch];
|
||||
int wi = s->wi[ch];
|
||||
ri = s->ri;
|
||||
for(i=ch;i<len;i+=nch){
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
}
|
||||
s->wi[ch] = wi;
|
||||
break;
|
||||
}
|
||||
case 2:{
|
||||
int16_t* a = c->planes[0];
|
||||
int16_t* a = c->audio;
|
||||
int16_t* q = s->q[ch];
|
||||
int wi = s->wi[ch];
|
||||
ri = s->ri;
|
||||
for(i=ch;i<len;i+=nch){
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
}
|
||||
s->wi[ch] = wi;
|
||||
break;
|
||||
}
|
||||
case 4:{
|
||||
int32_t* a = c->planes[0];
|
||||
int32_t* a = c->audio;
|
||||
int32_t* q = s->q[ch];
|
||||
int wi = s->wi[ch];
|
||||
ri = s->ri;
|
||||
for(i=ch;i<len;i+=nch){
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
q[wi] = a[i];
|
||||
a[i] = q[ri];
|
||||
UPDATEQI(wi);
|
||||
UPDATEQI(ri);
|
||||
}
|
||||
s->wi[ch] = wi;
|
||||
break;
|
||||
@@ -152,39 +170,28 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
}
|
||||
}
|
||||
s->ri = ri;
|
||||
return 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->uninit=uninit;
|
||||
af->filter=filter;
|
||||
af_delay_t *s = af->priv;
|
||||
int n = 1;
|
||||
int i = 0;
|
||||
char* cl = s->delaystr;
|
||||
while(cl && n && i < AF_NCH ){
|
||||
sscanf(cl,"%f%n",&s->d[i],&n);
|
||||
if(n==0 || cl[n-1] == '\0')
|
||||
break;
|
||||
cl=&cl[n];
|
||||
if (*cl != ',')
|
||||
break;
|
||||
cl++;
|
||||
i++;
|
||||
}
|
||||
return AF_OK;
|
||||
af->control=control;
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
af->mul=1;
|
||||
af->data=calloc(1,sizeof(struct mp_audio));
|
||||
af->setup=calloc(1,sizeof(af_delay_t));
|
||||
if(af->data == NULL || af->setup == NULL)
|
||||
return AF_ERROR;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_delay_t
|
||||
const struct af_info af_info_delay = {
|
||||
.info = "Delay audio filter",
|
||||
.name = "delay",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_delay_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("delays", delaystr, 0),
|
||||
{0}
|
||||
},
|
||||
// Description of this filter
|
||||
struct af_info af_info_delay = {
|
||||
"Delay audio filter",
|
||||
"delay",
|
||||
"Anders",
|
||||
"",
|
||||
AF_FLAGS_REENTRANT,
|
||||
af_open
|
||||
};
|
||||
+74
-53
@@ -26,7 +26,6 @@
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
// Methods:
|
||||
@@ -70,8 +69,8 @@ typedef struct af_volume_s
|
||||
// method 2
|
||||
int idx;
|
||||
struct {
|
||||
float avg; // average level of the sample
|
||||
int len; // sample size (weight)
|
||||
float avg; // average level of the sample
|
||||
int len; // sample size (weight)
|
||||
} mem[NSAMPLES];
|
||||
// "Ideal" level
|
||||
float mid_s16;
|
||||
@@ -81,27 +80,46 @@ typedef struct af_volume_s
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_drc_t* s = (af_drc_t*)af->setup;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
mp_audio_force_interleaved_format((struct mp_audio*)arg);
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
|
||||
if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16)){
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16_NE)){
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
}
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
int i = 0;
|
||||
float target = DEFAULT_TARGET;
|
||||
sscanf((char*)arg,"%d:%f", &i, &target);
|
||||
if (i != 1 && i != 2)
|
||||
return AF_ERROR;
|
||||
s->method = i-1;
|
||||
s->mid_s16 = ((float)SHRT_MAX) * target;
|
||||
s->mid_float = target;
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// Deallocate memory
|
||||
static void uninit(struct af_instance* af)
|
||||
{
|
||||
free(af->data);
|
||||
free(af->setup);
|
||||
}
|
||||
|
||||
static void method1_int16(af_drc_t *s, struct mp_audio *c)
|
||||
{
|
||||
register int i = 0;
|
||||
int16_t *data = (int16_t*)c->planes[0]; // Audio data
|
||||
int len = c->samples*c->nch; // Number of samples
|
||||
int16_t *data = (int16_t*)c->audio; // Audio data
|
||||
int len = c->len/2; // Number of samples
|
||||
float curavg = 0.0, newavg, neededmul;
|
||||
int tmp;
|
||||
|
||||
@@ -121,14 +139,14 @@ static void method1_int16(af_drc_t *s, struct mp_audio *c)
|
||||
s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
|
||||
|
||||
// clamp the mul coefficient
|
||||
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
|
||||
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
|
||||
}
|
||||
|
||||
// Scale & clamp the samples
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tmp = s->mul * data[i];
|
||||
tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
|
||||
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
|
||||
data[i] = tmp;
|
||||
}
|
||||
|
||||
@@ -142,8 +160,8 @@ static void method1_int16(af_drc_t *s, struct mp_audio *c)
|
||||
static void method1_float(af_drc_t *s, struct mp_audio *c)
|
||||
{
|
||||
register int i = 0;
|
||||
float *data = (float*)c->planes[0]; // Audio data
|
||||
int len = c->samples*c->nch; // Number of samples
|
||||
float *data = (float*)c->audio; // Audio data
|
||||
int len = c->len/4; // Number of samples
|
||||
float curavg = 0.0, newavg, neededmul, tmp;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -162,7 +180,7 @@ static void method1_float(af_drc_t *s, struct mp_audio *c)
|
||||
s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
|
||||
|
||||
// clamp the mul coefficient
|
||||
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
|
||||
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
|
||||
}
|
||||
|
||||
// Scale & clamp the samples
|
||||
@@ -179,8 +197,8 @@ static void method1_float(af_drc_t *s, struct mp_audio *c)
|
||||
static void method2_int16(af_drc_t *s, struct mp_audio *c)
|
||||
{
|
||||
register int i = 0;
|
||||
int16_t *data = (int16_t*)c->planes[0]; // Audio data
|
||||
int len = c->samples*c->nch; // Number of samples
|
||||
int16_t *data = (int16_t*)c->audio; // Audio data
|
||||
int len = c->len/2; // Number of samples
|
||||
float curavg = 0.0, newavg, avg = 0.0;
|
||||
int tmp, totallen = 0;
|
||||
|
||||
@@ -204,8 +222,8 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
|
||||
avg /= (float)totallen;
|
||||
if (avg >= SIL_S16)
|
||||
{
|
||||
s->mul = s->mid_s16 / avg;
|
||||
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
|
||||
s->mul = s->mid_s16 / avg;
|
||||
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +231,7 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tmp = s->mul * data[i];
|
||||
tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
|
||||
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
|
||||
data[i] = tmp;
|
||||
}
|
||||
|
||||
@@ -229,8 +247,8 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
|
||||
static void method2_float(af_drc_t *s, struct mp_audio *c)
|
||||
{
|
||||
register int i = 0;
|
||||
float *data = (float*)c->planes[0]; // Audio data
|
||||
int len = c->samples*c->nch; // Number of samples
|
||||
float *data = (float*)c->audio; // Audio data
|
||||
int len = c->len/4; // Number of samples
|
||||
float curavg = 0.0, newavg, avg = 0.0, tmp;
|
||||
int totallen = 0;
|
||||
|
||||
@@ -254,8 +272,8 @@ static void method2_float(af_drc_t *s, struct mp_audio *c)
|
||||
avg /= (float)totallen;
|
||||
if (avg >= SIL_FLOAT)
|
||||
{
|
||||
s->mul = s->mid_float / avg;
|
||||
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
|
||||
s->mul = s->mid_float / avg;
|
||||
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,56 +290,59 @@ static void method2_float(af_drc_t *s, struct mp_audio *c)
|
||||
s->idx = (s->idx + 1) % NSAMPLES;
|
||||
}
|
||||
|
||||
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
|
||||
// Filter data through filter
|
||||
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
af_drc_t *s = af->priv;
|
||||
af_drc_t *s = af->setup;
|
||||
|
||||
if(af->data->format == (AF_FORMAT_S16))
|
||||
if(af->data->format == (AF_FORMAT_S16_NE))
|
||||
{
|
||||
if (s->method == 2)
|
||||
method2_int16(s, data);
|
||||
if (s->method)
|
||||
method2_int16(s, data);
|
||||
else
|
||||
method1_int16(s, data);
|
||||
method1_int16(s, data);
|
||||
}
|
||||
else if(af->data->format == (AF_FORMAT_FLOAT))
|
||||
else if(af->data->format == (AF_FORMAT_FLOAT_NE))
|
||||
{
|
||||
if (s->method == 2)
|
||||
method2_float(s, data);
|
||||
if (s->method)
|
||||
method2_float(s, data);
|
||||
else
|
||||
method1_float(s, data);
|
||||
method1_float(s, data);
|
||||
}
|
||||
return 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
int i = 0;
|
||||
af->control=control;
|
||||
af->filter=filter;
|
||||
af_drc_t *priv = af->priv;
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
af->mul=1;
|
||||
af->data=calloc(1,sizeof(struct mp_audio));
|
||||
af->setup=calloc(1,sizeof(af_drc_t));
|
||||
if(af->data == NULL || af->setup == NULL)
|
||||
return AF_ERROR;
|
||||
|
||||
priv->mul = MUL_INIT;
|
||||
priv->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
|
||||
priv->idx = 0;
|
||||
((af_drc_t*)af->setup)->mul = MUL_INIT;
|
||||
((af_drc_t*)af->setup)->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
|
||||
((af_drc_t*)af->setup)->idx = 0;
|
||||
((af_drc_t*)af->setup)->mid_s16 = ((float)SHRT_MAX) * DEFAULT_TARGET;
|
||||
((af_drc_t*)af->setup)->mid_float = DEFAULT_TARGET;
|
||||
for (i = 0; i < NSAMPLES; i++)
|
||||
{
|
||||
priv->mem[i].len = 0;
|
||||
priv->mem[i].avg = 0;
|
||||
((af_drc_t*)af->setup)->mem[i].len = 0;
|
||||
((af_drc_t*)af->setup)->mem[i].avg = 0;
|
||||
}
|
||||
priv->mid_s16 = ((float)SHRT_MAX) * priv->mid_float;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_drc_t
|
||||
const struct af_info af_info_drc = {
|
||||
.info = "Dynamic range compression filter",
|
||||
.name = "drc",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_drc_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("method", method, 0, 1, 2, OPTDEF_INT(1)),
|
||||
OPT_FLOAT("target", mid_float, 0, OPTDEF_FLOAT(DEFAULT_TARGET)),
|
||||
{0}
|
||||
},
|
||||
// Description of this filter
|
||||
struct af_info af_info_drc = {
|
||||
"Dynamic range compression filter",
|
||||
"drc",
|
||||
"Alex Beregszaszi & Pierre Lombard",
|
||||
"",
|
||||
AF_FLAGS_NOT_REENTRANT,
|
||||
af_open
|
||||
};
|
||||
Loaded 100 of 629 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user