Compare commits

...
381 Commits
Author SHA1 Message Date
Alessandro Ghedini 40cd8867f1 Release 0.5.4 2014-09-23 21:21:41 +02:00
wm4 1a7b6f1810 audio: fix random crashes on uninit
I added this non-sense earlier this day. Oops.
2014-09-23 21:09:05 +02:00
wm4 6f7f7c3e2e player: don't let multiline filenames set options on resume
If --write-filename-in-watch-later-config is used, and the filename
contains newline characters (as generally allowed on Unix), then the
newline will be written to the resume file literally, and the parts
after the newline character are interpreted as options.

This is possibly security relevant.

Change newline characters (and in fact any other special characters)
to '_'.

Reported as #1099 (this commit is a reimplementation of the proposed
pull request).

CC: @mpv-player/stable
2014-09-23 21:08:54 +02:00
Alessandro Ghedini 5b6a67bf76 Release 0.5.3 2014-09-10 19:34:45 +02:00
Alessandro Ghedini 41f2ad33e5 player: fix build
This fixes a build failure introduced by c269fa1.
2014-09-10 19:32:54 +02:00
Alessandro Ghedini 27996bf1de Release 0.5.2 2014-09-09 12:18:47 +02:00
wm4 e3d3a47c0d stream_lavf: assume icy title data is terminated with ';'
Should fix #1071.
2014-09-06 14:32:24 +02:00
wm4 b109d20ef7 audio/out: make EOF handling properly event-based
With --gapless-audio=no, changing from one file to the next apparently
made it hang, until the player was woken up by unrelated events like
input. The reason was that the AO doesn't notify the player of EOF
properly. the played was querying ao_eof_reached(), and then just went
to sleep, without anything waking it up.

Make it event-based: the AO wakes up the playloop if the EOF state
changes.

We could have fixed this in a simpler way by synchronously draining the
AO in these cases. But I think proper event handling is preferable.

Fixes: #1069
CC: @mpv-player/stable (perhaps)
2014-09-06 00:22:07 +02:00
wm4 3c7d49c38f audio: correctly initialize output buffer
Just like the previous commit, this takes care of fallout from commit
7ab228, which fixed a bug, but introduced some new ones.

CC: @mpv-player/stable
2014-09-05 21:16:59 +02:00
James Ross-Gowan 0ba2da8a76 player: check for null in resume config search
Signed-off-by: wm4 <wm4@nowhere>

CC: @mpv-player/stable
2014-09-05 21:16:53 +02:00
wm4 1871f2a703 audio: fix obscure audio resync failure with timelines
Somehow, there was a larger misunderstanding in the code: ao_buffer
does not need to be preserved over audio reinit for proper support of
gapless audio. The actual AO internal buffer takes care of this.

In fact, preserving ao_buffer just breaks audio resync. In the ordered
chapter case, end_pts is used, which means not all audio data in the
buffer is played, thus some data is left over when audio decoding
resumes on the next segment. This triggers some code that aborts resync
if there's "audio decoded" (ao_buffer contains something), but no PTS
is known (nothing was actually decoded yet).

Simplify, and always bind the output buffer to the decoder.

CC: @mpv-player/stable (maybe)
2014-09-05 12:01:26 +02:00
wm4 4a8e5ea269 build: handle insane libavcodec API bullshit
The oldest supported FFmpeg release doesn't provide
av_vdpau_alloc_context(). With these versions, the application has no
other choice than to hard code the size of AVVDPAUContext. (On the other
hand, there's av_alloc_vdpaucontext(), which does the same thing, but is
FFmpeg specific - not sure if it was available early enough, so I'm not
touching it.)

Newer FFmpeg and Libav releases require you to call this function, for
ABI compatibility reasons. It's the typcal lakc of foresight that make
FFmpeg APIs terrible. mpv successfully pretended that this crap didn't
exist (ABI compat. is near impossible to reach anyway) - but it appears
newer developments in Libav change the function from initializing the
struct with all-zeros to something else, and mpv vdpau decoding would
stop working as soon as this new work is relewased.

So, add a configure test (sigh).

CC: @mpv-player/stable
2014-09-05 12:01:22 +02:00
wm4 367665b4c4 demux_mkv: allow up to 256 MB of extradata to make broken files work
What the flying fuck?

Unfortunately, these are already in the wild.

CC: @mpv-player/stable
2014-09-05 12:01:17 +02:00
wm4 46437e96c6 path: remove XDG bullshit
Don't worry, your ~/.config/... paths are safe. This merely removes
handling of $XDG_CONFIG_DIRS for global paths.

Maybe there is a better solution for this, like still including the
"traditional" config dir. But I will leave the fine reading of this
(crappy) spec and fixing the code accordingly to someone else. So, if
anyone has interest in getting this behavior back, you will have to
write a patch. This patch should _also_ not break expected behavior.

Fixes #1060.
2014-09-03 23:22:08 +02:00
wm4 29d3bbdaa2 player: properly resume playlists loaded at runtime
If a playlist is loaded from the internal playlist (like
"mpv playlist.m3u"), then attempt to resume from it.

CC: @mpv-player/stable
2014-09-03 23:21:36 +02:00
Alexander Preisinger 16c3b27d0a gl_wayland: fix crash after eglInitialze failed
Only execute most of the opengl termination procedure if we actually have an
egl context.
2014-09-01 17:13:24 +02:00
Martin Herkt f8dc208d0c man: fix a whole bunch of typos
Conflicts:
	DOCS/man/input.rst
	DOCS/man/options.rst
2014-09-01 14:53:55 +02:00
wm4 6d006a5a39 playlist: don't add the base path twice
Loading a playlist with --playlist from a sub-directory added the
playlist's base path twice: one time in the playlist demuxer, and then
again in playlist_parse_file(). The latter function is used only for
--playlist, so it worked when loading the playlist directly.

(This is probably a mess-up when the MPlayer playlist parsers were
replaced with newer code.)

CC: @mpv-player/stable
2014-09-01 14:48:23 +02:00
wm4 6a355959d9 audio/out: prevent burning CPU when seeking while paused
The audio/video sync code in player/audio.c calls ao_reset() each time
audio decoding is entered, but the player is paused, and there would be
more than 1 sample to skip to make audio start match with video start.
This caused a wakeup feedback loop with push.c.

CC: @mpv-player/stable
2014-09-01 14:48:17 +02:00
wm4 36bdbbe716 x11: when using --wid, inherit event flags from parent window
When embedding a X window, it's hard to control whether it receives
mouse/keyboard input or not. It seems the X protocol itself makes this
hard (basically due to the outdated design mismatching with modern
toolkits), and we have to take care of these things explicitly.

Simply do this by manually querying and using the parent window event
flags.

This restores some MPlayer behavior (it doesn't add back exactly the
same code, but it's very similar).

This probably has some potential to interfere with libmpv embedding, so
bump the client API minor.

CC: @mpv-player/stable (if applied, client-api-changes.rst has to be
    adjusted to include the 0.5.2 release)
2014-09-01 14:48:01 +02:00
wm4 c269fa1609 player: fix quit handling when seeking past EOF while paused
The player didn't quit when seeking past EOF in audio-only mode while
paused. The only case when we don't want to quit is when the last video
frame is displayed while paused.

This logic was probably broken a while ago, but I'm not exactly sure.

CC: @mpv-player/stable

Conflicts:
	player/playloop.c
2014-08-31 00:19:40 +02:00
wm4 0f1d6c09e0 audio: restore old speed change behavior
Don't attempt to resync after speed changes. Note that most other cases
of audio reinit (like switching tracks etc.) still resync, but other
code paths take care of setting the audio_status accordingly.

This restores the old behavior of not trying to fix audio desync, which
was probably changed with commit 261506e3.

Note that the code as of now wasn't even entirely correct, since the A/V
sync values are slightly shifted. The dsync depends on the audio buffer
size, so a larger buffer size will show more extreme desync. Also see
mplayer2 commit 213a224e, which should fixed this - it was not merged
into mpv, because it disabled audio for too long, resulting in a worse
user experience. This is similar to the issue this commit attempts to
fix.

Fixes: #1042 (probably)
CC: @mpv-player-stable
2014-08-28 18:10:32 +02:00
Alessandro Ghedini b677937c27 Release 0.5.1 2014-08-26 10:27:40 +02:00
Otto Modinos fdd648bdf9 manpage: mention that mp.commandv doesn't expand properties
The little lua snippet at #488 as well as the actual implementation
seems to indicate that not expanding properties is indeed the correct
behavior.  Document that.

Signed-off-by: wm4 <wm4@nowhere>
2014-08-26 10:24:21 +02:00
Stefano Pigozzi 334fd10f1c coreaudio_device: fix overwriting of user input
Fixes #1030
2014-08-25 11:24:01 +02:00
wm4 226735331c options: compatibility hack for --slave-broken
Seems some programs were still relying on it. Whatever, it's not hard to
support.

CC: @mpv-player/stable
2014-08-25 11:23:30 +02:00
wm4 5523370ab9 player: don't clobber playback position on video end
If video reaches EOF, and audio is also EOF (or is otherwise not
meaningful, like audio disabled), then the playback position was briefly
set to 0. Fix this by not trying to use a bogus audio PTS.

CC: @mpv-player/stable (maybe)
2014-08-25 11:23:26 +02:00
Bin Jin 8c7d61f1d9 manpage: remove duplicated content
Probably be accidentally added in b6b8bffd.

CC: @mpv-player/stable
Signed-off-by: wm4 <wm4@nowhere>
2014-08-25 11:23:05 +02:00
wm4 f021b5a379 etc/mpv.desktop: add audio/flac mimetype
Fixes #1025.

CC: @mpv-player/stable
2014-08-21 15:16:58 +02:00
wm4 43e164964c manpage: fix opengl-hq defaults
The FBO format was changed some time ago.

CC: @mpv-player/stable
2014-08-18 11:14:48 +02:00
Philip Sequeira aeb64723fd TOOLS/zsh.pl: protect global environment 2014-08-17 14:14:48 +02:00
Philip Sequeira f935838b2d TOOLS/zsh.pl: fix a ret that hadn't been changed to rc 2014-08-17 14:14:30 +02:00
Alessandro Ghedini ec46ad79e2 TOOLS/zsh.pl: properly set the return value
The previous commit made the completion script always return non-zero, even when
a match is found. This explicitly sets the return value to zero whenever a match
is found but defaults to non-zero in case nothing is matched.
2014-08-15 16:31:18 +02:00
c_14 d6ccc4d8de TOOLS/zsh.pl: properly return non-zero when no matches are found
Returning a non-zero value signals to the zsh completion system that no matches
were added by the script so that it can try the user-defined matchers (e.g.
those defined with matcher-list).

Fixes #1008.
2014-08-13 23:00:36 +02:00
Alexander Preisinger 8664dc3c47 wayland: pointer interface is created after theme
Just always load the theme. It gets freed properly and dosn't bother anyone.

Fixes #1012.

CC: @mpv-player/stable
2014-08-13 23:00:26 +02:00
wm4 51ce535822 player: don't quit with --loop-file
Fixes #1009.

CC: @mpv-player/stable
2014-08-13 23:00:21 +02:00
Alessandro Ghedini e33df9b7bc vdpau: correctly mark invalid mixer as such on vdp_video_mixer_create() failure
Otherwise vdp_video_mixer_destroy() would later fail when called on an invalid
video mixer handle. With mesa r600 vdpau driver, this would cause a segfault.
2014-08-13 19:16:53 +02:00
wm4 6310bd60e6 vo_vdpau: fix screenshots with anamorphic video
Fixes #1007.

CC: @mpv-player/stable
2014-08-13 10:55:49 +02:00
wm4 6729e39983 DOCS: mark the recent release in client-api-changes.rst
Also add instructions to release-policy.md, since this can be easily
forgotten.

CC: @mpv-player/stable
2014-08-13 10:55:44 +02:00
Alessandro Ghedini 88f247c0af Release 0.5.0 2014-08-12 10:50:38 +02:00
wm4 52f2f67553 input.conf: add some bindings for changing audio-delay
Requested on: https://github.com/mpv-player/mpv/commit/90ec3334174e80c16f00971886223a3afabc1aca#commitcomment-7331673

Might remove or remap them again later.
2014-08-11 23:40:35 +02:00
wm4 3f353f1219 input.conf: make explanatory text more readable
Or at leats this is the intention. It's a bit hard to tell which
information is needed, and which not.
2014-08-11 23:32:37 +02:00
wm4 b8f025a58a video: don't keep multiple pointers to hwdec info struct
This makes a certain corner case simpler at a later point.
2014-08-11 23:09:39 +02:00
wm4 056622c33e vaapi: fix uninitialized value read
Found with valgrind. This is somewhat terrifying, because the VA-API API
function is supposed to fill these values, and we access them only if
the API functions return success. So this shouldn't have happened.
2014-08-11 21:57:41 +02:00
wm4 beceb2fedc vf_vapoursynth: print more diagnostics on error 2014-08-11 21:57:15 +02:00
wm4 bead9766bc manpage: update keybindings 2014-08-11 21:56:37 +02:00
wm4 f9d2ad6c17 Move status-line.lua
Looks like TOOLS/lua/ is now established as dumping ground for random
Lua scripts, so DOCS/lua_examples/ is not needed anymore.
2014-08-11 17:08:32 +02:00
wm4 0ed48f5ec9 player: remove cache status from video OSD 2014-08-11 17:01:20 +02:00
wm4 9dd160ea97 demux: reduce log spam if threading is disabled 2014-08-11 15:55:12 +02:00
wm4 37357e2265 video: fix dangling pointer issue
The function video_decode_and_filter(), called between initializing the
local vf variable and using it, can actually destroy and recreate the
filter. Thus, the vf variable turns into a dangling pointer if that
happens.

Could be observed with: --hwdec=vda --deinterlace=yes --vf=yadif

(Also happens with vdpau/vaapi.)
2014-08-11 15:37:13 +02:00
wm4 33a8e2a9ad vdpau: allocate surfaces by age
Whether this helps is probably questionable and depends on the GPU. But
the vaapi code does it too.
2014-08-11 14:03:53 +02:00
wm4 ef9b399020 input: fix event wakeup
When a new event was added, merely a flag was set, instead of actually
waking up the core (if needed). This was ok in ancient times when all
event sources were part of the select() loop. But now there are several
cases where other threads can add input, and then you actually need to
wakeup the core in order to make it read the events at all.
2014-08-11 13:50:56 +02:00
wm4 bce48f30d2 input.conf: change LEFT/RIGHT keys to seek 5s instead of 10s 2014-08-11 13:37:18 +02:00
wm4 a87005b7fa command: for OSD, format cache property as integer
For convenience. Use ${=cache} to get the old formatting.
2014-08-11 13:30:08 +02:00
wm4 b016f92309 command: fix dangling pointer issue in script key bindings code
This code was sending a string to a different thread, and then
deallocated the string shortly after, which means most of the time
the other thread was accessing a dangling pointer.

It's possible that this is the cause for #1002.
2014-08-11 13:00:57 +02:00
wm4 ece61bfa10 player: don't delay OSD redraw when paused
We want this heuristic to trigger during normal playback only.
2014-08-10 13:24:29 +02:00
wm4 f5257e2c7d vo_sdl: fix redrawing issue
vo_sdl.c has broken event handling and just polls. The polling time was
quite low, so the playloop OSD redrawing heuristic inhibited redraws,
which made the window appear frozen when paused.
2014-08-10 13:24:29 +02:00
wm4 2a403092cd input.conf: map shift+pgup/dwn to the old seek bindings 2014-08-09 02:24:10 +02:00
wm4 8dfe0c73c9 video: remove "hard" framedrop mode
Completely useless, and could accidentally be enabled by cycling
framedrop modes. Just get rid of it.

But still allow triggering the old code with --vd-lavc-framedrop, in
case someone asks for it. If nobody does, this new option will be
removed eventually.
2014-08-09 00:35:35 +02:00
wm4 91be5e5c31 etc/example.conf: update cache options
Some of them changed semantics or got renamed.

Note that the replacements in the example.conf are not necessarily the
equivalents of the replaced options.
2014-08-09 00:13:16 +02:00
wm4 26d69758f6 command: run OSD display code even if a property is unavailable
Trying to jump chapters in a gile that has no chapters does nothing,
not even show a warning. This is confusing. The reason is that the
"add chapter" command will just bail out completely if the property
is unavailable.

This was because it exited when it couldn't get the property type.
Instead of exiting, just don't enter the code that needs the type.

(I'm not sure when this behavior changed. I consider it a regression.
It was probably caused by changes to the chapter code, which perhaps
started returning UNAVAILABLE instead of OK if there are no chapters.)
2014-08-09 00:08:04 +02:00
wm4 e0fb2a4f2d etc: add mplayer-input.conf 2014-08-09 00:08:04 +02:00
wm4 8b48b3fd3b input.conf: switch chapter seek next/previous keys
"UP" seeks forward, so "PGUP" should skip to the next chapter.

Fixes #998.
2014-08-08 23:26:03 +02:00
wm4 c4feba847c manpage: update key bindings 2014-08-08 12:06:01 +02:00
Alexander Preisinger b6b8bffd2d manpage: add sections and order by usage
Split the options into the following sections:

 * Playback Control
 * Program Behaviour
 * Video
 * Audio
 * Subtitles
 * Window
 * Disc Devices
 * Equalizer
 * Demuxer
 * Input
 * OSD
 * Screenshot
 * Software Scaler
 * Terminal
 * TV
 * Cache
 * Network
 * DVB
 * PVR
 * Miscellaneous

Most options are sorted by usefullness and how often they're used or how
important they are.
This makes finding the right options easier and adds some sort of structure.
2014-08-08 08:30:06 +02:00
wm4 b4f72b46e5 ao_dsound: reduce default buffer size
Reduce from 1000ms to 100ms. Since there is an audio thread updating AOs
quickly enough now, requesting such a large buffer size makes no sense
anymore.
2014-08-08 01:56:23 +02:00
wm4 90ec333417 input.conf: unmap some more obscure bindings
Changing audio-delay is probably not needed.

Changing balance "works", but not as expected (sets up a pan matrix to
change left/right contributions to each other, rather than changing the
relative volumes of each channel).

I expect that the rest are not in use by anyone.
2014-08-08 00:43:14 +02:00
wm4 fa682af6ee command: add a "seeking" property
The client API exports this state via events already, but maybe it's
better to explicitly provide this property in order to facilitate use on
OSD and similar cases.
2014-08-08 00:16:20 +02:00
wm4 5f042864f5 player: indicate on the status line whether a seek is active
This is delayed by 300ms - before that, the status doesn't change. I
feel like it would too annoying if the status line would "flicker" on
normal seek by quickly showing and hiding the indicator.
2014-08-08 00:05:24 +02:00
wm4 e1ff914679 input.conf: remap pgup/dwn to chapter seeks
As discussed in #973.

Keep the old bindings for now; there's no reason to unmap them yet.
2014-08-07 23:55:09 +02:00
wm4 7898169484 build: add hacks to force waf to generate valid .pc files
Don't like this? You're invited to fix this crap.
2014-08-07 23:45:40 +02:00
wm4 5906041343 input: fix off by one error in command parser
Should fix #989.
2014-08-07 18:21:36 +02:00
wm4 886ac09699 audio: fix encoding mode
If this code is not skipped, encoding (or dumping with --ao=pcm) will
attempt to adjust video timing to audio. Since another commit (0cce8fe6)
already avoids writing audio ahead, this didn't slow down encoding to
realtime, but it was still significantly slower.

This change should actually remove all extra sleeping.
2014-08-07 18:07:43 +02:00
wm4 483595e2d4 client API: qtexample: don't pass deallocated strings to mpv
The temporary QByteArray is deallocated already at the end of the
statement in C++, instead of the end of the scope (like in C).
2014-08-07 00:35:38 +02:00
wm4 1483fd443a demux: fix playback abort if --demuxer-thread is not used
Switching tracks caused cached_demux_control() to catch the command to
switch tracks, even if no thread was running. Thus, the tracks were
never really switched, and EOF happened immediately on playback start.

Fix it by not using the cache at all if the demuxer thread is disabled.
The cache code still has to be called somewhere, though, because it
handles stream metadata update.

Regression from today.
2014-08-07 00:34:14 +02:00
wm4 af57444bcb player: remove unused declaration 2014-08-07 00:30:43 +02:00
wm4 dfc5d96916 gitignore: fix manpage entries 2014-08-06 20:35:55 +02:00
wm4 41ff8a661b old-build: HAVE_COCOA_APPLICATION must be set
Also fix a typo in the Makefile manpage rule.
2014-08-06 20:33:51 +02:00
wm4 3234f5a123 stream_dvb: restore --dvbin-file option
Got lost some time ago. Although I'm not sure if it actually does
anything on MPlayer (it exists there, but might broken, not sure).

Fixes #988 (untested).
2014-08-06 20:30:48 +02:00
wm4 796db66174 stream_dvb: fix channels.conf preference order
channels.conf was never used (even though its config path was resolved).

Also remove the non-sense with the access() call.

Fixes #987 (untested).
2014-08-06 20:30:47 +02:00
wm4 19fd67096b vo_direct3d: allow resizing before video init
This can just happen in the time between VO creation, and the first call
to vo_reconfig. It seems the recent threading changes exposed this bug.

Fixes #986.
2014-08-06 20:30:47 +02:00
wm4 77ad49411a win32: never call GetClientRect(0, ...)
Sometimes GetClientRect() appeared to fail during init, and since we
don't check GetClientRect() calls (because they're on our own window,
and logically can never fail), bogus resizes were triggered. This could
cause vo_direct3d to fail initialization.

The reason was that w32->window was set to 0 during early window
initialization: CreateWindow*() can send messages to the new window,
even though it hasn't returned yet. This means w32->window is not yet
set to our window handle, and functions in WndProc may accidentally pass
hwnd=0 to win32 API functions.

Fix it by initializing w32->window on opportunity. This also means we
always strictly expect that the WndProc is used with our own window
only.
2014-08-06 20:30:47 +02:00
wm4 64e1132d39 demux: make track switching asynchronous
Because why not.

This can lead to reordering of operations between seeking and track
switching (happens when the demuxer wakes up after seek and track
switching operations were queued). Do the track switching strictly
before seeks if there is a chance of reordering, which guarantees that
the seek position will always start with key frames. The reverse
(seeking, then switching) does not really have any advantages.

(Not sure if the player relies on this behavior.)
2014-08-06 20:30:47 +02:00
wm4 b38ac38aec client API: trigger MPV_EVENT_VIDEO_RECONFIG on vf recreation
Until now, it was done only on VO reconfig, but this easily can miss
some events, in case the VO output format doesn't change.
2014-08-06 20:30:47 +02:00
wm4 be64535a4e audio: fix inverted condition
Recent regression. Could perhaps make gapless audio fail to work
correctly.
2014-08-06 20:30:46 +02:00
Alexander Preisinger 6ec39b4641 wayland: remove redundant line 2014-08-06 12:58:39 +02:00
Alexander Preisinger f175f2c38f wayland: changes for nested compositors
Adds necessary checks for nested compositors which only have limited
interfaces. Might also be useful for other minimal compositors.
2014-08-06 12:55:51 +02:00
Stefano Pigozzi 2e241c0a47 build: allow to disable building the cplayer 2014-08-06 09:31:59 +02:00
Stefano Pigozzi 212374149f build: expose waf variants to the user
This allows the user to execute multiple configuration and build steps. It
can be used for several scenarios where you need different compiler flags.
2014-08-06 09:31:59 +02:00
Stefano Pigozzi c8cf864bff cocoa: fix build by using the correct HAVE_* macro
This builds but both the libmpv example and the cplayer block infinitely when
building libmpv. That's because we wait inifinitely in `dispatch_sync` as
there's no event loop in the main thread that allows for libdispatch to work..

Whiel we are at it, we should probably investigate how to use mp_dispatch
instead since it is a little lower level and could give us higher control in
building and event loop.
2014-08-06 09:31:59 +02:00
FRAU KOUJIRO b8a431c892 cocoa: macosx_application needs cocoa-application 2014-08-06 09:31:59 +02:00
FRAU KOUJIRO 022a43de8e build: cocoa-application config target
I had to put it after video options because it depends on cocoa;
is there anywhere better to put it rather than making a new group?
2014-08-06 09:31:59 +02:00
FRAU KOUJIRO 0be25a8dbf cocoa: move handleFilesArray: to macosx_events 2014-08-06 09:31:58 +02:00
FRAU KOUJIRO 4fc8dcde2a cocoa: move set_input_context to macosx_events 2014-08-06 09:31:58 +02:00
FRAU KOUJIRO 78a2d99de1 cocoa: sync inputContext inside EventsResponder 2014-08-06 09:31:58 +02:00
FRAU KOUJIRO 78a266d599 cocoa: decouple events from application somewhat 2014-08-06 09:31:58 +02:00
wm4 ff6c387a73 win32: fix build 2014-08-05 23:43:40 +02:00
wm4 5a0db9bf8a build: move def file to libmpv/
This is more consistent with mpv.pc, which is also in libmpv/.
2014-08-05 20:02:41 +02:00
wm4 210c83ab31 win32: create OpenGL context on the GUI thread
This fixes the fullscreen issues on Intel for me. I'm baffled by it and
don't understand why this suddenly works. Intel drivers being shit?
Windows being shit? HWND or HDC being only 97% thread-safe instead of
98%? Me missing something subtle that is not documented anywhere?

Who knows.

Now instead of creating the HDC and OpenGL context on the renderer
thread, they're created on the GUI thread. The renderer thread will
then only call wglMakeCurrent, SwapBuffers, and OpenGL standard
functions.

Probably fixes github issue #968.
2014-08-05 20:02:23 +02:00
wm4 afcbee8305 win32: cleanup OpenGL context creation
Might handle some corner cases better. In particular, always call
wglMakeCurrent() with non-NULL HDC argument.
2014-08-05 19:59:32 +02:00
Stefano Pigozzi 8af0d9fec8 build: syms: reindent
We avoided reindenting this in the past to allow merging upstream changes.
In hindsight these are very unlikely and we are actually doing changes on
the code, so it's better to have the correct indentation and formatting in
our source file.
2014-08-05 12:57:34 +02:00
Stefano Pigozzi b34621e1b1 build: syms: fix *.def file for mach-o binary format
There was a missing trailing newline which caused some warnings when calling
`ld`.
2014-08-05 12:13:27 +02:00
wm4 9a6d7af074 options: fix key-value-list options
The parser can be called with dst (the target) set to NULL if the option
should be verified only. The code didn't respect this, and could result
in crashes when used in config profiles or filter sub-options.

Fixes #981.
2014-08-05 02:24:45 +02:00
wm4 43ddf2099b client API: add and use the MPV_MAKE_VERSION macro
This is probably nicer. The actual version number doesn't change (other
than the minor being incremented).

The "| 0UL" is to make the type unsigned long int, like it was before.
2014-08-05 02:23:14 +02:00
wm4 bdf607ea5f build: list exported symbols explicitly
Instead of using a regex to match names to be exported from the libmpv
dynamic shared library, use a libmpv.def file, which lists all exported
functions explicitly.

This reduces the platform specifics in syms.py. I'm not sure if the
separate compile_sym task is still needed (it could probably be
collapsed, which would concentrate the platform specifics into one
place).
2014-08-05 02:21:29 +02:00
wm4 62a79ae556 build: fix export of libmpv symbols on mingw
The _ usually prefixed to functions on Windows was unexpectedly missing.
2014-08-05 01:02:54 +02:00
wm4 91fbaa7d34 build: fix cross-compilation of libmpv
libmpv requires nm for creating the list of exported symbols (this is
done in syms.py). We should probably just make this list static instead,
but since this involves platform-specific code, for now this quick-fix
will do.
2014-08-05 00:20:31 +02:00
wm4 d634f7b01a vdpau: don't crash on flipped images
It seems the vdpau API does not support these.

Do a semi-expensive emulation of it. On the other hand, it's not like
this is a commonly-used feature. (It might be better to make --vf=flip
always copy instead of flipping it via pointer tricks - but everything
allows flipped images, and even decoders or libavfilter filters could
output them.)
2014-08-05 00:18:57 +02:00
wm4 ee6df2f76c sub: fix subtitle timing for TS
The subtitle timing logic always used the demuxer's start time as video
offset. This made external subtitle files "just work" with file formats
like TS, which usually have a non-0 start time. But it was wrong for
subtitles muxed with the TS, so adjust the time offset explicitly with
external files only.
2014-08-04 18:17:30 +02:00
wm4 922b1d8504 sub: don't read packets if video and audio are disabled
Although disabling both video and audio is surely an obscure corner
case, it's allowed, and we don't want the demuxer to skip arbitrary
packets.

Basically, make the heuristic for checking interleaved files affect
external files only.
2014-08-04 18:17:30 +02:00
Michael Forney 618361c697 gl_wayland: Don't try to resize the egl_window before it exists
If the compositor sends a configure event immediately after a window is
created (for example, if it implements tiling window management), mpv
will attempt to call wl_egl_window_resize before it has actually created
the egl_window, causing a crash.
2014-08-04 09:50:24 +02:00
wm4 913b49fcbc input.conf: remap 2 keys
Nobody uses 'c' (except accidentally) - remove.

Everyone agrees that OSD level cycling on 'o' is dumb, so map it to
show_progress instead. Cycling the OSD level is now available on 'O'.
No reason to ummap 'P' yet.

Also see issue #973.
2014-08-03 21:15:33 +02:00
wm4 0f5aed9c62 player: some further playloop cleanups
Handle --term-playing-msg at a better place.

Move MPV_EVENT_TICK hack into a separate function. Also add some words
to the client API that you shouldn't use it. (But better leave breaking
it for later.)

Handle --frames and frame_step differently. Remove the mess from the
playloop, and do it after frame display. Give up on the weird semantics
for audio-only mode (they didn't make sense anyway), and adjust the
manpage accordingly.
2014-08-03 20:31:00 +02:00
wm4 0ddbf92d06 player: make quit exit immediately
Stopping playback canceled waiting, but executed the remainder of the
playloop, including things like executing pointless seeks.
2014-08-03 20:30:39 +02:00
wm4 575314eafa player: allow redrawing screen during seeks
If seeks take very long, it's better not to freeze up the display.

(This doesn't handle the case when decoding video frames is extremely
slow; just if hr-seek is used, or the demuxer is threaded and blocks on
network I/O.)
2014-08-03 20:30:34 +02:00
wm4 27301ee691 manpage: be more explicit where input.conf is located 2014-08-02 06:57:36 +02:00
wm4 d68a759fa4 Improve setting AVOptions
Use OPT_KEYVALUELIST() for all places where AVOptions are directly set
from mpv command line options. This allows escaping values, better
diagnostics (also no more "pal"), and somehow reduces code size.

Remove the old crappy option parser (av_opts.c).
2014-08-02 03:12:33 +02:00
wm4 0c6c2da8bc manpage: explain how to replace --slave-broken 2014-08-02 03:12:33 +02:00
wm4 c94e8bcdd6 player: don't show the path part for external subtitle files
Show the filename only. Feature request on IRC.
2014-08-02 03:12:31 +02:00
wm4 733bdebcb9 client API: minor optimizations for property notification
Internally, there are two mechanisms which can trigger property
notification as used with "observed" properties in the client API.

The first mechanism associates events with a group of properties that
are potentially changed by a certain event. mp_event_property_change[]
declares these associations, and maps each event to a set of strings.
When an event happens, the set of strings is matched against the list of
observed properties of each client. Make this more efficient by
comparing bitsets of events instead. This way, only a bit-wise "and" is
needed for each observed property. Even better, we can completely skip
clients which have no observed properties that match.

The second mechanism just updates individual properties explicitly by
name. Optimize this by using the property index instead. It would be
nice if we could reuse the first mechanism for the second one, but
there are too many properties to fit into a 64 bit mask.

(Though the limit on 64 events might get us into trouble later...)
2014-08-02 01:53:22 +02:00
wm4 82a223e4e0 TOOLS: add test script for property change notifications 2014-08-02 01:53:21 +02:00
wm4 be337aa415 command: add a property that returns a list of all properties
Also remove the undocumented Lua mp.property_list() function.
2014-08-02 01:53:21 +02:00
wm4 16e5ec88e1 manpage: remove duplicated misplaced description of a command 2014-08-02 01:53:18 +02:00
wm4 aa46968ed0 win32: fix assertion failure
If OpenGL 3.x doesn't work, the fallback to legacy GL will call the
function to create the DC again, and it will assert. Just make it not
to, and also simplify the code a bit.

Fixes #974.
2014-08-01 23:03:52 +02:00
wm4 bf5b1e9a05 Remove the last remains of slave mode
Almost nothing was left of it.

The only thing this commit actually removes is support for reading
input commands from stdin. But you can emulate this via:

 --input-file=/dev/stdin --input-terminal=no

However, this won't work on Windows. Just use a named pipe.
2014-08-01 22:57:56 +02:00
Stefano Pigozzi 6aac17cebb vda: only support the new hwaccel 1.2 API (remove old code)
Since the new hwaccel API is now merged in ffmpeg's stable release, we can
finally remove support for the old API.

I pretty much kept lu_zero's new code unchanged and just added some error
printing (that we had with the old glue code) to make the life of our users
less miserable.
2014-08-01 10:38:18 +02:00
Stefano Pigozzi 33137606e9 travis: use homebrew to build ffmpeg on OS X
This allows us to use a newer version of ffmpeg and to test the build of our
VDA code.
2014-08-01 10:38:18 +02:00
wm4 33f465d9a7 player: don't ignore first chapter
It's a mystery why this was done this way. If the first chapter starts
later than the current position, we do have to return -1.
2014-07-31 22:54:57 +02:00
wm4 de8e9ca9d2 video: fix attached picture mode
Playing audio files with embedded cover art broke due to some of the
recent changes. Treat video EOF properly, and don't burn the CPU.
Disable hrseek for video in attached picture mode, since the decoder
will always produce a new image, which makes hrseek never terminate.

Fixes #970.
2014-07-31 21:57:11 +02:00
wm4 4cf3f3ca2c audio: simplify condition
The expression added with the previous commit (0cce8fe6) looked slightly
more complicated than it has to be. The code is equivalent.
2014-07-31 21:11:49 +02:00
wm4 0cce8fe64f audio: fix A/V sync in encoding mode
In encoding mode, the AO pretends to be infinitely fast (it will take
whatever we write, without ever rejecting input). Commit 261506e3 broke
this somehow. It turns out an old hack dealing with this was accidentally
dropped.

This is the hunk of code whose semantics were (partially) dropped:

    if (mpctx->d_audio && (mpctx->restart_playback ? !video_left :
                           ao_untimed(mpctx->ao) && (mpctx->delay <= 0 ||
                                                     !video_left)))
    {
        int status = fill_audio_out_buffers(mpctx, endpts);
        // Not at audio stream EOF yet
        audio_left = status > -2;
    }

This if condition is pretty wild, and it looked like it was pretty much
for audio-only mode, rather than subtle handling for encoding mode.
2014-07-31 04:49:44 +02:00
wm4 a1b54d3b89 client API: don't send internal events to the clients
"Internal" events were added in the previous commits to leverage the
client API property mechanism, without making weird properties public.

But they were sent to clients too (and returned by mpv_wait_event()).
2014-07-31 04:35:23 +02:00
wm4 c1b64cc693 command: add cache-idle property 2014-07-31 04:25:39 +02:00
wm4 b4f24544bb client API: make "cache" property and similar observable
Achieve this by polling. Will be used by the OSC. Basically a bad hack -
but the point is that the mpv core itself is in the best position to
improve this later.
2014-07-31 04:19:41 +02:00
wm4 aaa90e1a33 client API: fix deadlock when calling mpv_terminate_destroy before init
This is perfectly allowed, but was ignored, because it's a corner case.

It doesn't actually wait for other clients to be destroyed, but on the
other hand I think there's no way to have other clients before
initialization.

CC: @mpv-player/stable
2014-07-31 03:12:18 +02:00
wm4 dacbaea1a8 player: rename a variable
Make it clear that this condition happens when switching to a new
timeline segment. It doesn't even need to coincide with a chapter.
2014-07-30 23:29:01 +02:00
wm4 1423bd0bfd player: move video display code out of the playloop
Basically move the code from playloop.c to video.c. The new function
write_video() now contains the code that was part of run_playloop().

There are no functional changes, except handling "new_frame_shown"
slightly differently. This is done so that we don't need new a new
MPContext field or a return value for write_video() to signal this
condition. Instead, it's handled indirectly.
2014-07-30 23:29:00 +02:00
wm4 8f2e9f1d61 player: split seek_reset()
This also reduces some code duplication with other parts of the code.

The changfe is mostly cosmetic, although there are also some subtle
changes in behavior. At least one change is that the big desync message
is now printed after every seek.
2014-07-30 23:29:00 +02:00
wm4 c38013bffb video: actually flush filter chain
Frames buffered in filters weren't flushed, so on EOF, the last frames
were dropped, depending on how much filters buffered. Oops.

Test case: "mpv something.jpg --vf=buffer"
2014-07-30 23:29:00 +02:00
wm4 f8ab732ac3 video/filter: add vf_buffer
Mostly useful for debugging.
2014-07-30 23:29:00 +02:00
wm4 8d990408d7 audio: better sync behavior on bogus EOF
In situations when the demuxer reports EOF, but immediately "recovers"
after that and returns new data, it could happen that audio sync was
skipped. Deal with this by actually entering the EOF state, instead of
assuming this will happen later.
2014-07-30 23:29:00 +02:00
wm4 6afa1a2afc ao_alsa: disable use of non-interleaved formats by default
Some ALSA plugins take non-interleaved audio, but treat it as
interleaved, which results in various funny bugs. Users keep hitting
this issue, and it just doesn't seem worth the trouble.

CC: @mpv-player/stable
2014-07-30 23:28:44 +02:00
Stefano Pigozzi c3d15b50b4 cocoa: fix key equivalent dispatching
Prior to this commit we had a list of key modifiers and checked against that.
Actually, the Cocoa framework has a built in way to do it and it involves
calling performKeyEquivalent: on the menu instance.

Fixes #946

cc @mpv-player/stable: this should apply with no conflicts
2014-07-30 11:26:49 +02:00
wm4 471dfba018 client API: fix typos in documentation 2014-07-30 03:33:37 +02:00
wm4 0dd5228626 demux_lavf: don't consider EAGAIN as EOF condition
This happens apparently randomly with rtmp:// and after seeks. This
eventually leads to audio decoding returning an EOF status, which
basically disables audio sync. This will lead to audio desync, even if
audio decoding later "recovers" when the demuxer actually returns audio
packets.

Hack-fix this by special-casing EAGAIN.
2014-07-30 03:32:56 +02:00
wm4 6856d81c68 stream: hack-fix rtmp-level seeking
This didn't work, because the timebase was wrong. According to the
ffmpeg doxygen, if the stream index is -1 (which is what we used), the
timebase is AV_TIME_BASE. But this didn't work, and it really expected
the stream's timebase. Quite "surprising", since this feature
(avio_seek_time) is used by rtmp only.

Fixing this properly is too hard, so hack-fix our way around it.
STREAM_CTRL_SEEK_TO_TIME is also used by DVD/BD, so a new
STREAM_CTRL_AVSEEK is added. We simply pass-through the request
verbatim.
2014-07-30 02:21:51 +02:00
wm4 26d973ce82 stream_lavf: allow setting AVOptions with --stream-lavf-o
This commit also creates a private option struct for stream_lavf.c, but
since I'm lazy, I'm not moving any existing options to it.
2014-07-30 01:15:42 +02:00
wm4 da780309d7 audio: better initial sync for files where audio starts later
Some files have the first audio much later into the video (for whatever
reasons). Instead of appending large amounts of silence to the audio
buffer (and refusing to sync if the audio to append is "too large"),
just wait until enough video has played.
2014-07-30 00:40:45 +02:00
wm4 1cd2b5976d audio: cosmetics: remove unused return value 2014-07-30 00:24:57 +02:00
wm4 6b928fa2a0 demux_mf: allow seeking past the end
How's this for a corner case.
2014-07-30 00:23:16 +02:00
wm4 593ad996e0 player: fix time display wheen seeking past EOF with --keep-open
Regression since commit 261506e3. Internally speaking, playback was
often not properly terminated, and the main part of handle_keep_open()
was just executed once, instead of any time the user tries to seek. This
means playback_pts was not set, and the "current time" was determined by
the seek target PTS.

So fix this aspect of video EOF handling, and also remove the now
unnecessary eof_reached field.

The pause check before calling pause_player() is a lazy workaround for
a strange event feedback loop that happens on EOF with --keep-open.
2014-07-30 00:22:38 +02:00
wm4 b6a7c321db player: let explicitly imprecise seeks cancel precise seeks
If an imprecise seek is issues while a precise seek is ongoing,
don't wait up to 300ms (herustistic which usually improves user
experience), but instead let it cancel the seek.

Improves responsiveness of the OSC after the previous commit.

Note that we don't do this on "default-precise" seeks, because we
don't know if they're going to be precise or not.
2014-07-29 20:04:08 +02:00
ChrisK2 48f4f791f4 osc: Do precise seeks on simple clicks on seekbar 2014-07-29 19:26:32 +02:00
wm4 63d1d53d2f audio: ignore (some) decoding errors on initialization
It probably happens relatively often that the first packet (or even the
first N packets) of a stream will fail to decode, but decoding will
eventually succeed at a later point. Before commit 261506e3, this was
handled by an explicit retry loop (although this was also for other
purposes), but with then was changed to abort on the first error. This
makes it impossible to decode some audio streams.

Change this so that errors are ignored for the first 50 packets, which
should make it equivalent to the old code.
2014-07-29 18:05:55 +02:00
wm4 862d7d8a1a player: fix desync when seeking and switching external tracks
If you for example use --audio-file, disable the external track, seek,
and enable the external track again, the playback position of the
external file was off, and you would get major A/V desync. This was
actually supposed to work, but broke at some time ago (probably commit
2b87415f). It didn't work, because it attempted to seek the stream if it
was already selected, which was always true due to
reselect_demux_streams() being called before that.

Fix by putting the initial selection and the seek together.
2014-07-29 17:55:28 +02:00
wm4 6d15c491b0 player: remove a pointless field 2014-07-29 01:00:54 +02:00
wm4 25ceb3289d player: disable hr-seek in .ts files
Seeking in .ts files (and some other formats) is too unreliable, so
there's a separate code path for this case. But it breaks hr-seek.

Maybe hr-seek could actually be enabled in this case if we're careful
enough about timestamp resets, but for now nothing changes.
2014-07-29 00:59:00 +02:00
wm4 d6445d7ab6 player: allow precise seeking with percent seeks
I'm not sure why this was explicitly disabled. It's from mplayer2 times.
2014-07-29 00:26:52 +02:00
wm4 ecad4a20de manpage: lua: document timer timeout and oneshot fields
Might be useful for scripts, so document them. (Which means scripts
are allowed to use them, without risking breakage.)
2014-07-29 00:22:21 +02:00
wm4 fa34b4920a vdpau: don't upload video images in advance
With software decoding, images were uploaded to vdpau surfaces as they
were queued to the VO. This makes it slightly more complicated
(especially later on), and has no advantages - so stop doing it.

The only reason why this was done explicitly was due to attempts to keep
the code equivalent (instead of risking performance regressions). The
original code did this naturally for certain reasons, but now that we
can measure that it has no advantages and just requires extra code, we
can just drop it.
2014-07-29 00:21:02 +02:00
wm4 d563dc8cb9 player: update playback position on seek
If the actual PTS is not known yet right after a seek, the "time-pos"
property will just return the seek target PTS. For this purpose, trigger
a change event to make the client API update the "time-pos" and related
properties. (MPV_EVENT_TICK triggers this update.)
2014-07-29 00:20:54 +02:00
wm4 846257da13 player: logically speed up seek logic
Commit 261506e3 made constant seeking feel slower, because a subtle
change in the restart logic makes it now waste time showing another
video frame. The slowdown is about 20%.

(Background: the seek logic explicitly waits until a video frame is
displayed, because this makes it easier for the user to search for
something in the video. Without this logic, the display would freeze
until the user stops giving seek commands.)

Fix this by letting the seek logic issue another seek as soon as the
first video frame is displayed. This will prevent it from showing a
(useless, slow) second frame. Now it seems to be as fast as before the
change.

One side-effect is that the next seek happens after the first video
frame, but _before_ audio is restarted. Seeking is now silent. I guess
this is ok, so we don't do anything about it. Actually, I think whether
this happens is probably random; the seeking logic simply doesn't make
this explicit, so anything can happen.
2014-07-28 22:13:42 +02:00
wm4 26bfcc9c82 vo_x11: fix build with older Libav versions
Why does this happen everytime...
2014-07-28 21:48:43 +02:00
Niklas Haas d0a8b571c9 video: add a default color space for files with no video
Usually mp_image_params_guess_csp takes care of finding *some* default
for a video channel, but files with no video (or with extremely broken
configurations) end up leaving the colorspace information as
MP_CSP_PRIM_AUTO, which has no associated primaries.

As a result of this, color managed OSD messages could not display
because they were being color managed to match the (non-existing/absurd)
video channel. With this change, such non-spaces will have BT.709
primaries as far as color management and the OSD is concerned.

This fixes #961.

CC: @mpv-player/stable

Signed-off-by: wm4 <wm4@nowhere>
2014-07-28 21:24:44 +02:00
wm4 261506e36e audio: change playback restart and resyncing
This commit makes audio decoding non-blocking. If e.g. the network is
too slow the playloop will just go to sleep, instead of blocking until
enough data is available.

For video, this was already done with commit 7083f88c. For audio, it's
unfortunately much more complicated, because the audio decoder was used
in a blocking manner. Large changes are required to get around this.
The whole playback restart mechanism must be turned into a statemachine,
especially since it has close interactions with video restart. Lots of
video code is thus also changed.

(For the record, I don't think switching this code to threads would
make this conceptually easier: the code would still have to deal with
external input while blocked, so these in-between states do get visible
[and thus need to be handled] anyway. On the other hand, it certainly
should be possible to modularize this code a bit better.)

This will probably cause a bunch of regressions.
2014-07-28 21:20:37 +02:00
wm4 58255e0e2b input: be stricter about rejecting mouse input with --no-input-cursor
Apparently this switch means all mouse input should be strictly
rejected. Some VO backends (such as X11) explicitly disable all mouse
events if this option is set, but others don't. So check them in
input.c, which increases consistency.
2014-07-27 22:00:55 +02:00
wm4 4c533fbb16 vo: remove vo_mouse_movement() wrapper
So that VO backends don't have to access the VO just for that.
2014-07-27 21:53:29 +02:00
wm4 89391e7c94 vo: different hack for VOs which need to mangle mouse input
Follow up on commit 760548da. Mouse handling is a bit confusing, because
there are at least 3 coordinate systems associated with it, and it
should be cleaned up. But that is hard, so just apply a hack which gets
the currently-annoying issue (VO backends needing access to the VO) out
of the way.
2014-07-27 21:33:11 +02:00
wm4 bc6359313f ao_pulse: allow disabling timing bug workarounds
Add an option that enables using native PulseAudio auto-updated timing
information, instead of the manual calculations added in mplayer2 times.
You can use --ao=pulse:no-latency-hacks to enable the new code. The code
is almost the same as the code that was removed with commit de435ed5,
but I didn't readd some bits I didn't understand. Likewise, the option
will disable the code added with that commit.

In my tests this seemed to work well, though the A/V sync display looks
funny when seeking.

The default is still the old behavior.

See issue #959.
2014-07-26 23:20:09 +02:00
wm4 77d9e4b8a9 ao_pulse: remove hacks for ancient PulseAudio versions
This was needed by very old (0.9) versions only. Get rid of it.

Unfortunately, I can't cross-check with the original bug report, since
the bug URL leads to this:

Internal Server Error

TracError: IOError: [Errno 2] No such file or directory: '/home/lennart/svn/trac/pulseaudio/VERSION'
2014-07-26 23:19:48 +02:00
wm4 3c7675ae9a manpage: eliminate the word "movie"
It's evil and sounds outdated. Use the words "media" and "video"
instead.

Closes #935.
2014-07-26 23:19:47 +02:00
wm4 025431a7ae win32: remove minor non-sense
This is done in the wrong thread. Was harmless and had no effect at all.
2014-07-26 23:19:43 +02:00
wm4 97a82bcd56 win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.

In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)

This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.

One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.

Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 20:36:46 +02:00
wm4 760548da28 vo: hack to avoid threading issues with mouse input
VO backends which are or will run in their own thread have a problem
with vo_mouse_movement() calling vo_control(). Restrict this to VOs
which actually need this.
2014-07-26 20:31:26 +02:00
wm4 3f268cc4f2 win32: use gcc TLS instead of WM_NCCREATE tricks for window context
win32 does not provide a proper per-window context pointer. Although it
does allow passing a user-chosen value to WM_CREATE/WM_NCCREATE, this
is not enough - the first message doesn't even have to be WM_NCCREATE.
This gets us in trouble later on, so go the easy route and just use a
TLS variable.

__thread is gcc specific, but Windows is a very "special" platform
anyway. We support only MinGW and Cygwin on it, so who cares. (C11
standardizes __thread as _Thread_local; we can use that later.)
2014-07-26 20:30:52 +02:00
wm4 559fe1daac Add Plan 9-style barriers
Plan 9 has a very interesting synchronization mechanism, the
rendezvous() call. A good property of this is that you don't need to
explicitly initialize and destroy a barrier object, unlike as with e.g.
POSIX barriers (which are mandatory to begin with). Upon "meeting", they
can exchange a value.

This mechanism will be nice to synchronize certain stages of
initialization between threads in the following commit.

Unlike Plan 9 rendezvous(), this is not implemented with a hashtable,
because that would require additional effort (especially if you want to
make it actually scele). Unlike the Plan 9 variant, we use intptr_t
instead of void* as type for the value, because I expect that we will be
mostly passing a status code as value and not a pointer. Converting an
integer to void* requires two cast (because the integer needs to be
intptr_t), the other way around it's only one cast.

We don't particularly care about performance in this case either. It's
simply not important for our use-case. So a simple linked list is used
for waiters, and on wakeup, all waiters are temporarily woken up.
2014-07-26 20:29:48 +02:00
wm4 8ed6d298c5 win32: make a flag explicit
This shouldn't change anything. But it's worth making this explicit,
since it's very subtle and unintuitive: if the X parameter is the
magic value CW_USEDEFAULT, then the Y parameter is used as nCmdShow
parameter to ShowWindow(). And in our case, this is SW_HIDE (0),
because we want to create a hidden window.
2014-07-26 20:29:18 +02:00
wm4 9969694ece win32: simplify some --wid embedding code
This looked a bit overcomplicated. We don't care about the window
position (it should always be 0/0, unless the parent program moved it,
which it shouldn't). We don't care about the global on-screen position.
Also, we will just retrieve a WM_SIZE message if our window is resized,
and we don't need to update it manually.

The only thing we have to do is making sure our window fills the parent
window completely.
2014-07-26 20:28:36 +02:00
wm4 9a3f1f24ca win32: don't use CS_OWNDC
CS_OWNDC will make GetDC() always return the same HDC. This might
become a problem when OpenGL rendering and window management are
on different threads. Although I'm not too sure about this; our
code never requests a HDC outside of the OpenGL backend, and it
depends on whether win32 will internally request DCs. But in any
case, this seems to be cleaner.

Move the GL pixelformat setup code to gl_w32.c, where it's actually
needed. This also fixes that SetPixelFormat() should be called only
once, and not every time video params change.
2014-07-26 20:28:01 +02:00
wm4 7f0d9a95fa win32: remove worthless doxygen comments
These mostly describe self-explanatory things, and fail to explain
actually tricky things. Which means you just waste your time reading
this, and have to figure it out from the code anyway.
2014-07-26 20:27:57 +02:00
wm4 b3169390f5 win32: make private struct private, refactor
Preparation for moving win32 windowing to a separate thread.

The codesize is reduced a bit, because some small functions are
inlined, which reduces noise.

The main change is that now most functions use the private struct
directly, instead of accessing it indirectly through vo->w32.
Accesses to vo are minimalized.

The final goal is adding some sort of new windowing backend API. It
would be cleaner to use that as context pointer for all functions
(like struct vo was previously used), but since this is work in
progress, we just go with this commit.
2014-07-26 20:27:03 +02:00
wm4 7077526ffb ao_null: never fail at initialization
ao_null is used to stop autoprobing (if all AOs before fail to init).
After it come things like ao_pcm, which should never be automatically
selected.

Remove a certain theoretically possible failure case, and force "some"
fallback.
2014-07-26 20:26:57 +02:00
wm4 ac62244983 audio/out: fix initialization failure with win32
mp_make_wakeup_pipe() always fails on win32. If this call fails on Linux
(and e.g. ao_alsa is used), this will probably burn CPU since poll()
won't work on the invalid file descriptor, but whatever, the failure
case is obscure enough.
2014-07-26 20:26:27 +02:00
wm4 ef600041ba audio, client API: check mp_make_wakeup_pipe() return value
Could fail e.g. due to FD exhaustion.
2014-07-25 14:32:45 +02:00
wm4 18c432b83a osdep: don't assume errno is positive
Apparently this is not necessarily the case, so just drop the silly idea
that depended on this assumption.
2014-07-25 14:32:45 +02:00
wm4 f24f960ec7 command: fix and simplify overlay_add
Actually free the old mmap region when readding an overlay of the same
ID without removing it before. (This is explicitly documented as
working.)

Replace the OSD atomically. Before this commit, the overlays were
removed and then readded to avoid synchronization problems.

Simplify the code: now there is no weird mapping between index and ID.
The OSD sub-bitmap list still needs to be prepared to skip unused IDs
(since each sub-bitmap list entry must be in use), but the code for this
is relatively separated now.

Fixes issue #956.
2014-07-25 14:32:45 +02:00
Alessandro Ghedini 08415933db command: append entries to the end of the playlist with loadlist append
Currently entries are added after the current playlist element. This is kinda
confusing, more so given that "loadfile append" appends at the end of the
playlist.
2014-07-25 14:32:34 +02:00
wm4 623eac2b1b audio: cosmetics: collapse a function
There's no need for build_afilter_chain() to be a separate function
anymore.
2014-07-24 15:27:40 +02:00
wm4 69eb056333 audio: fix timestamps
Accidentally broken in b6af44d3. For ad_lavc (and in general), the PTS
was not updated correctly when filtering only parts of audio frames,
and for ad_mpg123 and ad_spdif the PTS was additionally offset by the
frame size.

This could lead to incorrect time display, and possibly broken A/V sync.
2014-07-24 15:27:31 +02:00
wm4 fc28e4af4d audio: adjust format change code
Execute the format change based on whether we logically detected EOF
(after filters), instead of when the decode buffer was drained. It's
slightly cleaner. (The requirement of len>0 existed before.)
2014-07-24 15:26:43 +02:00
wm4 986099d323 audio: fix race condition in EOF code
Don't return an EOF code if there's still buffered data.

Also, don't call demux_stream_eof() in the playloop. There's probably
nothing wrong with it, but it's cleaner not to use it.

Also give AD_EOF its own value, so that a decoding error doesn't drain
audio by causing an EOF condition.
2014-07-24 15:26:07 +02:00
wm4 b77dab0f6e audio: cosmetics
Move a function call, which does not change semantics.

Write the extra buffer sample count in a more straight-forward way; the
old code was not meaningful in any way (anymore).
2014-07-24 15:25:48 +02:00
wm4 6455bcc1da audio: remove unnecessary code
It's true that the decoder can successfully decode, but return no data
(for various reasons). We don't need to handle this specially, though.
We just let the decoder decode some more data. This doesn't increase the
danger of an endless loop either, because audio_decode() already calls
this function until enough is decoded.
2014-07-24 15:25:36 +02:00
wm4 cb4aa2df92 manpage: fix a typo 2014-07-24 15:25:24 +02:00
Rudolf Polzer c19ec6f6f6 encode: deal even more with codec->time_base deprecation.
I assume this works too with Libav 10 and FFmpeg d3e51b41.
2014-07-23 16:09:44 +02:00
wm4 843f5f4723 command: add append-play loadfile mode
"loadfile filename append-play" will now always append the file to the
playlist, and if nothing is playing yet, start playback. I don't want to
change the semantics of "append" mode, so a new mode is needed.

Probably fixes issue #950.
2014-07-23 00:20:53 +02:00
wm4 10debffc06 player: remove something DVD specific
This is not needed anymore, because demux_disc isolates us from this
crap.
2014-07-22 23:49:23 +02:00
wm4 870dc84839 sub: offset subtitle timing to video start PTS
This allows using external subtitle files with e.g. transport stream
files that don't start at time 0.

Note that if the .ts file has timestamp resets, everything goes south.
But I guess this was already the case before, unless there are external
subtitle files that also include timestamp resets, which is unlikely.
(On the other hand, you could for example expect that it works with
embedded DVB subtitles, that were somehow captured from the same stream
and use the same timestamps.)
2014-07-22 23:48:29 +02:00
wm4 aa1a383342 sub: add detection via BOM
Useful for Windows stuff. Actually, ENCA support should catch this, but,
well, whatever, everyone seems to hate ENCA.

Detection with BOM is trivial, although it needs some hackery to
integrate it with the existing autodetection support. For one, change
the default value of --sub-codepage to make this easier.

Probably fixes issue #937 (the second part).
2014-07-22 23:40:48 +02:00
wm4 63373ca424 encode: deal with codec->time_base deprecation
This seems to work with both Libav 10 and FFmpeg d3e51b41.
2014-07-22 23:04:12 +02:00
wm4 1041850523 video: fix corner case with accidental EOF
The video flushing logic was broken: if there are no more packets,
decode_image() will feed flush packets to the decoder. Even if an image
was produced, it will return the demuxer EOF state, and since commit
7083f88c, this EOF state is returned to the caller, which is incorrect.

Revert this part of the change, and explicitly check for VD_WAIT (the
bogus change was intended to forward this error code to the caller).

Also, turn the "r < 1" into something equivalent that doesn't rely on
the exact value of VD_EOF. "r < 0" is ok, because at least here, errors
are always negative.
2014-07-22 21:08:42 +02:00
wm4 8f8d524112 ass: remove some pointless ifdeffery
This will print compiler warnings about unused variables with older
libass versions, but that's harmless.
2014-07-22 20:49:01 +02:00
wm4 6b51b73a04 player: fix idle mode event handling 2014-07-22 19:33:24 +02:00
wm4 80d36a0aa2 ao_pulse: fix potential compilation problem
It seems at least on some platforms (OSX 10.9), the POSIX wait()
function becomes visible, and conflicts with this unrelated function.
Just rename it.
2014-07-22 19:26:53 +02:00
Stefano Pigozzi 12b2465c1e travis: add OS X continous integration
The travis guys were so nice to activate multi OS support for us (it's a beta
feature). So now we build on OS X ass well to check for OS X specific breakage.

Later I might investigate further and build with the minimum supported SDK
version so that we don't break older systems by using newer Cocoa features.
2014-07-21 20:43:39 +02:00
wm4 17256f13dc osd: properly handle OSD bar timeout
This could just remain stuck on the screen, until the playloop happened
to be run again.
2014-07-21 19:35:20 +02:00
wm4 a84517c1d8 player: don't sleep after seeks 2014-07-21 19:31:25 +02:00
wm4 b6af44d31e audio: move initial decode to generic code
This commit mainly moves the initial decoding of data (done to probe the
audio format) to generic code. This will make it easier to make audio
decoding non-blocking in a later commit.

This commit also changes how decoders return data: instead of having
them write the data into a prepared buffer, they return a reference to
an internal buffer (by setting dec_audio.decoded). This makes it
significantly easier to handle audio format changes, since the decoders
don't really need to care anymore.
2014-07-21 19:29:58 +02:00
wm4 1f9e0a15a1 ad_lavc: drop questionable fallback code
If the decoder didn't set a samplerate, it was initialized from the
container samplerate.

This probably didn't make much sense, because it's passed to the
decoder on initialization (so it could definitely use it). It's an
artifact from commit 66a9eb57 (which removed some Matroska-specific non-
sense), and I've never seen it actually happen since it was made into a
warning. Just get rid of it.
2014-07-21 19:29:58 +02:00
wm4 967add9f0f audio: remove unused metadata field
This was used for replaygain at some point, until replaygain info was
passed through explicitly.
2014-07-21 19:29:58 +02:00
wm4 d1bb1bf8af demux: fix timestamp type for seek calls
mpv/mplayer2/MPlayer use double for timestamps, but the demuxer API used
float.
2014-07-21 19:29:58 +02:00
wm4 4b4bd9e5f7 demux: asynchronous seeking
This tells the demuxer thread that it should seek, instead of waiting
until the demuxer thread is ready.

Care has to be taken about the state between seek request and actual
seeking: newly demuxed packets have to be discarded. We can't just
flush when doing the actual seek, because the user thread could read
these packets.

I'm wondering if this could lead to issues due to relaxed ordering of
operations. But it should be fine, since seeking influences packet
reading only, and seeking is always strictly done before that.

Currently, this will have no advantages; unless audio is disabled. Then
seeking as well as normal playback can be non-blocking.
2014-07-21 19:29:50 +02:00
wm4 b6dd1341d2 manpage: correct the --mf options
The MPlayer style syntax ("-mf fps=10:type=png") was removed a while
ago, and now only the flat variants ("--mf-fps=10" etc.) work.

CC: @mpv-player/stable
2014-07-21 19:27:15 +02:00
wm4 0248de84ea player: simplify a condition
Move a condition somewhere else, which makes it conceptually simpler.

Also, the assignment to full_audio_buffers removed with this commit was
dead, and its value never used.
2014-07-20 20:47:30 +02:00
wm4 ba621ac140 manpage: fix wording for --aid 2014-07-20 20:47:03 +02:00
wm4 da1925ae2e player: simplify logic on video errors
Fatal errors in the vidoe chain (such as failing to initialize the video
chain) disable video decoding. Restart the playloop, instead of just
continuing the current iteration.

The resulting behavior should be the same, but it gets rid of possible
corner cases.
2014-07-20 20:43:06 +02:00
wm4 9736f3309a audio: use symbolic constants instead of magic integers
Similar to commit 26468743.
2014-07-20 20:42:03 +02:00
wm4 e982b5b287 player: readd code accidentally removed with commit 61efe87e
Oops.
2014-07-20 20:41:20 +02:00
wm4 5526603a43 demux: don't start reading if no packets were requested yet
Instead of starting to fill the packet queue if at least 1 stream is
selected, wait until there is at least 1 stream had new packets
requested.

In theory this is cleaner, because it allows you to e.g. do a seek and
then reselect streams without losing packets. Seeking marks all streams
as inactive, and without this new logic, the thread would read new
packets anyway right after seek.
2014-07-20 20:13:08 +02:00
wm4 61efe87e48 player: fix regression with ordered chapters
Broken by commit 1301a907. This commit added demuxer threading, and
changed some other things to make them simpler and more orthogonal. One
of these things was ntofications about streams that appear during
playback. That's an obscure corner case, but the change made handling of
it as natural as normal initialization.

This didn't work for two reasons:
1. When playing an ordered chapters file where the initial segment was
not from the main file, its streams were added to the track list. So
they were printed twice, and switching to the next segment didn't work,
because the right streams were not selected.
2. EDL, CUE, as well as possibly certain Matroska files don't have any
data or tracks in the "main" demuxer, so normally the first segment is
picked for the track list. This was simply broken.

Fix by sprinkling the code with various hacks.
2014-07-20 20:13:08 +02:00
wm4 d320695207 command: potentially fix dvd angle setting
This called demux_flush(), but that doesn't make any sense with an
asynchronously running demuxer. It would just keep reading and add new
packets again. Explicitly pause the demuxer, so that this can't happen.
Also, when flushing, data will be missing, so the decoders should
always be reinitialized, even if the operation fails.
2014-07-20 20:13:07 +02:00
foo86 6a556f524e input: enable wakeup on LIRC socket
Commit dc00b14 removed playloop polling. Enable wakeup on LIRC socket,
otherwise remote control doesn't work when paused.
2014-07-20 13:52:06 +02:00
wm4 ded02bb78c demux: make the cache refresh cached STREAM_CTRLs
This fixes the same symptom as the previous commit, but when the demuxer
thread is enabled. In this case, if nothing was read from the demuxer,
the STREAM_CTRLs weren't updated either. To the player, this looked like
the stream cache was never making progress, so playback was kept paused.
2014-07-20 00:19:58 +02:00
wm4 1313d38efb player: don't wait forever with --cache-pause-below behavior
Commit dc00b146, which disables polling by default, missed another
instance of polling: when the player pauses automatically on low cache.

This could lead to apparent freezes when playing network streams.
2014-07-20 00:16:43 +02:00
Tsukasa OMOTO b0ff0527a3 build: enable compiler optimization by default 2014-07-20 00:08:36 +02:00
wm4 887140b7d4 demux: fix a corner case (2)
It can happen that read_packet() doesn't read a packet, even if it
succeeds. Typically this is because a packet was read, but then thrown
away, because it's not part of a selected stream. The result would be a
bogus EOF condition.

Fix by explicitly checking for EOF.
2014-07-19 12:34:07 +02:00
wm4 cfdb1312da demux: ensure demux_read_packet_async() always reads
In corner cases, it might be possible that a demux_read_packet_async()
call fails to make the demuxer thread to read more packets.

If a packet is queued, the function will simply return a packet, without
marking the stream as active. As a consequence, read_packet() might
decide not to read any further packets, and the demuxer will never read
a packet and wake up the playback thread.

This was originally done to align it with demux_read_packet() semantics;
just drop this.
2014-07-19 12:27:25 +02:00
wm4 24efaa3ad7 demux: fix a corner case
demux_read_any_packet() attempts to call read_packet(), but if no stream
is active, it can decide not to read anything. The function will return
NULL, which implies EOF. Fix this by explicitly
setting demux_stream->active if needed.

Also use dequeue_packet() instead of demux_read_packet(), because it's
cleaner. (Shouldn't change behavior.)

Possibly fixes #938.
2014-07-19 12:27:21 +02:00
Stefano Pigozzi c76958d20c cocoa: don't send messages to uninitialized gl contexts
This should fix some crashes where we sent makeCurrentContext to a context that
was being freed from another thread.

/cc @mpv-player/stable
2014-07-19 12:01:53 +02:00
wm4 1942e424e3 demux: fix opening pipes with demux_lavf
We told the demuxer that a pipe (if stream cache is enabled) is
seekable. This is because the stream cache is technically seekable, it's
just that seeking may fail at runtime if a non-cached byte range is
requested.

This caused libavformat to issue seeks on initialization (at least when
piping mp4 youtube videos). Initialization failed completely after
spamming tons of error messages.

So, if an unseekable stream is cached, tell the demuxer that the file is
not seekable. This gets reversed later (when printing a message about
caching an unseekable stream), so the user can still try his luck by
issuing a seek command. The important part is that libavformat
initialization will not take code paths that will unnecessarily seek for
whatever reasons.

CC: @mpv-player/stable: regression from 0.3.x
2014-07-18 16:16:05 +02:00
wm4 2646874369 video: use symbolic constants instead of magic integers
In my opinion this is not really necessary, since there's only a single
user of update_video(), but others reading this code would probably hate
me for using magic integer values instead of symbolic constants.

This should be a purely cosmetic commit; any changes in behavior are
bugs.
2014-07-18 15:11:21 +02:00
wm4 7083f88ca8 video: don't block when reading video packets
Instead of blocking on the demuxer when reading a packet, let packets be
read asynchronously. Basically, it polls whether a packet is available,
and if not, the playloop goes to sleep until the demuxer thread wakes it
up.

Note that the player will still block for I/O, because audio is still
read synchronously. It's much harder to do the same change for audio
(because of the design of the audio decoding path and especially
initialization), so audio will have to be done later.
2014-07-18 15:10:28 +02:00
wm4 848546f2de demux: fix problems with EOF
It was easy to get into a wakeup feedback loop on EOF. The reason that
EOF is complicated is that we try to retry reading when EOF is reached,
in case the EOF state actually disappears (e.g. when watching a
currently downloaded file).

This feature is probably worthless, since in practice you have to do a
seek to "unstuck" it anyway, but since the old code also did this, we
want to keep this behavior for now.

Avoid the feedback loop by introducing another EOF flag (last_eof), that
contains the actual previous EOF state, and is not overwritten when
retrying reading. Wakeup is skipped if the EOF state didn't change.

Also, actually call the wakeup callback when EOF is detected.

The line that adds "ds->active = false;" actually does nothing, but in
theory it's cleaner.
2014-07-18 15:08:38 +02:00
wm4 152a099c3a demux: add function to read packets asychronously 2014-07-18 15:08:31 +02:00
wm4 dc00b146c4 player: remove the last instances of polling
Mouse cursor handling, --heartbeat-cmd, and OSD messages basically
relied on polling. For this reason, the playloop always used a small
timeout (not more than 500ms).

Fix these cases, and raise the timeout to 100 seconds. There is no
reason behind this number; for this specific purpose it's as close to
infinity as any other number.

On MS Windows, or if vo_sdl is used, the timeout remains very small.
In these cases the GUI code doesn't do proper event handling in the
first place, and fixing it requires much more effort.

getch2_poll() still does polling, because as far as I'm aware no event-
based way to detect this state change exists.
2014-07-18 15:04:46 +02:00
wm4 34fdf082d8 dvd, bd: fix A/V sync
Slightly less robust, but simpler, and usually guarantees that audio
and video are properly in sync.
2014-07-18 01:26:46 +02:00
wm4 1d7a68d75c demux: fix debug log output
It printed the PTS instead of the DTS.
2014-07-17 22:03:12 +02:00
wm4 9faa131959 demux: drop some unused definitions 2014-07-17 21:53:44 +02:00
wm4 11944e0b83 manpage: fix documented default for --demuxer-thread 2014-07-17 01:50:29 +02:00
wm4 30542456c3 demux_lavf: reverse rotation direction with new API
The old FFmpeg API and the new Libav API disagree about mp4 display
rotation direction. Well, whatever, fix it trial-and-error-style.

CC: @mpv-player/stable: add
2014-07-17 00:30:03 +02:00
wm4 1301a90761 demux: add a demuxer thread
This adds a thread to the demuxer which reads packets asynchronously.
It will do so until a configurable minimum packet queue size is
reached. (See options.rst additions.)

For now, the thread is disabled by default. There are some corner cases
that have to be fixed, such as fixing cache behavior with webradios.

Note that most interaction with the demuxer is still blocking, so if
e.g. network dies, the player will still freeze. But this change will
make it possible to remove most causes for freezing.

Most of the new code in demux.c actually consists of weird caches to
compensate for thread-safety issues (with the previously single-threaded
design), or to avoid blocking by having to wait on the demuxer thread.

Most of the changes in the player are due to the fact that we must not
access the source stream directly. the demuxer thread already accesses
it, and the stream stuff is not thread-safe.

For timeline stuff (like ordered chapters), we enable the thread for the
current segment only. We also clear its packet queue on seek, so that
the remaining (unconsumed) readahead buffer doesn't waste memory.

Keep in mind that insane subtitles (such as ASS typesetting muxed into
mkv files) will practically disable the readahead, because the total
queue size is considered when checking whether the minimum queue size
was reached.
2014-07-16 23:25:56 +02:00
wm4 69a8f08f3e tags: add copy function 2014-07-16 22:40:12 +02:00
Rudolf Polzer 073b2becfe ao_lavc: Fix design of audio pts handling.
There was confusion about what should go into audio pts calculation and
what not (mainly due to the audio push thread). This has been fixed by
using the playing - not written - audio pts (which properly takes into
account the ao's buffer), and incrementing the samples count only by the
amount of samples actually taken from the buffer (unfortunately this
now forces us to keep the lock too long for my taste).
2014-07-16 16:18:34 +02:00
Rudolf Polzer e257cbfdbb ao_lavc: Add a missing newline for the log. 2014-07-16 16:18:34 +02:00
Rudolf Polzer 2a985716cd ao_lavc: Fix advancing of audio pts. 2014-07-16 16:18:34 +02:00
Rudolf Polzer 35c20c3094 idet.sh: Fix a typo. 2014-07-16 16:15:12 +02:00
Tsukasa OMOTO 1c850ff559 osxbundle: fix detection of user libraries
Previous code would detect for example `libcaca.0.dylib` as a system library,
because it matched the `libc` condition.
2014-07-16 08:36:34 +02:00
Stefano Pigozzi 135ba132a8 cocoa: fix regression preventing window resize
I introduced this bug in b5bbb49. Sorry!
This could fix #943 and #930 even though I can't reproduce those specific bugs.
2014-07-15 21:33:19 +02:00
Rudolf Polzer f5449870e0 idet.sh: Fix telecine detection. 2014-07-15 11:10:24 +02:00
wm4 23a7257cca Revert "Remove DVD and Bluray support"
This reverts commit 4b93210e0c.

*shrug*
2014-07-15 01:49:02 +02:00
wm4 4b93210e0c Remove DVD and Bluray support
It never worked well. Just remux your DVD and BD images to mkv.
2014-07-14 14:34:14 +02:00
wm4 c129e3f666 demux_lavf: don't let metadata update mess up ogm playback
For OGG audio files, we usually merge the per-stream metadata back to
the file-global metadata. Don't do that for OGM, because with OGM most
metadata is actually per-stream.
2014-07-14 14:34:14 +02:00
Stefano Pigozzi ac71cb8611 cocoa: fix compilation on OS X 10.8 2014-07-14 07:21:44 +02:00
wm4 9fadc92eb1 command: don't show VO information in colorspace properties
Until now, changing the properties showed the VO colorspace parameters
on OSD. This didn't work quite well, because it showed the VO parameters
_before_ the change. This is because at least one video frame with the
new parameters has to be shown, and this doesn't happen right after
changing the property, but a bit later.

Also fix a random typo in unrelated code.
2014-07-13 20:12:55 +02:00
wm4 417ffa8b40 Remove some mp_msg calls with no trailing \n
The final goal is all mp_msg calls produce complete lines. We want this
because otherwise, race conditions could corrupt the terminal output,
and it's inconvenient for the client API too. This commit works towards
this goal. There's still code that has this not fixed yet, though.
2014-07-13 20:12:13 +02:00
wm4 d54d21cbd5 config: adjust config parser messages
Some cleanup. Also, try not to call mp_msg multiple times for 1 line.
2014-07-13 20:11:46 +02:00
wm4 9b654fd540 player: remove some inactive code
demux_seek() actually doesn't return seek success. Instead, it fails if
the demuxer is flagged as unseekable (but this is checked explicitly at
the beginning of this function), or if the seek target PTS is
MP_NOPTS_VALUE (which can never happen).
2014-07-13 20:07:29 +02:00
wm4 ae7228c6a3 audio: drop buffered audio when switching tracks or filters
No reason to wait until the audio has been played. This isn't a problem
with gapless audio disabled, and since gapless is now default, this
behavior might be perceived as regression.

CC: @mpv-player/stable
2014-07-13 20:07:14 +02:00
wm4 fb54a1436a audio: don't wait for draining if paused
Logic for this was missing from pull.c. For push.c it was missing if the
driver didn't support it. But even if the driver supported it (such as
with ao_alsa), strange behavior was observed by users. See issue #933.

Always check explicitly whether the AO is in paused mode, and if so,
don't drain.

Possibly fixes #933.

CC: @mpv-player/stable
2014-07-13 20:06:33 +02:00
wm4 b505cab597 dvdnav: fix time display when starting in the middle of the DVD
libdvdnav can actually jump into the middle of the DVD (e.g. scene
selection menus do that). Then time display is incorrect: we start from
0, even though playback time is somewhere else. This really matters when
seeking. If the display time mismatches, a small relative seek will
apparently jump to the beginning of the movie.

Fix this by initializing the PTS stuff on opening. We have to do this
after some small amount of data has been read from the stream (because
libdvdnav is crap and doesn't always update the time between seeks and
the first read; also see STREAM_CTRL_GET_CURRENT_TIME remarks in
cache.c; although this was not observed when testing with scene
selection menus). On the other hand, we want to do it before opening the
demuxer, because that will read large amounts of data and likely will
change the stream position.

Also see commit 49813670.
2014-07-13 20:05:25 +02:00
Alessandro Ghedini 1f98bb3adc stream_dvdnav: suspend read on vts change even if the requested title is not found 2014-07-13 18:26:08 +02:00
Alessandro Ghedini 712cf3342e DOCS/release-policy: mention the release version in the RELEASE_NOTES template
Also suggest adding changes in point releases to RELEASE_NOTES as well.
2014-07-13 15:03:47 +02:00
wm4 a09329bcf7 input: skip BOM in input.conf 2014-07-12 21:25:32 +02:00
wm4 fed69f3637 config: skip BOM 2014-07-12 21:24:55 +02:00
wm4 5b820ff1b4 dvd: potentially fix video aspect ratio
This overwrote the source stream header, instead of the stream header
exported to the decoder.
2014-07-12 20:17:19 +02:00
wm4 c37956b364 stream: don't sleep for reconnecting network if playback is stopped
Also silences the bogus message if that happens.

CC: @mpv-player/stable
2014-07-12 19:19:04 +02:00
wm4 64e3b07a9d cache_file: fix operation if stream size is unknown
Happens when playing from a pipe.

Note that seeking forward doesn't work. It would be possible to create a
workaround for that by reading and skipping data until the target
position is reached (and writing the skipped data into the cache file),
but I'm not sure about that.

Fixes #928.

CC: @mpv-player/stable
2014-07-12 19:18:21 +02:00
ChrisK2 1e8b98af73 osc: improve previous commit 2014-07-10 11:22:05 +02:00
ChrisK2 549afdb9c5 osc: round displayed cache value
Fixes #919
2014-07-10 11:17:51 +02:00
wm4 d68d4dd984 Revert "build: avoid defining _GNU_SOURCE"
This reverts commit 2e6a8f260c.

Too many problems for now, such as with OSX and asprintf().
2014-07-10 09:25:37 +02:00
wm4 f8c2dd1b78 build: include <strings.h> for strcasecmp()
It happens to work without strings.h on glibc or with _GNU_SOURCE, but
the POSIX standard requires including <strings.h>.

Hopefully fixes OSX build.
2014-07-10 08:29:32 +02:00
wm4 e1f2540ede video/out: use strcmp() instead of strcasecmp() for equalizers
This makes it more consistent with the more important VOs.
2014-07-10 08:27:15 +02:00
wm4 1a1e631ccd build: deal with endian mess
There is no standard mechanism for detecting endianess. Doing it at
compile time in a portable way is probably hard. Doing it properly
with a configure check is probably hard too. Using the endian
definitions in <sys/types.h> (usually includes <endian.h>, which is
not available everywhere) works under circumstances, but the previous
commit broke it on OSX.

Ideally all code should be endian dependent, but that is not possible
due to the dependencies (such as FFmpeg, some video output APIs, some
audio output APIs).

Create a header osdep/endian.h, which contains various fallbacks.
Note that the last fallback uses libavutil; however, it's not clear
whether AV_HAVE_BIGENDIAN is a public symbol, or whether including
<libavutil/bswap.h> really makes it visible. And in fact we don't want
to pollute the namespace with libavutil definitions either. Thus it's
only the last fallback.
2014-07-10 00:58:56 +02:00
wm4 2e6a8f260c build: avoid defining _GNU_SOURCE
_GNU_SOURCE defines the kitchen sink, and also prefers glibc definitions
where glibc and POSIX conflict. Even though POSIX is worth less than
toilet paper, we still prefer the POSIX definitions.

rar.c needs asprintf(), which is _GNU_SOURCE-only. So we define
_GNU_SOURCE too specifically for this file.
2014-07-09 22:10:33 +02:00
wm4 5d3f1a17a7 DOCS/tech-overview: minor updates 2014-07-09 02:14:23 +02:00
wm4 93f63214e0 demux: remove accurate_seek field
It's unused now. (Only the dvd code used it until recently.)
2014-07-08 22:20:39 +02:00
Stefano Pigozzi 609187082f cocoa: simplify logic that returns used bits per color 2014-07-08 22:06:57 +02:00
Stefano Pigozzi b5bbb49a1a cocoa: move CGL context creation to gl_cocoa
This approach is similar to what other vo_opengl backends do. It can also be
used in the future to create another cocoa backend that renders offscreen
with IOSurfaces or FBOs.
2014-07-08 21:06:57 +02:00
wm4 469ec23f85 demux_disc: flush slave demuxer packet queue on resync
Technically needed, but not strictly. It seems it works without in
practice, because demux_lavf.c reads exactly one packet for fill_buffer
call, so there are never packets queued.
2014-07-07 19:24:22 +02:00
wm4 4981367021 cache, dvd, bluray: simplify stream time handling
We used a complicated and approximate method to cache the stream
timestamp, which is basically per-byte. (To reduce overhead, it was only
cached per 8KB-block, so it was approximate.)

Simplify this, and read/keep the timestamp only on discontinuities. This
is when demux_disc.c actually needs the timestamp.

Note that caching is currently disabled for dvdnav, but we still read
the timestamp only after some data is read. libdvdread behaves well, but
I don't know about libbluray, and the previous code also read the
timestamp only after reading data, so try to keep it safe.

Also drop the start_time offset. It wouldn't be correct anymore if used
with the cache, and the idea behind it wasn't very sane either (making
the player to offset the initial playback time to 0).
2014-07-07 19:09:37 +02:00
wm4 4d829d750c input: restore ability to combine mouse buttons
Key bindings are decided on the "down" event, so if the prefix is not
unique, the first/shortest will be used (e.g. when both "a" and "a-b"
are mapped, "a" will always be chosen).

This also breaks combining multiple mouse buttons. But it seems users
expect it to work, and it's indeed a bit strange that it shouldn't work,
as mouse bindings are emitted on the key "up" event, not "down" (if the
shorter binding didn't emit a command yet, why shouldn't it be
combinable).

Deal with this by clearing the key history when a command is actually
emitted, instead of when a command is decided. This means if both
MOUSE_BTN0 and MOUSE_BTN0-MOUSE_BTN1 are mapped, the sequence of holding
down BTN0 and then BTN1 will redecide the current command. On the other
hand, if BTN0 is released before BTN1 is pressed, the command is
emitted, and the key history is deleted. So the BTN1 press will not
trigger BTN0-BTN1.

For normal keys, nothing should change, because commands are emitted on
the "down" event already, so the key history is always cleared.

Might fix #902.

CC: @mpv-player/stable (if this fix is successful)
2014-07-07 18:18:41 +02:00
wm4 c07bae02e2 ao_null: disable latency emulation
Doesn't work quite right, and will pause for the latency duration after
seeking. Some users use --ao=null to disable audio (even though they
should probably use --no-audio), and this use-case is broken by this
issue too.

CC: @mpv-player/stable
2014-07-07 18:00:48 +02:00
wm4 19dde186a0 demux: print initial metadata
This was accidentally broken in 7e209185, and metadata was printed only
when it changed.
2014-07-07 18:00:41 +02:00
wm4 e2b0416ae1 osc: fix failure when using DVD menus
[osc] Lua error: mp.assdraw:31: attempt to concatenate local 's' (a nil value)
2014-07-07 18:00:41 +02:00
wm4 02ca42c7f1 lua: redo error handling, print backtraces
The original goal was just adding backtraces, however making the code
safe (especially wrt. to out of memory Lua errors) was hard. So this
commit also restructures error handling to make it conceptually simpler.
Now all Lua code is run inside a Lua error handling, except the calls
for creating and destroying the Lua context, and calling the wrapper C
function in a safe way.

The new error handling is actually conceptually simpler and more
correct, because you can just call any Lua function at initialization,
without having to worry whwther it throws errors or not.

Unfortunately, Lua 5.2 removes lua_cpcall(), so we have to emulate it.
There isn't any way to emulate it in a way that works the same on 5.1
and 5.2 with the same semantics in error cases, so ifdeffery is needed.
The debug.traceback() function also behaves weirdly differently between
the Lua versions, so its output is not as nice as it could be (empty
extra line).
2014-07-07 18:00:41 +02:00
wm4 0ea2bc2778 old-configure: fix Lua tests
This added every Lua that was found on the system.
2014-07-07 18:00:41 +02:00
wm4 acd60736ef Remove stream_pts stuff
This was used by DVD/BD, but its usage was removed with one of the
previous commits.
2014-07-06 19:05:59 +02:00
wm4 a90b5cfddf DOCS/client-api-changes: try to fix rst formatting again 2014-07-06 19:05:45 +02:00
wm4 f512604f02 dvd, bd: enable precise seeking
This should work now, at least kind of. Note that actual success depends
on the behavior of the underlying lib{dvd{nav,read},bluray}
implementation, which could go very wrong.

In the worst case, it could happen that the underlying implementation
seeks a long time before the seek target time. In this case, the player
will just decode video until the target time is reached, even if that
requires e.g. decoding 30 mintues of video before refreshing.

In the not-so-bad but still bad case, it would just miss the seek
target, and seek past it.

In my tests, it works mostly ok, though. Seeking backwards usually
fails, unless something like --hr-seek-demuxer-offset=1 is used (this
makes it seek to 1 second before the target, which may or may not be
enough to compensate for the DVD/BD imprecision).
2014-07-06 19:03:43 +02:00
wm4 7bf090ad24 dvd, bluray: handle playback display time handling differently
This is a pretty big change. Instead of doing a half-hearted passthrough
of the playback timestamp, we attempt to rewrite the raw MPEG timestamps
such that they match with the playback time.

We add the offset between raw start timestamp and playback time to the
packet timestamps. This is the easy part; but the problem is with
timestamp resets. We simply detect timestamp discontinuities by checking
whether they are more than 500ms apart (large enough for all video
faster than 2 FPS and audio with reasonable framesizes/samplerates), and
adjust the timestamp offset accordingly.

This should work pretty well. There may be some problems with subtitles.
If the first packet after a timestamp reset is a subtitle instead of
video, it will fail. Also, selecting multiple audio or video streams
won't work (but mpv doesn't allow selecting several anyway). Trying to
demux subtitles with no video stream enabled will probably fail.

Untested with Bluray, because I have no Bluray sample.

Background:

libdvdnav/libdvdread/libbluray make this relatively hard. They return a
raw MPEG (PS/TS) byte stream, and additionally to that provide a
function to retrieve the current "playback" time. The playback time is
what should be displayed to the user, while the MPEG timestamps can be
completely different. Even worse, the MPEG timestamps can reset. Since
we use the libavformat demuxer (instead of parsing the MPEG packets in
the DVD/BD code), it's hard to associate between these timestamps. As a
result, the time display is special cased in the playloop, and of low
quality (updates only all 1 or 2 seconds, sometimes is incorrect). The
fact that the stream cache can be between demuxer and the stream source
makes things worse.

All the libs seem to provide an event that tells whether timestamps are
resetting. But since this signalling is byte based, it's hard to connect
it to the demuxed MPEG packets. It might be possible to create some sort
of table mapping file positions to discontinuities and new timestamps.
(For simplicity, this table could be 2 entries large, sufficient to
catch all discontinuities if the distance between them is larger than
the total buffering.)
2014-07-06 19:03:12 +02:00
wm4 361be8385b stream_dvdnav: more debugging output 2014-07-06 19:03:05 +02:00
wm4 f3604fc3fb demux: fix a corner case related to demux_disc
It can happen that demux_fill_buffer() adds more than 1 packet, and then
the packets would add up. Affects demux_disc.c only (nothing else uses
this function).
2014-07-06 19:02:58 +02:00
wm4 e3a3b764c8 dvd: fix first subtitle with delayed subtitle streams
This was accidentally broken with moving the DVD code to demux_disc.c.

Also remove an abort() call meant for debugging.
2014-07-06 19:02:49 +02:00
wm4 1d55547adf stream: remove now unused STREAM_CTRL_GET_START_TIME
demux_disc.c takes care of this now.
2014-07-06 19:02:33 +02:00
wm4 de28876222 demux: minor simplification
Oops, should have been part of commit 37085788.
2014-07-06 19:02:21 +02:00
atomnuker e6643abc98 ao_pulse: set icon name
Will replace the generic XDG video icon inherited from media role.
2014-07-05 17:07:16 +02:00
wm4 6fcb34d887 vf_vapoursynth: reset error state on seeking
When seeking, we violently destroy the filter, because vapoursynth has
no proper API for terminating a video with unknown frame count. This
looks like an error to vapoursynth, and the error is returned via the
frame callbacks. The bug is that we remember this error state across
reinitialization, so on the first filter call after reinitialization, we
thought filtering the current frame failed. This caused a shift by 1
frame on each seek.

CC: @mpv-player/stable
2014-07-05 17:07:16 +02:00
wm4 d27a2bc546 build: allow compilation without any atomics
Not all compilers on all platforms have atomics available (even if they
could, technically speaking).

We don't use atomics that much, only the following things rely on it:
1. the audio pull code, and all audio outputs using it
2. updating global msg levels
3. reading log messages through the client API

Just disable 1. and 3. if atomics are not available. For 2., using fake-
atomics isn't too bad; at worst, message levels won't properly update
under certain situations (but most likely, it will work just fine).

This means if atomics are not available, the client API function
mpv_request_log_messages() will do nothing.

CC: @mpv-player/stable
2014-07-05 17:07:16 +02:00
wm4 49df035514 options: remove bogus replacement message for --fstype
CC: @mpv-player/stable
2014-07-05 17:07:16 +02:00
wm4 962b928192 options: add --autosub-match to option replacement list
CC: @mpv-player/stable
2014-07-05 17:07:16 +02:00
wm4 4f00c74491 player: don't use stream position as fallback for percent-pos
This should be unneeded, and the packet position is already sufficient
for this case.

Accessing the stream position directly is going to be a problem when the
stream is accessed from another thread later.
2014-07-05 17:07:15 +02:00
wm4 54a4a25fe9 tv: move demuxer parts to separate file
Now all demuxer implementations (at least demuxer API-wise) are in the
demux directory.
2014-07-05 17:07:15 +02:00
wm4 37085788e4 demux: minor simplification to internal API
Also some other unrelated minor changes.
2014-07-05 17:07:15 +02:00
wm4 de71b50249 dvd: move angle switching code
No need to provide a "nice" API for it; just do this stuff directly in
the command code.
2014-07-05 17:07:15 +02:00
wm4 0cc3594623 dvd: flush buffers properly on seek
Suggested by tholin on github issue #882.

This is not entirely clean, but the fields we're accessing might be
considered internal to libavformat. On the other hand, existence of the
fields is guaranteed by the ABI, and nothing in the libavformat doxygen
suggestes they're not allowed to be accessed.

CC: @mpv-player/stable
2014-07-05 17:07:15 +02:00
wm4 338004bcfc dvd, bluray, cdda: add demux_disc containing all related hacks
DVD and Bluray (and to some extent cdda) require awful hacks all over
the codebase to make them work. The main reason is that they act like
container, but are entirely implemented on the stream layer. The raw
mpeg data resulting from these streams must be "extended" with the
container-like metadata transported via STREAM_CTRLs. The result were
hacks all over demux.c and some higher-level parts.

Add a "disc" pseudo-demuxer, and move all these hacks and special-cases
to it.
2014-07-05 17:07:15 +02:00
wm4 4d2a4afdef discnav: fix a comment 2014-07-05 17:07:15 +02:00
wm4 942619c779 demux: set filepos field when dequeuing a packet
Otherwise the position can be too far ahead.
2014-07-05 17:07:15 +02:00
wm4 85eb2bee3a demux: cosmetics: minimize code 2014-07-05 17:07:15 +02:00
wm4 8d40b1e8ab demux: make start time a simple field
Simpler, especially for later changes.
2014-07-05 17:07:15 +02:00
wm4 7e209185f1 demux, stream: change metadata notification
(Again.)

This time, we simply make it event-based, as it should be. This is done
for both demuxer metadata and stream metadata.

For some ogg-over-icy streams, 2 updates are reported on stream start.
This is because libavformat reports an update right on start, while
including the same info in the "static" metadata. I don't know if that's
a bug or a feature.
2014-07-05 17:07:14 +02:00
wm4 58880c00ee demux: make replaygain per-track
It's unlikely that files with multiple audio tracks and with replaygain
actually happen, but this change might help avoid minor corner cases
with later changes.
2014-07-05 17:07:14 +02:00
wm4 a97256c1d5 demux: move packet functions to a separate source file 2014-07-05 17:07:14 +02:00
wm4 e4221f3189 demux: move packet list functions
Move them to the only place where they are used, demux_subreader.c.
2014-07-05 17:07:14 +02:00
wm4 18e6d07612 demux_lavf: for now, ignore the new libavformat image demuxers
Recently, libavformat added demuxers to open image files like normal
demuxers. This is a good thing, but for now they interfere with the
operation of demux_mf. Add them to the blacklist until there is a proper
solution.

(The list doesn't contain _all_ recognized image formats, just those
that might interfere with demux_mf.)

CC: @mpv-player/stable
2014-07-05 17:07:14 +02:00
wm4 5eb9039c6b demux_lavf: support OTF fonts in Matroska
Apparently it's FFmpeg only.
2014-07-05 17:07:14 +02:00
wm4 1713dc58f1 demux_lavf: don't dump transport stream programs
Probably useless.
2014-07-05 17:07:14 +02:00
wm4 78f3ba73b9 demux_lavf: cleanup debug output
Remove unnecessary prefix, remove some messages.
2014-07-05 17:07:14 +02:00
wm4 bc29e17bcf demux_lavf: fix read_seek return value
This returned a stream error value directly to libavformat, which can't
make sense. For example STREAM_ERROR (0) means success in libavformat
error codes. (The meaning of the libavformat read_seek return value is
underdocumented too.)
2014-07-05 17:07:14 +02:00
wm4 44d7499190 demux_mkv: cosmetics 2014-07-05 17:07:14 +02:00
wm4 e21ecaa18d DOCS, client API: add mpv release versions
The intention is to make it obvious which mpv releases certain changes
will apply to.

Also attempt to fix RST formatting of the list. This is not very proper,
but probably good enough.
2014-07-05 17:07:14 +02:00
Stefano Pigozzi 055bc74ed4 Merge pull request #909 from Nyx0uf/patch-1
OS X bundle: Add more imported UTI
2014-07-05 10:15:14 +02:00
Nyx0uf 22c3de3f8a OS X bundle: Add more imported UTI
Not that there are widely used formats, but it will allow to play them directly from the Finder.
2014-07-04 10:05:52 +02:00
tholin bc5e539d3b stream_dvdnav: check the length of all titles with dvdnav://longest
The last title was ignored before.

CC: @mpv-player/stable

Signed-off-by: wm4 <wm4@nowhere>
2014-07-04 02:24:50 +02:00
tholin f8a7517fb7 stream_dvdnav: free pointer to priv->filename on close
CC: @mpv-player/stable

Signed-off-by: wm4 <wm4@nowhere>
2014-07-04 02:24:50 +02:00
tholin 4bd75313d1 stream_dvdnav: make sure seeking bounds are within range
libdvdnav returns an error is the seek position is out of range.

CC: @mpv-player/stable

Signed-off-by: wm4 <wm4@nowhere>
2014-07-04 02:24:50 +02:00
wm4 fe4fbb5775 DOCS/client_api_examples: add a Qt example
This is pretty dumb and extremely basic. The main purpose is
demonstrating how to integrate mpv into the Qt GUI thread.
2014-07-04 02:24:49 +02:00
wm4 4fedf86b8a client API: allow calling mpv_terminate_destroy(NULL)
This is an oversight and a bug.

CC: @mpv-player/stable
2014-07-04 02:24:49 +02:00
wm4 4a5cfb4323 x11: avoid obscure behavior when --wid is partially garbage
Cast away the "extra" bits (since apparently Window/XID is always
32 bit unsigned). This is not striclty needed, because you're not
supposed to pass garbage to --wid, just because the upper bits are
possibly not interpreted. But if you do so, this change increases
consistency in behavior and removes a strange behavior that was
thought to be a bug.

Also see github issue #906.
2014-07-04 02:23:05 +02:00
Stefano Pigozzi 97f6d7f4ec ao_coreaudio: report hardware latency to ao_read_data
Commit a6a4cd2c88 added reporting of playout latency, this commit also adds
support for reporting hardware and constant audio unit latency.
2014-07-03 20:05:15 +02:00
Alessandro Ghedini 29b047da6f TOOLS/zsh.pl: complete URL schemes based on --list-protocols 2014-07-03 11:03:56 +02:00
Alessandro Ghedini 17d65f9aff TOOLS/zsh.pl: sort options in reverse order by length
This stops options that are prefixes of other options from blocking
completion of values for the longer ones.
2014-07-03 11:03:56 +02:00
Philip Sequeira e940afec15 TOOLS/zsh.pl: untabify 2014-07-03 11:03:56 +02:00
Philip Sequeira 7450a7f531 TOOLS/zsh.pl: fix _arguments line
Don't use _x_arguments, as we don't support X arguments.

Get rid of -s, because we don't support multiple single-letter options
in one argument.

Add -S, because we ignore options after "--".
2014-07-03 11:03:56 +02:00
Philip Sequeira bbc005825a TOOLS/zsh.pl: don't consume extra arguments
Completion now uses "--opt=value" instead of "--opt value". Once the
user presses space and starts a new argument, the option just
completed is out of the picture, whether or not it was given an
argument. This handles options with no arguments or optional arguments
much better; previously, completing such an option would effectively
disable completion for the next argument.

Custom completed options such as "--ao" and friends will no longer
claim to consume an extra argument.
2014-07-03 11:03:56 +02:00
Philip Sequeira fd09578f0f TOOLS/zsh.pl: escape all colons in option descriptions 2014-07-03 11:03:56 +02:00
wm4 4d9ce2993f command: include new "playback-time" property in update mechanism 2014-07-03 01:26:19 +02:00
wm4 0004aaf788 DOCS: add a file listing API changes for the client API 2014-07-03 01:26:19 +02:00
Stefano Pigozzi 0bda8f48e2 manpage: reflect new coreaudio changes 2014-07-02 23:17:44 +02:00
Stefano Pigozzi a6a4cd2c88 ao_coreaudio: report latency more correctly
Previous code was completly wrong. This still doesn't report the device
latency, but we report the buffer latency (as before the AO refactoring) and
the AudioUnit's latency (this is a new 'feature').

Apparently we can also report the device actual latency and we should also
calculate the actual sample rate of the audio device instead of using the
nominal sample rate, but I'll leave this for a later commit.
2014-07-02 23:17:44 +02:00
wm4 703de5d84e cache_file: use unicode on windows
This enables the MinGW Windows crap wrapper for fopen().
2014-07-02 22:01:56 +02:00
Stefano Pigozzi 4f5f034ba2 ao_coreaudio: move channel mapping away from utils
Channel mapping functions are only used in the AUHAL based coreaudio, so move
them there.
2014-07-02 21:43:08 +02:00
Stefano Pigozzi c8fc02612e ao_coreaudio: use mpv's internal pull API 2014-07-02 21:43:08 +02:00
Stefano Pigozzi d16e4b836a ao_coreaudio: remove useless comments 2014-07-02 21:43:08 +02:00
Stefano Pigozzi 0ffbd05e99 ao_coreaudio: rename init_lpcm -> init_audiounit 2014-07-02 21:43:08 +02:00
Stefano Pigozzi 80ec0ba6d0 ao_coreaudio: fill asbd with an helper function 2014-07-02 21:43:07 +02:00
Stefano Pigozzi fa85bfde69 ao_coreaudio: split control to helper functions 2014-07-02 21:43:07 +02:00
Stefano Pigozzi f317d24a39 ao_coreaudio: move device related functions to the new AO 2014-07-02 21:43:07 +02:00
Stefano Pigozzi a8ef70b0f8 ao_coreaudio: remove useless call to print_asbd 2014-07-02 21:43:07 +02:00
Stefano Pigozzi 041557b639 ao_coreaudio: move spdif code to a new AO
The mplayer1/2/mpv CoreAudio audio output historically contained both usage
of AUHAL APIs (these go through the CoreAudio audio server) and the Device
based APIs (used only for output of compressed formats in exclusive mode).

The latter is a very unwieldy and low level API and pretty much forces us to
write a lot of code for little workr. Also with the widespread of HDMI, the
actual need for outputting compressed audio directly to the device is getting
lower (it was very useful with S/PDIF for bandwidth constraints not allowing
a number if channels transmitted in LPCM).

Considering how invasive it is (uses hog/exclusive mode), the new AO
(`ao_coreaudio_device`) is not going to be autoprobed but the user will have
to select it.
2014-07-02 21:43:07 +02:00
wm4 7084e800be cache: clear DVD timestamps
When resizing the cache, the buffer for the DVD timestamps is
initialized with 0. This causes the player to always return playback
position 0 with any file format (not just DVD), and also makes all
relative seeks relative to position 0. Fix this by clearing the
timestamps explicitly.

Closes #899.

CC: @mpv-player/stable
2014-07-02 19:01:47 +02:00
wm4 6d38ad1410 x11: clear window only on initial map
Apparently clearing on every map can cause problems with vdpau when
switching virtual desktops and such. This was observed with at least
XMonad and nvidia-340.17. It's not observed on some other setups without
XMonad.

It's not clear why this happens. Normally, the window background is not
saved, so clearing should have no additional affect. It's a complete
mystery. Possible, the use of legacy X drawing commands (used to clear
the window) interferes with vdpau operation in non-trivial ways.

Work this around by clearing on initial map only. This probably only
hides the underlying issue, but good enough.

Closes #897.

CC: @mpv-player/stable
2014-07-02 02:07:06 +02:00
wm4 4b5fbb3ee0 command: remove some code duplication in cache properties
This also means that the printed size is always rounded to KBs, because
the cache properties are returned in KB. I think this doesn't matter
much. But if it does, the cache properties should probably changed to
return bytes in the first place.
2014-07-02 01:46:04 +02:00
wm4 94450bf410 command: cache can actually have full-size 0
Then it's simply empty.
2014-07-02 01:32:05 +02:00
Andrey Morozov b1969c0eba command: change cache perentage to float, add cache-free and cache-used 2014-07-02 01:28:11 +02:00
wm4 9fee8fd3b3 demux: drop AVI special code
I'm pretty sure libavformat does this automatically, and we don't have
other demuxers where this could happen.

Still, slightly "risky" - so let's see.
2014-07-02 00:32:46 +02:00
wm4 bb131f4c66 demux_mkv: minor improvement to overflow check
CC: @mpv-player/stable
2014-07-02 00:14:18 +02:00
wm4 9a210ca2d5 Audit and replace all ctype.h uses
Something like "char *s = ...; isdigit(s[0]);" triggers undefined
behavior, because char can be signed, and thus s[0] can be a negative
value. The is*() functions require unsigned char _or_ EOF. EOF is a
special value outside of unsigned char range, thus the argument to the
is*() functions can't be a char.

This undefined behavior can actually trigger crashes if the
implementation of these functions e.g. uses lookup tables, which are
then indexed with out-of-range values.

Replace all <ctype.h> uses with our own custom mp_is*() functions added
with misc/ctype.h. As a bonus, these functions are locale-independent.
(Although currently, we _require_ C locale for other reasons.)
2014-07-01 23:11:08 +02:00
wm4 0208ad4f3b demux: minor cleanups 2014-07-01 21:53:23 +02:00
Alessandro Ghedini 2224ac5190 README: fix link to release policy document 2014-06-30 23:23:50 +02:00
Alessandro Ghedini ab241c05c8 options: add --list-protocols option 2014-06-30 23:20:10 +02:00
Alessandro Ghedini 211ca98e9c build: set default cdrom and dvd devices on linux 2014-06-30 23:19:53 +02:00
wm4 1c0939cb80 options: fix --gapless-audio default value
It was intended to be set to "weak" (and that was even documented), but
the actual setting was "no".

Closes #890.

CC: @mpv-player/stable
2014-06-30 18:24:35 +02:00
wm4 dd9420471f demux_mkv: cosmetics 2014-06-29 23:28:47 +02:00
wm4 37251cef69 demux_mkv: add some overflow checks etc.
Some of these might be security relevant.

The RealAudio code was especially bad. I'm not sure if all RealAudio
stuff still plays correctly; I didn't have that many samples for
testing. Some checks might be unnecessary or overcomplicated compared
to the (obfuscated) nature of the code.

CC: @mpv-player/stable
2014-06-29 23:27:28 +02:00
Tsukasa OMOTO 1646584058 player: fix start position when specifying with percent 2014-06-29 20:39:49 +02:00
Tsukasa OMOTO 1aef780b6c options: support setting start time relative to start PTS
Signed-off-by: wm4 <wm4@nowhere>
2014-06-29 20:39:49 +02:00
Tsukasa OMOTO a73415584c player: make the time display relative to start PTS
This commit makes the playback start time always at time 0.

Signed-off-by: wm4 <wm4@nowhere>
2014-06-29 20:39:49 +02:00
wm4 7412257305 av_common: remove unneeded field
This is actually needed for encoding only, and not decoding. Drop it.
2014-06-29 20:39:32 +02:00
Mohammad Alsaleh 8b06fc86f3 af_volume: fix calculations including replay-gain
rgain is not an additive value. It's a multiplier/gain.

Previous behaviour produced negative level values in some cases
(when rgain < 1.0) which caused volume to be louder when its value
was lowered.

CC: @mpv-player/stable

Signed-off-by: Mohammad Alsaleh <CE.Mohammad.AlSaleh@gmail.com>
Signed-off-by: wm4 <wm4@nowhere>
2014-06-28 15:56:16 +02:00
wm4 88225d0afb sub: fix undefined behavior with dvd://
The string could get reallocated.

CC: @mpv-player/stable
2014-06-28 15:55:24 +02:00
wm4 6eb955e274 manpage: update config file locations
Also add some explanations how the config paths are determined.
2014-06-28 15:55:09 +02:00
wm4 07bc199e35 scripting: shorten a line
Also allows it to deal with NULL return values, which currently is not
needed, but may or may not be required at some point in the future (what
if malloc fails).
2014-06-26 19:56:45 +02:00
wm4 3e631b9cb8 config: use the same signature for win32/OSX specific path functions
Seems like a good idea, even if it's basically unused (yet).

Also document requirements on the functions (they're not obvious).

OSX changes untested.
2014-06-26 19:56:45 +02:00
wm4 7f55a39646 config: handle --no-config case directly in mp_config_dirs()
Requires less special-casing, and probably also avoids that starting mpv
with --no-config creates a config dir (even if nothing is loaded).
2014-06-26 19:56:45 +02:00
wm4 b4d64ac434 config, player: avoid some temporary talloc contexts
IMO a semi-bad concept, that the mpv code unfortunately uses way too
much.
2014-06-26 19:56:45 +02:00
wm4 3e1c0e5853 config: make passing talloc context optional for some functions
Until now, the config functions added various allocations to the user-
provided talloc context. Make it so that they're all under the returned
allocation instead. This allows avoiding having to create an extra
temporary context for some callers, and also avoids adding random memory
leaks by accidentally passing a NULL context.

mp_find_all_config_files() has to be changed not to return a pointer
into the middle array for this to work. Make it add paths in order
(instead of reverse), and then reverse the array entries after that.

Also remove the declarations for the win-specific private functions.
Remove STRNULL(); it's barely needed anymore and the functions are
not called with NULL filenames anymore.
2014-06-26 19:56:45 +02:00
wm4 0dcde951b9 player: create config dir if it doesn't exist
This was dropped in the commit adding XDG support, probably
accidentally.

Also normalize some whitespace.
2014-06-26 19:43:23 +02:00
wm4 236fcd3648 config: prefer the old config dir if it exists, but XDG doesn't
This means normally the XDG config dir will be used. But if the old
config dir (~/.mpv) exists and the XDG config dir does not, then don't
create it.

To simplify the code, also make mp_path_exists() accept NULL paths. In
that case it's considered as not existing. (Funnily, on Linux this
already worked, because the string is passed directly to the kernel,
and the kernel will just return EFAULT on invalid memory.)
2014-06-26 19:41:48 +02:00
wm4 c63378d41c player: remove some minor code duplication in config loader code
It's better to keep the logic in one place.

Also drop that a broken config file aborts loading of the player. I
don't see much reason for this, and it inflates the code slightly.
2014-06-26 19:37:26 +02:00
Kenneth Zhou cb250d490c Basic xdg directory implementation
Search $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS for config files.
This also negates the need to have separate user and global variants of
mp_find_config_file()

Closes #864, #109.

Signed-off-by: wm4 <wm4@nowhere>
2014-06-26 19:37:25 +02:00
218 changed files with 9819 additions and 7453 deletions

No files matched your search

+6 -6
View File
@@ -20,12 +20,12 @@
/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
/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
+15 -5
View File
@@ -1,5 +1,8 @@
language: c
os:
- linux
- osx
env:
- LIBAV=libav-stable
- LIBAV=libav-git
@@ -7,24 +10,31 @@ env:
- LIBAV=ffmpeg-git
compiler:
- clang
branches:
only:
- master
- ci
matrix:
exclude:
- os: osx
env: LIBAV=libav-stable
- os: osx
env: LIBAV=libav-git
- os: osx
env: LIBAV=ffmpeg-git
before_install: ./travis-deps libass-stable $LIBAV
script:
- ./bootstrap.py
- ./waf configure
- ./waf build
notifications-policy: &notifications-policy
on_success: change
on_failure: always
notifications:
email: false
irc:
channels:
- "irc.freenode.org#mpv-player-dev"
<<: *notifications-policy
on_success: change
on_failure: always
+44
View File
@@ -0,0 +1,44 @@
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.5.2 is released ---
1.4 - subtle change in X11 and "--wid" behavior
--- 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
+136
View File
@@ -0,0 +1,136 @@
// License: pick one of: public domain, WTFPL, ISC, Ms-PL, AGPLv3
// This example can be built with: qmake && make
#include <sstream>
#include <QFileDialog>
#include <QStatusBar>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QApplication>
#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)
{
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();
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. This doesn't work on OSX,
// because Cocoa doesn't support this form of embedding.
mpv_container = new QWidget(this);
setCentralWidget(mpv_container);
mpv_container->setAttribute(Qt::WA_NativeWindow);
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");
// 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);
// 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("");
}
}
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);
}
}
MainWindow::~MainWindow()
{
if (mpv)
mpv_terminate_destroy(mpv);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef QTEXAMPLE_H
#define QTEXAMPLE_H
#include <QMainWindow>
#include <mpv/client.h>
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;
void create_player();
void handle_mpv_event(mpv_event *event);
};
#endif // QTEXAMPLE_H
+12
View File
@@ -0,0 +1,12 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = qtexample
TEMPLATE = app
CONFIG += link_pkgconfig
PKGCONFIG = mpv
SOURCES += qtexample.cpp
HEADERS += qtexample.h
+1 -1
View File
@@ -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 .mpv/config
Of course, the options can be stored in a profile, like this .config/mpv/mpv.conf
section::
[myencprofile]
+14 -14
View File
@@ -72,7 +72,7 @@ Available filters are:
``tospdif=<yes|no>``
Output raw AC-3 stream if ``no``, output to S/PDIF for
passthrough if ``yes`` (default).
pass-through if ``yes`` (default).
``bitrate=<rate>``
The bitrate use for the AC-3 stream. Set it to 384 to get 384 kbps.
@@ -101,7 +101,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/60Hz
Remove a sine at the specified frequency. Useful to get rid of the 50/60 Hz
noise on low quality audio equipment. It only works on mono input.
``<freq>``
@@ -182,7 +182,7 @@ 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 1kHz.
while canceling it almost completely around 1 kHz.
``channels=nch[:routes]``
Can be used for adding, removing, routing and copying audio channels. If
@@ -277,8 +277,8 @@ Available filters are:
once for every audio stream.
``<volumedb>``
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
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
gain of 1000 (default: 0).
``replaygain-track``
Adjust volume gain according to the track-gain replaygain value stored
@@ -311,7 +311,7 @@ Available filters are:
.. admonition:: Example
``mpv --af=volume=10.1 media.avi``
Would amplify the sound by 10.1dB and hard-clip if the sound level
Would amplify the sound by 10.1 dB and hard-clip if the sound level
is too high.
``pan=n:[<matrix>]``
@@ -368,8 +368,8 @@ Available filters are:
to the subwoofer.
``<fc>``
cutoff frequency in Hz for the low-pass filter (20Hz to 300Hz)
(default: 60Hz) For the best result try setting the cutoff frequency
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
as low as possible. This will improve the stereo or surround sound
experience.
``<ch>``
@@ -381,7 +381,7 @@ 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 100Hz to
Would add a subwoofer channel with a cutoff frequency of 100 Hz to
output channel 4.
``center``
@@ -409,7 +409,7 @@ Available filters are:
.. admonition:: Example
``mpv --af=surround=15 --audio-channels=4 media.avi``
Would add surround sound decoding with 15ms delay for the sound to
Would add surround sound decoding with 15 ms delay for the sound to
the rear speakers.
``delay[=[ch1,ch2,...]]``
@@ -437,8 +437,8 @@ 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.5ms, the two rear channels
and the subwoofer by 0ms and the center channel by 7ms.
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.
``export=mmapped_file:nsamples]``
Exports the incoming signal to other processes using memory mapping
@@ -588,7 +588,7 @@ Available filters are:
tempo.
``mpv --af=scaletempo=stride=30:overlap=.50:search=10 media.ogg``
Would tweak the quality and performace parameters.
Would tweak the quality and performance parameters.
``mpv --af=format=float,scaletempo media.ogg``
Would make scaletempo use float code. Maybe faster on some
@@ -600,7 +600,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
+24 -2
View File
@@ -23,7 +23,7 @@ normal driver parameters.
where PulseAudio is used. On Windows, ``--ao=wasapi`` is preferred,
though it might cause trouble sometimes, in which case ``--ao=dsound``
should be used. On BSD systems, ``--ao=oss`` or `--ao=sndio`` may work
(the latter being experimental). On OSX systems, use ``--ao=coreaudio``.
(the latter being experimental). On OS X systems, use ``--ao=coreaudio``.
.. admonition:: Examples
@@ -53,6 +53,10 @@ 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::
@@ -110,7 +114,19 @@ Available audio output drivers are:
with fixed setups.
``coreaudio`` (Mac OS X only)
Native Mac OS X audio output driver
Native Mac OS X audio output driver using the AudioUnits and the CoreAudio
sound server.
``device_id=<id>``
ID of output device to use (0 = default device)
``help``
List all available output devices with their IDs.
``coreaudio_device`` (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)
@@ -137,6 +153,12 @@ Available audio output drivers are:
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.
+37 -25
View File
@@ -19,14 +19,14 @@ General Changes for MPlayer to mpv
Linux 2.4 (including VIDIX)
* Large internal cleanups
* Removal of support for dead platforms
* New buildsystem
* No embedded copy of ffmpeg and other libraries
* New build system
* No embedded copy of FFmpeg and other libraries
* Better pause handling (do not unpause on a command)
* Better MKV support (such as ordered chapters)
* vo_vdpau improvements
* Precise seeking support
* Native OpenGL backend for OSX
* General OSX improvements
* Native OpenGL backend for OS X
* General OS X improvements
* Improvements in audio/video sync handling
* Cleaned up terminal output
* Gapless audio support (``--gapless-audio``)
@@ -35,14 +35,14 @@ General Changes for MPlayer to mpv
* 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
* 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
* Generally improved MS Windows support (dealing with unicode filenames,
* Generally improved MS Windows support (dealing with Unicode file names,
improved ``--vo=direct3d``, improved window handling)
* Better OSD rendering (using libass). This has full unicode support, and
* 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
@@ -50,19 +50,19 @@ General Changes for MPlayer to mpv
* 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 improvements (can save screenshots as JPEG or PNG, configurable
file names, 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,
* Removal of most built-in demuxers, using libavformat instead
* Removal of built-in 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``)
* 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``.
@@ -71,10 +71,10 @@ General Changes for MPlayer to mpv
* 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
* OS X: 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.
* OS X: Apple Remote support.
* OS X: 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).
@@ -99,7 +99,7 @@ General Changes for MPlayer to mpv
* 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
* OS X: VDA support using libavcodec hwaccel API instead of FFmpeg's decoder. Up
to 2-2.5x reduction in CPU usage.
* Make hardware decoding in general work with the ``opengl`` video output.
* Lua scripting (see `LUA SCRIPTING`_)
@@ -308,28 +308,40 @@ input.conf and Slave Commands
Slave mode
~~~~~~~~~~
* 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
* 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 **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.
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 already removed (``TOOLS/mpv_identify.sh`` is provided
instead), and ``--slave-broken`` might be removed in the future.
``--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 planned, but nothing
has emerged yet.
* **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.
* A slave protocol is planned, but nothing has emerged yet.
Policy for Removed Features
---------------------------
+2 -2
View File
@@ -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 128kbps MP3 encoding.
selects 128 kbps MP3 encoding.
Options are managed in lists. There are a few commands to manage the
options list.
+38 -15
View File
@@ -4,12 +4,18 @@ INPUT.CONF
The input.conf file consists of a list of key bindings, for example::
s screenshot # take a screenshot with the s key
LEFT seek 15 # map the left-arrow key to seeking forward by 15 seconds
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.
The file is normally located in ``~/.config/mpv/input.conf`` (some platforms
may use different paths). The default bindings are defined here::
https://github.com/mpv-player/mpv/blob/master/etc/input.conf
A list of special keys can be obtained with
``mpv --input-keylist``
@@ -101,14 +107,14 @@ List of Input Commands
``revert_seek`` command itself.
``frame_step``
Play one frame, then pause.
Play one frame, then pause. Does nothing with audio-only playback.
``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
post-processing that modifies timing of frames (e.g. deinterlacing) should
usually work, but might make backstepping silently behave incorrectly in
corner cases. Using ``--hr-seek-framedrop=no`` should help, although it
might make precise seeking slower.
@@ -193,6 +199,8 @@ List of Input Commands
Stop playback of the current file, and play the new file immediately.
<append>
Append the file to the playlist.
<append-play>
Append the file, and if nothing is currently playing, start playback.
The third argument is a list of options and values which should be set
while the file is playing. It is of the form ``opt1=value1,opt2=value2,..``.
@@ -564,7 +572,7 @@ Property list
See ``--osd-level``.
``osd-scale`` (RW)
OSD font size multiplicator, see ``--osd-scale``.
OSD font size multiplier, see ``--osd-scale``.
``loop`` (RW)
See ``--loop``.
@@ -613,11 +621,6 @@ Property list
``stream-end``
Raw end position in bytes in source stream.
``stream-time-pos`` (RW)
Time position in source stream. This only works for DVD and Bluray. This
is probably never different from ``time-pos``, because ``time-pos`` is
forced to this value anyway.
``length``
Length of the current file in seconds. If the length is unknown, the
property is unavailable. Note that the file duration is not always exactly
@@ -658,6 +661,9 @@ Property list
``playtime-remaining``
``time-remaining`` scaled by the the current ``speed``.
``playback-time``
Return the playback time, which is the time difference between start PTS and current PTS.
``chapter`` (RW)
Current chapter number. The number of the first chapter is 0.
@@ -759,7 +765,7 @@ Property list
::
MPV_FORMAT_NODE_MAP
(key and string value for each metdata entry)
(key and string value for each metadata entry)
``chapter-metadata``
Metadata of current chapter. Works similar to ``metadata`` property. It
@@ -791,7 +797,7 @@ Property list
pauses itself due to low network cache.
``cache``
Network cache fill state (0-100).
Network cache fill state (0-100.0).
``cache-size`` (RW)
Total network cache size in KB. This is similar to ``--cache``. This allows
@@ -803,7 +809,17 @@ Property list
this easier, the cache resizing code will allocate the new cache while the
old cache is still allocated.
Don't use this when playing DVD or Bluray.
Don't use this when playing DVD or Blu-ray.
``cache-free`` (R)
Total free cache size in KB.
``cache-used`` (R)
Total used cache size in KB.
``cache-idle`` (R)
Returns ``yes`` if the cache is idle, which means the cache is filled as
much as possible, and is currently not reading more data.
``paused-for-cache``
Returns ``yes`` when playback is paused because of waiting for the cache.
@@ -815,6 +831,12 @@ Property list
or enter idle mode), and in these cases the ``eof-reached`` property will
logically be cleared immediately after it's set.
``seeking``
Returns ``yes`` if the player is currently seeking, or otherwise trying
to restart playback. (It's possible that it returns ``yes`` while a file
is loaded, or when switching ordered chapter segments. This is because
the same underlying code is used for seeking and resyncing.)
``pts-association-mode`` (RW)
See ``--pts-association-mode``.
@@ -978,6 +1000,7 @@ Property list
"par" MPV_FORMAT_DOUBLE
"colormatrix" MPV_FORMAT_STRING
"colorlevels" MPV_FORMAT_STRING
"primaries" MPV_FORMAT_STRING
"chroma-location" MPV_FORMAT_STRING
"rotate" MPV_FORMAT_INT64
@@ -1002,7 +1025,7 @@ Property list
Estimated/measured FPS of the video filter chain output. (If no filters
are used, this corresponds to decoder output.) This uses the average of
the 10 past frame durations to calculate the FPS. It will be inaccurate
if framedropping is involved (such as when framedrop is explicitly
if frame-dropping is involved (such as when framedrop is explicitly
enabled, or after precise seeking). Files with imprecise timestamps (such
as Matroska) might lead to unstable results.
@@ -1067,7 +1090,7 @@ Property list
See ``--sub-forced-only``.
``sub-scale`` (RW)
Subtitle font size multiplicator.
Subtitle font size multiplier.
``ass-use-margins`` (RW)
See ``--ass-use-margins``.
@@ -1247,8 +1270,8 @@ Property list
require reloading the file for changes to take effect. If there is an
equivalent property, prefer setting the property instead.
``write_watch_later_config``
Saves current playback position.
``property-list``
Return the list of top-level properties.
Property Expansion
------------------
+26 -8
View File
@@ -2,7 +2,7 @@ LUA SCRIPTING
=============
mpv can load Lua scripts. Scripts in ``~/.mpv/lua/`` will be loaded on program
start, or if passed to ``--lua``. mpv provides the builtin module ``mp``, which
start, or if passed to ``--lua``. 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.
@@ -74,6 +74,10 @@ The ``mp`` module is preloaded, although it can be loaded manually with
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.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
@@ -94,9 +98,9 @@ The ``mp`` module is preloaded, although it can be loaded manually with
will always result in a string.
``mp.get_property_bool(name [,def])``
Similar to ``mp.get_property``, but return the property value as boolean.
Similar to ``mp.get_property``, but return the property value as Boolean.
Returns a boolean on success, or ``def, error`` on error.
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.
@@ -109,7 +113,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
``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,
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.
@@ -124,7 +128,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
``mp.set_property_bool(name, value)``
Similar to ``mp.set_property``, but set the given property to the given
boolean value.
Boolean value.
``mp.set_property_number(name, value)``
Similar to ``mp.set_property``, but set the given property to the given
@@ -159,7 +163,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
(e.g. ``ctrl+a``).
After calling this function, key presses will cause the function ``fn`` to
be called (unless the user overmapped the key with another binding).
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``
@@ -197,7 +201,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
This will print the message when the key ``y`` is pressed. (``x`` will
still work, unless the user overmaps it.)
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``:
@@ -299,6 +303,20 @@ The ``mp`` module is preloaded, although it can be loaded manually with
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
@@ -359,7 +377,7 @@ are useful only in special situations.
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 unqueue and dispatch mpv events.
``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
+42 -59
View File
@@ -2,7 +2,7 @@ mpv
###
##############
a movie player
a media player
##############
:Copyright: GPLv2+
@@ -19,7 +19,7 @@ SYNOPSIS
DESCRIPTION
===========
**mpv** is a movie player based on MPlayer and mplayer2. It supports a wide variety of video
**mpv** is a media 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
@@ -40,16 +40,13 @@ Keyboard Control
----------------
LEFT and RIGHT
Seek backward/forward 10 seconds. Shift+arrow does a 1 second exact seek
Seek backward/forward 5 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``).
PGUP and PGDWN
Seek forward/backward 10 minutes.
[ and ]
Decrease/increase current playback speed by 10%.
@@ -69,11 +66,11 @@ p / SPACE
Pause (pressing again unpauses).
\.
Step forward. Pressing once will pause movie, every consecutive press will
Step forward. Pressing once will pause, every consecutive press will
play one frame and then go into pause mode again.
,
Step backward. Pressing once will pause movie, every consecutive press will
Step backward. Pressing once will pause, every consecutive press will
play one frame in reverse and then go into pause mode again.
q / ESC
@@ -83,21 +80,12 @@ 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.
@@ -107,9 +95,6 @@ m
\#
Cycle through the available audio tracks.
TAB (MPEG-TS and libavformat only)
Cycle through the available programs.
f
Toggle fullscreen (see also ``--fs``).
@@ -119,7 +104,10 @@ T
w and e
Decrease/increase pan-and-scan range.
o
o (also P)
Show progression bar, elapsed time and total duration on the OSD.
O
Toggle OSD states: none / seek / seek + timer / seek + timer + total time.
d
@@ -132,9 +120,6 @@ v
j and J
Cycle through the available subtitles.
F
Toggle displaying "forced subtitles".
x and z
Adjust subtitle delay by +/- 0.1 seconds.
@@ -155,23 +140,14 @@ S
I
Show filename on the OSD.
P
Show progression bar, elapsed time and total duration on the OSD.
! and @
PGUP and PGDWN
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``.
D (``--vo=vdpau``, ``--vf=yadif`` only)
D
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``).)
@@ -187,24 +163,24 @@ corresponding adjustment, or the software equalizer (``--vf=eq``).)
7 and 8
Adjust saturation.
(The following keys are valid only on OSX.)
(The following keys are valid only on OS X.)
command + 0
Resize movie window to half its original size.
Resize video window to half its original size.
(On other platforms, you can bind keys to change the ``window-scale``
property.)
command + 1
Resize movie window to its original size.
Resize video window to its original size.
command + 2
Resize movie window to double its original size.
Resize video window to double its original size.
command + f
Toggle fullscreen (see also ``--fs``).
command + [ and command + ]
Set movie window alpha.
Set video window alpha.
(The following keys are valid if you have a keyboard with multimedia keys.)
@@ -223,12 +199,6 @@ support.)
h and k
Select previous/next channel.
n
Change norm.
u
Change channel list.
Mouse Control
-------------
@@ -338,7 +308,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 ``~/.mpv/config``.
user-specific one is ``~/.config/mpv/mpv.conf``.
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
@@ -373,8 +343,8 @@ File-specific Configuration Files
---------------------------------
You can also write file-specific configuration files. If you wish to have a
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
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
``~/.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
@@ -449,8 +419,8 @@ PROTOCOLS
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``
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]``
@@ -539,15 +509,23 @@ ENVIRONMENT VARIABLES
There are a number of environment variables that can be used to control the
behavior of mpv.
``HOME``
Used to determine mpv config directory: ``$HOME/.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.
``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/config``.
will try to load the config file as ``$MPV_HOME/mpv.conf``.
``MPV_VERBOSE`` (see also ``-v`` and ``--msg-level``)
Set the initial verbosity level across all message modules (default: 0).
@@ -660,18 +638,23 @@ FILES
``/usr/local/etc/mpv/mpv.conf``
mpv system-wide settings (depends on ``--prefix`` passed to configure)
``~/.mpv/config``
``~/.config/mpv/mpv.conf``
mpv user settings
``~/.mpv/input.conf``
input bindings (see ``--input-keylist`` for the full list)
``~/.config/mpv/input.conf``
key bindings (see `INPUT.CONF`_ section)
``~/.mpv/lua/``
``~/.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.
EXAMPLES OF MPV USAGE
=====================
@@ -689,7 +672,7 @@ Play only chapters 5, 6, 7:
Play only titles 5, 6, 7:
``mpv dvd://5-7``
Play a multiangle DVD:
Play a multi-angle DVD:
``mpv dvd://1 --dvd-angle=2``
Play from a different DVD device:
+1838 -1714
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -122,11 +122,11 @@ time remaining
left-click toggle between total and remaining time
============= ================================================
Keybindings
~~~~~~~~~~~
Key Bindings
~~~~~~~~~~~~
These keybindings are active by default if nothing else is already bound to
these keys. In case of collosision, the function needs to be bound to a
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.
============= ================================================
+22 -16
View File
@@ -47,7 +47,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 movies.
black bands from widescreen videos.
``<w>,<h>``
Cropped width and height, defaults to original width and height.
@@ -55,7 +55,7 @@ Available filters are:
Position of the cropped picture, defaults to center.
``expand[=w:h:x:y:aspect:round]``
Expands (not scales) movie resolution to the given value and places the
Expands (not scales) video resolution to the given value and places the
unscaled original at coordinates x, y.
``<w>,<h>``
@@ -93,7 +93,7 @@ Available filters are:
``scale[=w:h:param:param2:chr-drop:noup:arnd``
Scales the image with the software scaler (slow) and performs a YUV<->RGB
colorspace conversion (see also ``--sws``).
color space conversion (see also ``--sws``).
All parameters are optional.
@@ -120,9 +120,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)
--sws=9 (Lanczos): filter length (1-10)
``<chr-drop>``
chroma skipping
@@ -222,8 +222,8 @@ Available filters are:
====================== =====================
``noformat[=fmt]``
Restricts the colorspace for the next filter without doing any conversion.
Unlike the format filter, this will allow any colorspace except the one
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
you specify.
.. note:: For a list of available formats, see ``noformat=fmt=help``.
@@ -232,7 +232,7 @@ Available filters are:
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
``pp[=[filter1[:option1[:option2...]]/[-]filter2...]]``
Enables the specified chain of postprocessing subfilters. Subfilters must
Enables the specified chain of post-processing 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. All subfilters share
@@ -336,7 +336,7 @@ Available filters are:
filtering every second line with a (-1 4 2 4 -1) filter.
``l5/lowpass5``
Vertically applied FIR lowpass deinterlacing filter that deinterlaces
Vertically applied FIR low-pass deinterlacing filter that deinterlaces
the given block by filtering all lines with a (-1 2 6 2 -1) filter.
``fq/forceQuant[:quantizer]``
@@ -356,7 +356,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
@@ -443,7 +443,7 @@ Available filters are:
temporal noise (noise pattern changes between frames)
``uniform``
uniform noise (gaussian otherwise)
uniform noise (Gaussian otherwise)
``hq``
high quality (slightly better looking, slightly slower) - not available
@@ -503,7 +503,7 @@ Available filters are:
:1: linear interpolation (default)
``unsharp[=lx:ly:la:cx:cy:ca]``
unsharp mask / gaussian blur
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.
@@ -617,7 +617,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 movies that have been captured with the
The intended use is to fix PAL videos that have been captured with the
opposite field order to the film-to-video transfer. The options are:
``t``
@@ -769,8 +769,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
@@ -803,7 +803,7 @@ Available filters are:
``gradfun[=strength[:radius|:size=<size>]]``
Fix the banding artifacts that are sometimes introduced into nearly flat
regions by truncation to 8bit color depth. Interpolates the gradients that
regions by truncation to 8-bit color depth. Interpolates the gradients that
should go where the bands are, and dithers them.
``<strength>``
@@ -969,3 +969,9 @@ Available filters are:
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.)
+25 -25
View File
@@ -38,36 +38,36 @@ Available video output drivers are:
.. note:: This driver is for compatibility with old systems.
``adaptor=<number>``
Select a specific XVideo adaptor (check xvinfo results).
Select a specific XVideo adapter (check xvinfo results).
``port=<number>``
Select a specific XVideo port.
``ck=<cur|use|set>``
Select the source from which the colorkey is taken (default: cur).
Select the source from which the color key is taken (default: cur).
cur
The default takes the colorkey currently set in Xv.
The default takes the color key currently set in Xv.
use
Use but do not set the colorkey from mpv (use the ``--colorkey``
Use but do not set the color key from mpv (use the ``--colorkey``
option to change it).
set
Same as use but also sets the supplied colorkey.
Same as use but also sets the supplied color key.
``ck-method=<man|bg|auto>``
Sets the colorkey drawing method (default: man).
Sets the color key drawing method (default: man).
man
Draw the colorkey manually (reduces flicker in some cases).
Draw the color key manually (reduces flicker in some cases).
bg
Set the colorkey as window background.
Set the color key as window background.
auto
Let Xv draw the colorkey.
Let Xv draw the color key.
``colorkey=<number>``
Changes the colorkey to an RGB value of your choice. ``0x000000`` is
Changes the color key to an RGB value of your choice. ``0x000000`` is
black and ``0xffffff`` is white.
``no-colorkey``
Disables colorkeying.
Disables color-keying.
``x11`` (X11 only)
Shared memory video output driver without hardware acceleration that works
@@ -82,7 +82,7 @@ Available video output drivers are:
.. note::
Earlier versions of mpv (and MPlayer, mplayer2) provided sub-options
to tune vdpau postprocessing, like ``deint``, ``sharpen``, ``denoise``,
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.
@@ -173,7 +173,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 queueing functionality controlled by the queuetime
Using the VDPAU frame queuing 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
@@ -282,7 +282,7 @@ Available video output drivers are:
Hardware decoding over OpenGL-interop is supported to some degree. Note
that in this mode, some corner case might not be gracefully handled, and
colorspace conversion and chroma upsampling is generally in the hand of
color space conversion and chroma upsampling is generally in the hand of
the hardware decoder APIs.
``lscale=<filter>``
@@ -349,7 +349,7 @@ 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 quadbuffered stereo. Only supported
Convert side by side input to quad buffered stereo. Only supported
by very few OpenGL cards.
``srgb``
@@ -452,7 +452,7 @@ Available video output drivers are:
auto
auto-select (default)
cocoa
Cocoa/OSX
Cocoa/OS X
win
Win32/WGL
x11
@@ -479,7 +479,7 @@ Available video output drivers are:
``gamma=<0.0..10.0>``
Set a gamma value. If gamma is adjusted in other ways (like with
the ``--gamma`` option or keybindings and the ``gamma`` property), the
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.
@@ -487,7 +487,7 @@ Available video output drivers are:
``icc-profile=<file>``
Load an ICC profile and use it to transform linear RGB to screen output.
Needs LittleCMS2 support compiled in. This option overrides the ``srgb``
Needs LittleCMS 2 support compiled in. This option overrides the ``srgb``
property, as using both is somewhat redundant. It also enables linear
light scaling.
@@ -500,12 +500,12 @@ Available video output drivers are:
``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 LittleCMS2 can take a while
This can be used to speed up loading, since LittleCMS 2 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 colorspaces.
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.
@@ -524,7 +524,7 @@ Available video output drivers are:
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 movies and shows to see if things look better that way.
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``.
@@ -562,7 +562,7 @@ Available video output drivers are:
This is equivalent to::
--vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgb16
--vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
@@ -571,7 +571,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 OSX setups being very slow with ``rgb16``
float texture support, and some OS X setups being very slow with ``rgb16``
but fast with ``rgb32f``.
``opengl-old``
@@ -706,7 +706,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 quadbuffered stereo. Only supported
Convert side by side input to quad buffered stereo. Only supported
by very few OpenGL cards.
The following options are only useful if writing your own fragment programs.
@@ -748,7 +748,7 @@ Available video output drivers are:
auto
auto-select (default)
cocoa
Cocoa/OSX
Cocoa/OS X
win
Win32/WGL
x11
+15 -1
View File
@@ -24,11 +24,14 @@ Release procedure
- 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``.
- Create a new GitHub release using the content of ``RELEASE_NOTES`` related to
the new version.
Release notes template
----------------------
@@ -36,6 +39,9 @@ Release notes template
Here is a template that can be used for writing the ``RELEASE_NOTES`` file.
```markdown
Release 0.X.Y
=============
Changes
-------
@@ -55,3 +61,11 @@ 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.
+12 -12
View File
@@ -17,12 +17,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 (slave mode only)
playlist or an exit command was given (only if --idle it set)
* 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 slave command terminates playback
done or a 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.)
@@ -82,7 +82,7 @@ talloc.h & talloc.c:
(See ta/README for details.)
player/command.c:
This contains the implementation for slave commands and properties.
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,
@@ -92,9 +92,10 @@ 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/options.h, options/options.c
options.h contains the global option struct MPOpts. The option declarations
@@ -111,15 +112,14 @@ options/options.h, options/options.c
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
as remote control devices like Apple IR or client API 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.
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.)
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.
common/msg.h:
All terminal output should go through mp_msg().
@@ -213,7 +213,7 @@ audio/out/:
sub/:
Contains subtitle and OSD rendering.
sub.c/.h is actually the OSD code. It queries dec_sub.c to retrieve
osd.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).
+1 -1
View File
@@ -136,4 +136,4 @@ To contact the `mpv` team in private write to `mpv-team@googlegroups.com`.
[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/release-policy.md
[release-policy]: https://github.com/mpv-player/mpv/blob/master/DOCS/release-policy.md
+173
View File
@@ -0,0 +1,173 @@
Release 0.5.4
=============
Bug fixes
---------
- Fix generation of watch later config file for multiline file names when
--write-filename-in-watch-later-config is used (avoid writing the new lines
in the config, which would make it possible for the filename to set options).
- Fix random crashes on audio uninit.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 6f7f7c3..1a7b6f1`` in
the git repository.
Release 0.5.3
=============
Bug fixes
---------
- Fix build failure introduced by the previous release.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 41f2ad3..41f2ad3`` in
the git repository.
Release 0.5.2
=============
Changes
-------
- Inherit event flags from parent X11 window when using --wid.
- Properly resume playlists loaded at runtime (this makes mpv try to resume a
playlist when loaded like "mpv playlist.m3u").
- Don't honor $XDG_CONFIG_DIRS anymore for global paths (this fixes a regression
on systems that set the variable by default to somewhere else then /etc,
making mpv skip previously created config files).
- Allow up to 256 MB of extradata to make broken files work.
Bug fixes
---------
- Restore old speed change behavior (this fixes a temporary lack of sound after
restoring normal speed e.g. when pressing Backspace).
- Fix quit handling when seeking past EOF while paused.
- Prevent burning CPU when seeking while paused.
- Don't add the playlist base path twice when using --playlist.
- Fix a crash when the initialization of the EGL context fails on Wayland.
- Make EOF handling properly event-based (this fixes a bug that made mpv hang
when changing from one file to the next with --gapless-audio=no).
- Assume icy title data is terminated with ';' (this fixes handling of streams
with metadata containing apostrophes).
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 0f1d6c0..e3d3a47`` in
the git repository.
Release 0.5.1
=============
Changes
-------
- Add audio/flac to the MIME type list in mpv.desktop.
- The --slave-broken option has been reintroduced and is equivalent to using
--input-file=/dev/stdin and --input-terminal=no.
Bug fixes
---------
- Fix screenshots with anamorphic videos in the vdpau vo.
- Fix a crash happening when mpv fails to create a vdpau mixer in the vdpau vo.
- Don't quit when the --loop-file option is used.
- Always load the cursor theme in the wayland vo (this fixes a crash caused by
moving the mouse cursor over the video).
- Properly return non-zero from the zsh completion script when no matches are
found (this fixes the use of user-defined zsh completion matchers).
- Fix the description of the opengl-hq vo default options in the manpage.
- Remove duplicated content from the manpage that was accidentally added in the
manpage reorganization from the previous release.
- Avoid clobbering playback position on video end (when both audio and video
streams reached the end, the playback poisition was briefly set at 0).
- Avoid overwriting user input in the coreaudio_device ao.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 6729e39..fdd648b`` in
the git repository.
Release 0.5.0
=============
Changes
-------
- Support setting the start time relative to the start PTS with --start.
- Use /dev/sr0 as default cdrom/dvd device on Linux.
- Change "cache" property representation to float.
- Split the ao_coreaudio audio output into ao_coreaudio (which uses the
AudioUnits and the CoreAudio sound server) and ao_coreaudio_device (which uses
direct device access).
- Complete supported URL schemes in the zsh completion script.
- Enable compiler optimizations (-O2) during build.
- Allow precise seeking with percent-based seeks too.
- Do precise seeks when clicking on the OSC seekbar.
- Remove support for older VDA hwaccel versions on Mac OS X (requires recent
versions of ffmpeg/libav).
- Remove the last remains of slave mode (--slave-broken option). Support for
reading input commands from stdin can be emulated using: --input-terminal=no
--input-file=/dev/stdin.
- Some key binding were remapped or removed:
+ "LEFT" and "RIGHT" have been remapped to seek 5 seconds instead of 10.
+ "c" for changing the YUV color space was unmapped.
+ OSD level cycling has been remapped from "o" to "O".
+ "o" is now mapped to the show_progress command (like "P").
+ "PGUP" and "PGDWN" have been remapped to chapter-based seeks (the old
bindings "@" and "!" are still available). The old behvaiour has been
remapped to "Shift+PGUP" and "Shift+PGDWM".
+ Increasing/decreasing audio delay have been remapped from "+" and "-" to
"Ctrl++" and "Ctrl+-".
+ "(" and ")" for adjusting audio balance have been unmapped.
+ "F" for toggling the "sub-forced-only" option has been unmapped.
+ "TAB" for cycling TS programs has been unmapped.
+ "A" for cycling DVD angle has been unmapped.
+ "U" for stopping playback has been unmapped.
+ "l" and "L" for cycling quvi format have been unmapped.
- Make it possible to build the client API (libmpv) on Mac OS X.
- Do necessary checks for nested Wayland compositors which only have limited
interfaces.
- The options in the manpage have been re-organized into sections and ordered by
usage to make it easier to navigate.
- The "hard" framedrop mode has been removed (this behaviour is still available
via the --vd-lavc-framedrop option).
- Show the basename of a subtitle file only (without the directory part).
Bug fixes
---------
- Use a default color space for files with no video (this fixes color managed
OSD messages when no video channel is present).
- Fix key equivalent dispatching for Cocoa on Mac OS X.
- Disable non-interleaved audio formats by default with ao_alsa, since some
ALSA plugins don't cope well with them (non-interleaved formats can still be
enabled using the "non-interleaved" ao_alsa option).
- Fix deadlock in the client API when calling mpv_terminate_destroy() before
mpv_initialize().
- Fix flushing of filter chain (this fixes a bug where the last frames would get
dropped on EOF, if buffered by filters).
- Actually use the channels.conf configuration file for DVB.
- Restore the --dvbin-file option.
- Fix off by one error in the command parser which caused a segfault when using
the mp.commandv() Lua function with 10 or more arguments.
New features
------------
- Search for config files in $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS in addition
to ~/.mpv. The config file has also been renamed to mpv.conf (although the
old file still works and will be loaded even if the new one exists).
- Add "playback-time" property that returns the current playback time always
starting at 0. This is now used to show the playback time in the OSD and the
status line, fixing the shown playback time for transport streams that start
at a completely random timestamp.
- Add cache-related "cache-free", "cache-used" and "cache-idle" properties.
- Add option to list supported protocols (--list-protocols).
- Allow setting libavformat stream options with the --stream-lavf-o option.
- Support running the demuxer in a separate thread and prefetch packets (this
can be enabled using the --demuxer-thread 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 cb250d4..52f2f67`` in
the git repository.
+4 -4
View File
@@ -15,10 +15,10 @@ 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 "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):
+5 -5
View File
@@ -30,7 +30,7 @@ esac
testfun()
{
$ILDETECT_MPV "$@" \
--vf=lavfi="[idet]" --msg-level ffmpeg=v \
--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 } \
@@ -87,7 +87,7 @@ judge()
echo "$verdict"
}
judge "$@"
judge "$@" --vf-clr
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
@@ -98,7 +98,7 @@ case "$verdict" in
exit 0
;;
interlaced-tff)
judge "$@" --vf-pre=pullup --field-dominance=top
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=top
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
@@ -117,7 +117,7 @@ case "$verdict" in
esac
;;
interlaced-bff)
judge "$@" --vf-pre=pullup --field-dominance=bottom
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=bottom
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
@@ -136,7 +136,7 @@ case "$verdict" in
esac
;;
interlaced)
judge "$@" --vf-pre=pullup
judge "$@" --vf-clr --vf-pre=pullup
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
+16
View File
@@ -0,0 +1,16 @@
-- 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,4 +1,4 @@
-- Rebuild the status line as a lua script
-- 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
@@ -624,6 +624,90 @@
</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>
+63 -33
View File
@@ -7,7 +7,7 @@ use warnings;
my $mpv = $ARGV[0] || 'mpv';
my @opts = parse_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)');
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*: (.*)');
@@ -15,9 +15,11 @@ 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 ($opts_str, $ao_str, $vo_str, $af_str, $vf_str);
my @protos = parse_opts("$mpv --list-protocols", '^ ([^\s]*)');
$opts_str .= qq{ '$_': \\\n} foreach (@opts);
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);
@@ -32,14 +34,22 @@ chomp $af_str;
$vf_str .= qq{ '$_' \\\n} foreach (@vf);
chomp $vf_str;
$protos_str .= qq{$_ } foreach (@protos);
chomp $protos_str;
my $tmpl = <<"EOS";
#compdef mpv
# mpv zsh completion
_x_arguments -C -s \\
local curcontext="\$curcontext" state state_descr line
typeset -A opt_args
local rc=1
_arguments -C -S \\
$opts_str
'*:files:->mfiles'
'*:files:->mfiles' && rc=0
case \$state in
ao)
@@ -48,7 +58,7 @@ case \$state in
$ao_str
)
_describe -t values 'audio outputs' values
_describe -t values 'audio outputs' values && rc=0
;;
vo)
@@ -57,7 +67,7 @@ $ao_str
$vo_str
)
_describe -t values 'video outputs' values
_describe -t values 'video outputs' values && rc=0
;;
af)
@@ -66,7 +76,7 @@ $vo_str
$af_str
)
_describe -t values 'audio filters' values
_describe -t values 'audio filters' values && rc=0
;;
vf)
@@ -75,55 +85,75 @@ $af_str
$vf_str
)
_describe -t values 'video filters' values
_describe -t values 'video filters' values && 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)(-.)" && ret=0
"*.(#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[@]" && ret=0
compadd -S '' "\$expl[@]" \{dvd,vcd,cdda,cddb,tv\}:// && ret=0
_urls "\$expl[@]" && rc=0
compadd -S '' "\$expl[@]" $protos_str && rc=0
done
fi
(( ret )) || return 0
(( rc )) || return 0
done
;;
esac
return rc
EOS
print $tmpl;
sub parse_opts {
my ($cmd, $regex) = @_;
my ($cmd, $regex, $parsing_main_options) = @_;
my @list;
my @lines = split /\n/, `$cmd`;
my @list;
my @lines = split /\n/, `$cmd`;
foreach my $line (@lines) {
if ($line !~ /^$regex/) {
next;
}
foreach my $line (@lines) {
if ($line !~ /^$regex/) {
next;
}
my $entry = "$1:";
my $entry = $1;
if (defined $2) {
my $desc = $2;
$desc =~ s/\:/\\:/;
if ($parsing_main_options) {
$entry .= '=-';
}
$entry .= $desc;
}
if (defined $2) {
my $desc = $2;
$desc =~ s/\:/\\:/g;
$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 .= ':' . $desc;
}
push @list, $entry if ($line =~ /^$regex/)
}
if ($parsing_main_options) {
$entry .= ':';
return @list;
$entry .= '->ao' if ($1 eq '--ao');
$entry .= '->vo' if ($1 eq '--vo');
$entry .= '->af' if ($1 eq '--af');
$entry .= '->vf' if ($1 eq '--vf');
}
push @list, $entry
}
if ($parsing_main_options) {
@list = sort {
$a =~ /(.*?)\:/; my $ma = $1;
$b =~ /(.*?)\:/; my $mb = $1;
length($mb) <=> length($ma)
} @list;
}
return @list;
}
+1
View File
@@ -0,0 +1 @@
0.5.4
+1 -1
View File
@@ -36,7 +36,7 @@ struct ad_functions {
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_audio)(struct dec_audio *da, struct mp_audio *buffer, int maxlen);
int (*decode_packet)(struct dec_audio *da);
};
enum ad_ctrl {
+22 -74
View File
@@ -33,7 +33,6 @@
#include "common/codecs.h"
#include "common/msg.h"
#include "options/options.h"
#include "common/av_opts.h"
#include "ad.h"
#include "audio/fmt-conversion.h"
@@ -49,14 +48,13 @@ struct priv {
};
static void uninit(struct dec_audio *da);
static int decode_new_packet(struct dec_audio *da);
#define OPT_BASE_STRUCT struct ad_lavc_params
struct ad_lavc_params {
float ac3drc;
int downmix;
int threads;
char *avopt;
char **avopts;
};
const struct m_sub_options ad_lavc_conf = {
@@ -64,7 +62,7 @@ const struct m_sub_options ad_lavc_conf = {
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 2),
OPT_FLAG("downmix", downmix, 0),
OPT_INTRANGE("threads", threads, 0, 1, 16),
OPT_STRING("o", avopt, 0),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
.size = sizeof(struct ad_lavc_params),
@@ -143,11 +141,10 @@ static const char *find_pcm_decoder(const struct pcm_map *map, int format,
return NULL;
}
static int setup_format(struct dec_audio *da)
static void set_data_from_avframe(struct dec_audio *da)
{
struct priv *priv = da->priv;
AVCodecContext *lavc_context = priv->avctx;
struct sh_audio *sh_audio = da->header->audio;
// Note: invalid parameters are rejected by dec_audio.c
@@ -157,12 +154,6 @@ static int setup_format(struct dec_audio *da)
MP_FATAL(da, "unsupported lavc format %s", av_get_sample_fmt_name(fmt));
da->decoded.rate = lavc_context->sample_rate;
if (!da->decoded.rate && sh_audio->wf) {
// If not set, try container samplerate.
// (Maybe this can't happen, and it's an artifact from the past.)
da->decoded.rate = sh_audio->wf->nSamplesPerSec;
MP_WARN(da, "using container rate.\n");
}
struct mp_chmap lavc_chmap;
mp_chmap_from_lavc(&lavc_chmap, lavc_context->channel_layout);
@@ -170,12 +161,15 @@ static int setup_format(struct dec_audio *da)
if (lavc_chmap.num != lavc_context->channels)
mp_chmap_from_channels(&lavc_chmap, lavc_context->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(&da->decoded, &lavc_chmap);
return 0;
da->decoded.samples = priv->avframe->nb_samples;
for (int n = 0; n < da->decoded.num_planes; n++)
da->decoded.planes[n] = priv->avframe->data[n];
}
static void set_from_wf(AVCodecContext *avctx, MP_WAVEFORMATEX *wf)
@@ -233,13 +227,7 @@ 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 (opts->avopt) {
if (parse_avopts(lavc_context, opts->avopt) < 0) {
MP_ERR(da, "setting AVOptions '%s' failed.\n", opts->avopt);
uninit(da);
return 0;
}
}
mp_set_avopts(da->log, lavc_context, opts->avopts);
lavc_context->codec_tag = sh->format;
lavc_context->sample_rate = sh_audio->samplerate;
@@ -267,22 +255,6 @@ static int init(struct dec_audio *da, const char *decoder)
uninit(da);
return 0;
}
MP_VERBOSE(da, "INFO: libavcodec \"%s\" init OK!\n",
lavc_codec->name);
// Decode at least 1 sample: (to get header filled)
for (int tries = 1; ; tries++) {
int x = decode_new_packet(da);
if (x >= 0 && ctx->frame.samples > 0) {
MP_VERBOSE(da, "Initial decode succeeded after %d packets.\n", tries);
break;
}
if (tries >= 50) {
MP_ERR(da, "initial decode failed\n");
uninit(da);
return 0;
}
}
if (lavc_context->bit_rate != 0)
da->bitrate = lavc_context->bit_rate;
@@ -314,7 +286,7 @@ static int control(struct dec_audio *da, int cmd, void *arg)
switch (cmd) {
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
ctx->frame.samples = 0;
mp_audio_set_null_data(&da->decoded);
talloc_free(ctx->packet);
ctx->packet = NULL;
return CONTROL_TRUE;
@@ -322,16 +294,18 @@ static int control(struct dec_audio *da, int cmd, void *arg)
return CONTROL_UNKNOWN;
}
static int decode_new_packet(struct dec_audio *da)
static int decode_packet(struct dec_audio *da)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
priv->frame.samples = 0;
mp_audio_set_null_data(&da->decoded);
struct demux_packet *mpkt = priv->packet;
if (!mpkt)
mpkt = demux_read_packet(da->header);
if (!mpkt) {
if (demux_read_packet_async(da->header, &mpkt) == 0)
return AD_WAIT;
}
priv->packet = talloc_steal(priv, mpkt);
@@ -364,22 +338,16 @@ static int decode_new_packet(struct dec_audio *da)
}
// LATM may need many packets to find mux info
if (ret == AVERROR(EAGAIN))
return 0;
return AD_OK;
}
if (ret < 0) {
MP_VERBOSE(da, "lavc_audio: error\n");
return -1;
MP_ERR(da, "Error decoding audio.\n");
return AD_ERR;
}
if (!got_frame)
return mpkt ? 0 : -1; // -1: eof
return mpkt ? AD_OK : AD_EOF;
if (setup_format(da) < 0)
return -1;
priv->frame.samples = priv->avframe->nb_samples;
mp_audio_copy_config(&priv->frame, &da->decoded);
for (int n = 0; n < priv->frame.num_planes; n++)
priv->frame.planes[n] = priv->avframe->data[n];
set_data_from_avframe(da);
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
if (out_pts != MP_NOPTS_VALUE) {
@@ -387,27 +355,7 @@ static int decode_new_packet(struct dec_audio *da)
da->pts_offset = 0;
}
MP_DBG(da, "Decoded %d -> %d samples\n", in_len,
priv->frame.samples);
return 0;
}
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
{
struct priv *priv = da->priv;
if (!priv->frame.samples) {
if (decode_new_packet(da) < 0)
return -1;
}
if (!mp_audio_config_equals(buffer, &priv->frame))
return 0;
buffer->samples = MPMIN(priv->frame.samples, maxlen);
mp_audio_copy(buffer, 0, &priv->frame, 0, buffer->samples);
mp_audio_skip_samples(&priv->frame, buffer->samples);
da->pts_offset += buffer->samples;
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, da->decoded.samples);
return 0;
}
@@ -424,5 +372,5 @@ const struct ad_functions ad_lavc = {
.init = init,
.uninit = uninit,
.control = control,
.decode_audio = decode_audio,
.decode_packet = decode_packet,
};
+73 -147
View File
@@ -35,9 +35,7 @@
struct ad_mpg123_context {
mpg123_handle *handle;
bool new_format;
int sample_size;
bool need_data;
/* Running mean for bit rate, stream length estimation. */
float mean_rate;
unsigned int mean_count;
@@ -58,7 +56,7 @@ static void uninit(struct dec_audio *da)
/* This initializes libmpg123 and prepares the handle, including funky
* parameters. */
static int preinit(struct dec_audio *da)
static int init(struct dec_audio *da, const char *decoder)
{
int err;
struct ad_mpg123_context *con;
@@ -111,15 +109,18 @@ static int preinit(struct dec_audio *da)
* We need at least 1152 samples. dec_audio.c normally guarantees this. */
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
err = mpg123_open_feed(con->handle);
if (err != MPG123_OK)
goto bad_end;
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_ERR(da, "mpg123 preinit error: %s\n", mpg123_plain_strerror(err));
} else {
MP_ERR(da, "mpg123 preinit error: %s\n", mpg123_strerror(con->handle));
}
uninit(da);
return 0;
@@ -140,111 +141,6 @@ static int mpg123_format_to_af(int mpg123_encoding)
return 0;
}
/* libmpg123 has a new format ready; query and store, return return value
of mpg123_getformat() */
static int set_format(struct dec_audio *da)
{
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(&da->decoded, channels);
da->decoded.rate = rate;
int af = mpg123_format_to_af(encoding);
if (!af) {
/* 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);
return MPG123_ERR;
}
mp_audio_set_format(&da->decoded, af);
con->sample_size = channels * af_fmt2bps(af);
con->new_format = 0;
}
return ret;
}
static int feed_new_packet(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
int ret;
struct demux_packet *pkt = demux_read_packet(da->header);
if (!pkt)
return -1; /* EOF. */
/* Next bytes from that presentation time. */
if (pkt->pts != MP_NOPTS_VALUE) {
da->pts = pkt->pts;
da->pts_offset = 0;
}
/* Have to use mpg123_feed() to avoid decoding here. */
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
talloc_free(pkt);
if (ret == MPG123_ERR)
return -1;
if (ret == MPG123_NEW_FORMAT)
con->new_format = 1;
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(struct dec_audio *da, const char *decoder)
{
if (!preinit(da))
return 0;
struct ad_mpg123_context *con = da->priv;
int ret;
ret = mpg123_open_feed(con->handle);
if (ret != MPG123_OK)
goto fail;
for (int n = 0; ; n++) {
if (feed_new_packet(da) < 0) {
ret = MPG123_NEED_MORE;
goto fail;
}
size_t got_now = 0;
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
if (ret == MPG123_OK || ret == MPG123_NEW_FORMAT) {
ret = set_format(da);
if (ret == MPG123_OK)
break;
}
if (ret != MPG123_NEED_MORE)
goto fail;
// max. 16 retries (randomly chosen number)
if (n > 16) {
ret = MPG123_NEED_MORE;
goto fail;
}
}
return 1;
fail:
if (ret == MPG123_NEED_MORE) {
MP_ERR(da, "Could not find mp3 stream.\n");
} else {
MP_ERR(da, "mpg123 init error: %s\n",
mpg123_strerror(con->handle));
}
uninit(da);
return 0;
}
/* Compute bitrate from frame size. */
static int compute_bitrate(struct mpg123_frameinfo *i)
{
@@ -290,57 +186,86 @@ static void update_info(struct dec_audio *da)
}
}
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
/* libmpg123 has a new format ready; query and store, return return value
of mpg123_getformat() */
static int set_format(struct dec_audio *da)
{
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(&da->decoded, channels);
da->decoded.rate = rate;
int af = mpg123_format_to_af(encoding);
if (!af) {
/* 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);
return MPG123_ERR;
}
mp_audio_set_format(&da->decoded, af);
con->sample_size = channels * af_fmt2bps(af);
}
return ret;
}
static int decode_packet(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
void *buf = buffer->planes[0];
int ret;
if (con->new_format) {
ret = set_format(da);
if (ret == MPG123_OK) {
return 0; // let caller handle format change
} else if (ret == MPG123_NEED_MORE) {
con->need_data = true;
} else {
goto mpg123_fail;
}
mp_audio_set_null_data(&da->decoded);
struct demux_packet *pkt;
if (demux_read_packet_async(da->header, &pkt) == 0)
return AD_WAIT;
if (!pkt)
return AD_EOF;
/* Next bytes from that presentation time. */
if (pkt->pts != MP_NOPTS_VALUE) {
da->pts = pkt->pts;
da->pts_offset = 0;
}
if (con->need_data) {
if (feed_new_packet(da) < 0)
return -1;
}
/* Have to use mpg123_feed() to avoid decoding here. */
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
talloc_free(pkt);
if (!mp_audio_config_equals(&da->decoded, buffer))
return 0;
size_t got_now = 0;
ret = mpg123_replace_buffer(con->handle, buf, maxlen * con->sample_size);
if (ret != MPG123_OK)
goto mpg123_fail;
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
unsigned char *audio = NULL;
size_t bytes = 0;
ret = mpg123_decode_frame(con->handle, NULL, &audio, &bytes);
int got_samples = got_now / con->sample_size;
buffer->samples += got_samples;
da->pts_offset += got_samples;
if (ret == MPG123_NEED_MORE)
return 0;
if (ret == MPG123_NEW_FORMAT) {
con->new_format = true;
} else if (ret == MPG123_NEED_MORE) {
con->need_data = true;
} else if (ret != MPG123_OK && ret != MPG123_DONE) {
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;
da->decoded.planes[0] = audio;
da->decoded.samples = got_samples;
update_info(da);
return 0;
mpg123_fail:
MP_ERR(da, "mpg123 decoding error: %s\n",
mpg123_strerror(con->handle));
return -1;
MP_ERR(da, "mpg123 decoding error: %s\n", mpg123_strerror(con->handle));
return AD_ERR;
}
static int control(struct dec_audio *da, int cmd, void *arg)
@@ -349,6 +274,7 @@ static int control(struct dec_audio *da, int cmd, void *arg)
switch (cmd) {
case ADCTRL_RESET:
mp_audio_set_null_data(&da->decoded);
mpg123_close(con->handle);
if (mpg123_open_feed(con->handle) != MPG123_OK) {
@@ -373,5 +299,5 @@ const struct ad_functions ad_mpg123 = {
.init = init,
.uninit = uninit,
.control = control,
.decode_audio = decode_audio,
.decode_packet = decode_packet,
};
+15 -18
View File
@@ -38,8 +38,7 @@ struct spdifContext {
AVFormatContext *lavf_ctx;
int iec61937_packet_size;
int out_buffer_len;
int out_buffer_size;
uint8_t *out_buffer;
uint8_t out_buffer[OUTBUF_SIZE];
bool need_close;
};
@@ -47,7 +46,7 @@ static int write_packet(void *p, uint8_t *buf, int buf_size)
{
struct spdifContext *ctx = p;
int buffer_left = ctx->out_buffer_size - ctx->out_buffer_len;
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;
@@ -183,24 +182,21 @@ fail:
return 0;
}
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
static int decode_packet(struct dec_audio *da)
{
struct spdifContext *spdif_ctx = da->priv;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
int sstride = 2 * da->decoded.channels.num;
assert(sstride == buffer->sstride);
if (maxlen * sstride < spdif_ctx->iec61937_packet_size)
return 0;
mp_audio_set_null_data(&da->decoded);
spdif_ctx->out_buffer_len = 0;
spdif_ctx->out_buffer_size = maxlen * sstride;
spdif_ctx->out_buffer = buffer->planes[0];
struct demux_packet *mpkt = demux_read_packet(da->header);
struct demux_packet *mpkt;
if (demux_read_packet_async(da->header, &mpkt) == 0)
return AD_WAIT;
if (!mpkt)
return -1;
return AD_EOF;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
@@ -211,12 +207,13 @@ static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxle
da->pts_offset = 0;
}
int ret = av_write_frame(lavf_ctx, &pkt);
avio_flush(lavf_ctx->pb);
buffer->samples = spdif_ctx->out_buffer_len / sstride;
da->pts_offset += buffer->samples;
talloc_free(mpkt);
avio_flush(lavf_ctx->pb);
if (ret < 0)
return -1;
return AD_ERR;
da->decoded.planes[0] = spdif_ctx->out_buffer;
da->decoded.samples = spdif_ctx->out_buffer_len / da->decoded.sstride;
return 0;
}
@@ -253,5 +250,5 @@ const struct ad_functions ad_spdif = {
.init = init,
.uninit = uninit,
.control = control,
.decode_audio = decode_audio,
.decode_packet = decode_packet,
};
+54 -51
View File
@@ -56,13 +56,6 @@ static const struct ad_functions * const ad_drivers[] = {
NULL
};
// ad_mpg123 needs to be able to decode 1152 samples at once
// ad_spdif needs up to 8192
#define DECODE_MAX_UNIT MPMAX(8192, 1152)
// At least 8192 samples, plus hack for ad_mpg123 and ad_spdif
#define DECODE_BUFFER_SAMPLES (8192 + DECODE_MAX_UNIT)
// Drop audio buffer and reinit it (after format change)
// Returns whether the format was valid at all.
static bool reinit_audio_buffer(struct dec_audio *da)
@@ -73,7 +66,6 @@ static bool reinit_audio_buffer(struct dec_audio *da)
return false;
}
mp_audio_buffer_reinit(da->decode_buffer, &da->decoded);
mp_audio_buffer_preallocate_min(da->decode_buffer, DECODE_BUFFER_SAMPLES);
return true;
}
@@ -98,11 +90,6 @@ static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
}
d_audio->decode_buffer = mp_audio_buffer_create(NULL);
if (!reinit_audio_buffer(d_audio)) {
uninit_decoder(d_audio);
return 0;
}
return 1;
}
@@ -164,9 +151,6 @@ int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders)
talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family,
decoder->decoder);
MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc);
MP_VERBOSE(d_audio, "AUDIO: %d Hz, %d ch, %s\n",
d_audio->decoded.rate, d_audio->decoded.channels.num,
af_fmt_to_str(d_audio->decoded.format));
} else {
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
d_audio->header->codec ? d_audio->header->codec : "<unknown>");
@@ -209,8 +193,6 @@ int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
mp_audio_set_channels(&afs->output, out_channels);
mp_audio_set_format(&afs->output, *out_format);
afs->metadata = d_audio->metadata;
afs->replaygain_data = d_audio->replaygain_data;
char *s_from = mp_audio_config_to_str(&afs->input);
@@ -233,61 +215,82 @@ int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
return 1;
}
/* 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 (!mp_audio_config_valid(&da->decoded)) {
if (da->decoded.samples > 0)
return AD_ERR; // invalid format, rather than uninitialized
int ret = da->ad_driver->decode_packet(da);
if (ret < 0)
return ret;
}
if (mp_audio_buffer_samples(da->decode_buffer) > 0) // avoid accidental flush
return AD_OK;
return reinit_audio_buffer(da) ? AD_OK : AD_ERR;
}
// Filter len bytes of input, put result into outbuf.
static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf,
int len)
{
bool format_change = false;
int error = 0;
struct mp_audio config;
mp_audio_buffer_get_format(da->decode_buffer, &config);
assert(len > 0); // would break EOF logic below
while (mp_audio_buffer_samples(da->decode_buffer) < len) {
int maxlen = mp_audio_buffer_get_write_available(da->decode_buffer);
if (maxlen < DECODE_MAX_UNIT)
break;
struct mp_audio buffer;
mp_audio_buffer_get_write_buffer(da->decode_buffer, maxlen, &buffer);
buffer.samples = 0;
error = da->ad_driver->decode_audio(da, &buffer, maxlen);
if (error < 0)
break;
// Commit the data just read as valid data
mp_audio_buffer_finish_write(da->decode_buffer, buffer.samples);
// Format change
if (!mp_audio_config_equals(&da->decoded, &config)) {
// If there are still samples left in the buffer, let them drain
// first, and don't signal a format change to the caller yet.
if (mp_audio_buffer_samples(da->decode_buffer) > 0)
break;
error = -2;
// Check for a format change
struct mp_audio config;
mp_audio_buffer_get_format(da->decode_buffer, &config);
format_change = !mp_audio_config_equals(&da->decoded, &config);
if (format_change) {
error = AD_EOF; // drain remaining data left in the current buffer
break;
}
if (da->decoded.samples > 0) {
int copy = MPMIN(da->decoded.samples, len);
struct mp_audio append = da->decoded;
append.samples = copy;
mp_audio_buffer_append(da->decode_buffer, &append);
mp_audio_skip_samples(&da->decoded, copy);
da->pts_offset += copy;
continue;
}
error = da->ad_driver->decode_packet(da);
if (error < 0)
break;
}
if (error == AD_WAIT)
return error;
// Filter
struct mp_audio filter_data;
mp_audio_buffer_peek(da->decode_buffer, &filter_data);
filter_data.rate = da->afilter->input.rate; // due to playback speed change
len = MPMIN(filter_data.samples, len);
filter_data.samples = len;
bool eof = filter_data.samples == 0 && error < 0;
bool eof = error == AD_EOF && filter_data.samples == 0;
if (af_filter(da->afilter, &filter_data, eof ? AF_FILTER_FLAG_EOF : 0) < 0)
return -1;
return AD_ERR;
mp_audio_buffer_append(outbuf, &filter_data);
if (eof && filter_data.samples > 0)
if (error == AD_EOF && filter_data.samples > 0)
error = 0; // don't end playback yet
// remove processed data from decoder buffer:
mp_audio_buffer_skip(da->decode_buffer, len);
// Assume the filter chain is drained from old data at this point.
// (If not, the remaining old data is discarded.)
if (error == -2) {
// if format was changed, and all data was drained, execute the format change
if (format_change && eof) {
error = AD_NEW_FMT;
if (!reinit_audio_buffer(da))
error = -1; // switch to invalid format
error = AD_ERR; // switch to invalid format
}
return error;
@@ -295,17 +298,17 @@ static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf,
/* Try to get at least minsamples decoded+filtered samples in outbuf
* (total length including possible existing data).
* Return 0 on success, -1 on error/EOF (not distinguidaed).
* 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 *d_audio, struct mp_audio_buffer *outbuf,
int minsamples)
{
if (!d_audio->afilter)
return AD_ERR;
// 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 samples
// (Note: the reason for this is unknown, possibly a refactoring artifact)
int unitsize = 16;
/* Filter output size will be about filter_multiplier times input size.
* If some filter buffers audio in big blocks this might only hold
@@ -322,8 +325,8 @@ int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
prev_buffered = buffered;
int decsamples = (minsamples - buffered) / filter_multiplier;
// + some extra for possible filter buffering
decsamples += unitsize << 5;
// + some extra for possible filter buffering, and avoid 0
decsamples += 512;
if (huge_filter_buffer) {
/* Some filter must be doing significant buffering if the estimated
+11 -4
View File
@@ -36,12 +36,10 @@ struct dec_audio {
struct mp_audio_buffer *decode_buffer;
struct af_stream *afilter;
char *decoder_desc;
struct mp_tags *metadata;
struct replaygain_data *replaygain_data;
int init_retries;
// set by decoder
struct mp_audio decoded; // format of decoded audio (no data, temporarily
// different from decode_buffer during format
// changes)
struct mp_audio decoded; // decoded audio set by last decode_packet() call
int bitrate; // input bitrate, can change with VBR sources
// last known pts value in output from decoder
double pts;
@@ -51,10 +49,19 @@ struct dec_audio {
void *priv;
};
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);
+6 -7
View File
@@ -204,7 +204,6 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
.mul = 1,
.data = talloc_zero(af, struct mp_audio),
.log = mp_log_new(af, s->log, name),
.metadata = s->metadata,
.replaygain_data = s->replaygain_data,
};
struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
@@ -311,22 +310,22 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
struct af_instance *af = s->first;
while (af) {
MP_MSG(s, msg_level, " [%s] ", af->info->name);
char b[128] = {0};
mp_snprintf_cat(b, sizeof(b), " [%s] ", af->info->name);
if (af->data) {
char *info = mp_audio_config_to_str(af->data);
MP_MSG(s, msg_level, "%s", info);
mp_snprintf_cat(b, sizeof(b), "%s", info);
talloc_free(info);
}
if (af == at)
MP_MSG(s, msg_level, " <-");
MP_MSG(s, msg_level, "\n");
mp_snprintf_cat(b, sizeof(b), " <-");
MP_MSG(s, msg_level, "%s\n", b);
af = af->next;
}
MP_MSG(s, msg_level, " [ao] ");
char *info = mp_audio_config_to_str(&s->output);
MP_MSG(s, msg_level, "%s\n", info);
MP_MSG(s, msg_level, " [ao] %s\n", info);
talloc_free(info);
}
-2
View File
@@ -59,7 +59,6 @@ struct af_info {
struct af_instance {
const struct af_info *info;
struct mp_log *log;
struct mp_tags *metadata;
struct replaygain_data *replaygain_data;
int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af);
@@ -92,7 +91,6 @@ struct af_stream {
struct mp_log *log;
struct MPOpts *opts;
struct mp_tags *metadata;
struct replaygain_data *replaygain_data;
};
+5 -7
View File
@@ -38,8 +38,9 @@
#include "audio/fmt-conversion.h"
#include "af.h"
#include "common/av_common.h"
#include "options/m_option.h"
#include "common/av_opts.h"
// FFmpeg and Libav have slightly different APIs, just enough to cause us
// unnecessary pain. <Expletive deleted.>
@@ -62,7 +63,7 @@ struct priv {
// options
char *cfg_graph;
char *cfg_avopts;
char **cfg_avopts;
};
static void destroy_graph(struct af_instance *af)
@@ -90,11 +91,8 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
if (!graph)
goto error;
if (parse_avopts(graph, p->cfg_avopts) < 0) {
MP_FATAL(af, "lavfi: could not set opts: '%s'\n",
p->cfg_avopts);
if (mp_set_avopts(af->log, graph, p->cfg_avopts) < 0)
goto error;
}
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
@@ -306,7 +304,7 @@ const struct af_info af_info_lavfi = {
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_STRING("graph", cfg_graph, 0),
OPT_STRING("o", cfg_avopts, 0),
OPT_KEYVALUELIST("o", cfg_avopts, 0),
{0}
},
};
+4 -6
View File
@@ -52,9 +52,9 @@
#error "config.h broken or no resampler found"
#endif
#include "common/av_common.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "common/av_opts.h"
#include "audio/filter/af.h"
#include "audio/fmt-conversion.h"
@@ -74,7 +74,7 @@ struct af_resample_opts {
struct af_resample {
int allow_detach;
char *avopts;
char **avopts;
struct AVAudioResampleContext *avrctx;
struct AVAudioResampleContext *avrctx_out; // for output channel reordering
struct af_resample_opts ctx; // opts in the context
@@ -164,10 +164,8 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
av_opt_set_double(s->avrctx, "cutoff", s->ctx.cutoff, 0);
if (parse_avopts(s->avrctx, s->avopts) < 0) {
MP_FATAL(af, "af_lavrresample: could not set opts: '%s'\n", s->avopts);
if (mp_set_avopts(af->log, s->avrctx, s->avopts) < 0)
return AF_ERROR;
}
struct mp_chmap map_in = in->channels;
struct mp_chmap map_out = out->channels;
@@ -402,7 +400,7 @@ const struct af_info af_info_lavrresample = {
OPT_FLAG("linear", opts.linear, 0),
OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1),
OPT_FLAG("detach", allow_detach, 0),
OPT_STRING("o", avopts, 0),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
};
+2 -2
View File
@@ -79,7 +79,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (!s->rgain_clip) // clipping prevention
s->rgain = MPMIN(s->rgain, 1.0 / peak);
}
if (s->detach && fabs(s->level + s->rgain - 2.0) < 0.00001)
if (s->detach && fabs(s->level * s->rgain - 1.0) < 0.00001)
return AF_DETACH;
return af_test_output(af, in);
}
@@ -97,7 +97,7 @@ static void filter_plane(struct af_instance *af, void *ptr, int num_samples)
{
struct priv *s = af->priv;
float level = s->level + s->rgain - 1.0;
float level = s->level * s->rgain;
if (af_fmt_from_planar(af->data->format) == AF_FORMAT_S16) {
int16_t *a = ptr;
+1 -1
View File
@@ -24,8 +24,8 @@
#define MPLAYER_AF_FORMAT_H
#include <stdbool.h>
#include <sys/types.h>
#include "osdep/endian.h"
#include "bstr/bstr.h"
#if BYTE_ORDER == BIG_ENDIAN
+6 -14
View File
@@ -31,13 +31,13 @@
#include "options/options.h"
#include "options/m_config.h"
#include "osdep/timer.h"
#include "common/msg.h"
#include "common/common.h"
#include "common/global.h"
extern const struct ao_driver audio_out_oss;
extern const struct ao_driver audio_out_coreaudio;
extern const struct ao_driver audio_out_coreaudio_exclusive;
extern const struct ao_driver audio_out_rsound;
extern const struct ao_driver audio_out_sndio;
extern const struct ao_driver audio_out_pulse;
@@ -94,6 +94,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_ENCODING
&audio_out_lavc,
#endif
#if HAVE_COREAUDIO
&audio_out_coreaudio_exclusive,
#endif
#if HAVE_RSOUND
&audio_out_rsound,
#endif
@@ -309,27 +312,16 @@ void ao_resume(struct ao *ao)
ao->api->resume(ao);
}
// Be careful with locking
void ao_wait_drain(struct ao *ao)
{
// This is probably not entirely accurate, but good enough.
mp_sleep_us(ao_get_delay(ao) * 1000000);
ao_reset(ao);
}
// Block until the current audio buffer has played completely.
void ao_drain(struct ao *ao)
{
if (ao->api->drain) {
if (ao->api->drain)
ao->api->drain(ao);
} else {
ao_wait_drain(ao);
}
}
bool ao_eof_reached(struct ao *ao)
{
return ao_get_delay(ao) < AO_EOF_DELAY;
return ao->api->get_eof ? ao->api->get_eof(ao) : true;
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
+6 -1
View File
@@ -31,7 +31,6 @@
#include <sys/time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
@@ -64,6 +63,7 @@ struct priv {
char *cfg_mixer_name;
int cfg_mixer_index;
int cfg_resample;
int cfg_ni;
};
#define BUFFER_TIME 250000 // 250ms
@@ -353,6 +353,9 @@ static int init(struct ao *ao)
struct priv *p = ao->priv;
if (!p->cfg_ni)
ao->format = af_fmt_from_planar(ao->format);
/* switch for spdif
* sets opening sequence for SPDIF
* sets also the playback and other switches 'on the fly'
@@ -755,6 +758,7 @@ const struct ao_driver audio_out_alsa = {
.cfg_mixer_device = "default",
.cfg_mixer_name = "Master",
.cfg_mixer_index = 0,
.cfg_ni = 0,
},
.options = (const struct m_option[]) {
OPT_STRING("device", cfg_device, 0),
@@ -763,6 +767,7 @@ const struct ao_driver audio_out_alsa = {
OPT_STRING("mixer-device", cfg_mixer_device, 0),
OPT_STRING("mixer-name", cfg_mixer_name, 0),
OPT_INTRANGE("mixer-index", cfg_mixer_index, 0, 0, 99),
OPT_FLAG("non-interleaved", cfg_ni, 0),
{0}
},
};
+257 -514
View File
@@ -1,39 +1,21 @@
/*
* CoreAudio audio output driver for Mac OS X
* This file is part of mpv.
*
* original copyright (C) Timothy J. Wood - Aug 2000
* ported to MPlayer libao2 by Dan Christiansen
*
* Chris Roccati
* Stefano Pigozzi
*
* The S/PDIF part of the code is based on the auhal audio output
* module from VideoLAN:
* Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org>
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* 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.
*
* MPlayer is distributed in the hope that it will be useful,
* 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
* along with MPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* The MacOS X CoreAudio framework doesn't mesh as simply as some
* simpler frameworks do. This is due to the fact that CoreAudio pulls
* audio samples rather than having them pushed at it (which is nice
* when you are wanting to do good buffering of audio).
*/
#include <CoreAudio/HostTime.h>
#include "config.h"
#include "ao.h"
@@ -46,61 +28,64 @@
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
static void audio_pause(struct ao *ao);
static void audio_resume(struct ao *ao);
static void reset(struct ao *ao);
static void print_buffer(struct ao *ao, struct mp_ring *buffer)
{
void *tctx = talloc_new(NULL);
MP_VERBOSE(ao, "%s\n", mp_ring_repr(buffer, tctx));
talloc_free(tctx);
}
struct priv_d {
// digital render callback
AudioDeviceIOProcID render_cb;
// pid set for hog mode, (-1) means that hog mode on the device was
// released. hog mode is exclusive access to a device
pid_t hog_pid;
// stream selected for digital playback by the detection in init
AudioStreamID stream;
// stream index in an AudioBufferList
int stream_idx;
// format we changed the stream to: for the digital case each application
// sets the stream format for a device to what it needs
AudioStreamBasicDescription stream_asbd;
AudioStreamBasicDescription original_asbd;
bool changed_mixing;
int stream_asbd_changed;
bool muted;
};
struct priv {
AudioDeviceID device; // selected device
bool is_digital; // running in digital mode?
AudioDeviceID device;
AudioUnit audio_unit;
AudioUnit audio_unit; // AudioUnit for lpcm output
bool paused;
struct mp_ring *buffer;
struct priv_d *digital;
uint64_t hw_latency_us;
// options
int opt_device_id;
int opt_list;
};
static int get_ring_size(struct ao *ao)
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap);
static int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
{
return af_fmt_seconds_to_bytes(
ao->format, 0.5, ao->channels.num, ao->samplerate);
return frames / (float) ao->samplerate * 1e6;
}
static int64_t ca_get_hardware_latency(struct ao *ao) {
struct priv *p = ao->priv;
double audiounit_latency_sec = 0.0;
uint32_t size = sizeof(audiounit_latency_sec);
OSStatus err = AudioUnitGetProperty(
p->audio_unit,
kAudioUnitProperty_Latency,
kAudioUnitScope_Global,
0,
&audiounit_latency_sec,
&size);
CHECK_CA_ERROR("cannot get audio unit latency");
uint32_t frames = 0;
err = CA_GET_O(p->device, kAudioDevicePropertyLatency, &frames);
CHECK_CA_ERROR("cannot get device latency");
uint64_t audiounit_latency_us = audiounit_latency_sec * 1e6;
uint64_t device_latency_us = ca_frames_to_us(ao, frames);
MP_VERBOSE(ao, "audiounit latency [us]: %lld\n", audiounit_latency_us);
MP_VERBOSE(ao, "device latency [us]: %lld\n", device_latency_us);
return audiounit_latency_us + device_latency_us;
coreaudio_error:
return 0;
}
static int64_t ca_get_latency(const AudioTimeStamp *ts)
{
uint64_t out = AudioConvertHostTimeToNanos(ts->mHostTime);
uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
if (now > out)
return 0;
return (out - now) * 1e-3;
}
static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
@@ -109,259 +94,125 @@ static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
{
struct ao *ao = ctx;
struct priv *p = ao->priv;
AudioBuffer buf = buffer_list->mBuffers[0];
int requested = buf.mDataByteSize;
if (mp_ring_buffered(p->buffer) < requested) {
MP_VERBOSE(ao, "buffer underrun\n");
audio_pause(ao);
memset(buf.mData, 0, requested);
} else {
mp_ring_read(p->buffer, buf.mData, requested);
}
int64_t end = mp_time_us();
end += p->hw_latency_us + ca_get_latency(ts) + ca_frames_to_us(ao, frames);
ao_read_data(ao, &buf.mData, frames, end);
return noErr;
}
static OSStatus render_cb_digital(
AudioDeviceID device, const AudioTimeStamp *ts,
const void *in_data, const AudioTimeStamp *in_ts,
AudioBufferList *out_data, const AudioTimeStamp *out_ts, void *ctx)
{
struct ao *ao = ctx;
struct priv *p = ao->priv;
struct priv_d *d = p->digital;
AudioBuffer buf = out_data->mBuffers[d->stream_idx];
int requested = buf.mDataByteSize;
static int get_volume(struct ao *ao, struct ao_control_vol *vol) {
struct priv *p = ao->priv;
float auvol;
OSStatus err =
AudioUnitGetParameter(p->audio_unit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, &auvol);
if (d->muted)
mp_ring_drain(p->buffer, requested);
else
mp_ring_read(p->buffer, buf.mData, requested);
CHECK_CA_ERROR("could not get HAL output volume");
vol->left = vol->right = auvol * 100.0;
return CONTROL_TRUE;
coreaudio_error:
return CONTROL_ERROR;
}
return noErr;
static int set_volume(struct ao *ao, struct ao_control_vol *vol) {
struct priv *p = ao->priv;
float auvol = (vol->left + vol->right) / 200.0;
OSStatus err =
AudioUnitSetParameter(p->audio_unit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, auvol, 0);
CHECK_CA_ERROR("could not set HAL output volume");
return CONTROL_TRUE;
coreaudio_error:
return CONTROL_ERROR;
}
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
ao_control_vol_t *control_vol;
OSStatus err;
Float32 vol;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
control_vol = (ao_control_vol_t *)arg;
if (p->is_digital) {
struct priv_d *d = p->digital;
// Digital output has no volume adjust.
int digitalvol = d->muted ? 0 : 100;
*control_vol = (ao_control_vol_t) {
.left = digitalvol, .right = digitalvol,
};
return CONTROL_TRUE;
}
err = AudioUnitGetParameter(p->audio_unit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, &vol);
CHECK_CA_ERROR("could not get HAL output volume");
control_vol->left = control_vol->right = vol * 100.0;
return CONTROL_TRUE;
return get_volume(ao, arg);
case AOCONTROL_SET_VOLUME:
control_vol = (ao_control_vol_t *)arg;
if (p->is_digital) {
struct priv_d *d = p->digital;
// Digital output can not set volume. Here we have to return true
// to make mixer forget it. Else mixer will add a soft filter,
// that's not we expected and the filter not support ac3 stream
// will cause mplayer die.
// Although not support set volume, but at least we support mute.
// MPlayer set mute by set volume to zero, we handle it.
if (control_vol->left == 0 && control_vol->right == 0)
d->muted = true;
else
d->muted = false;
return CONTROL_TRUE;
}
vol = (control_vol->left + control_vol->right) / 200.0;
err = AudioUnitSetParameter(p->audio_unit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, vol, 0);
CHECK_CA_ERROR("could not set HAL output volume");
return CONTROL_TRUE;
} // end switch
return CONTROL_UNKNOWN;
coreaudio_error:
return CONTROL_ERROR;
}
static void print_list(struct ao *ao)
{
char *help = talloc_strdup(NULL, "Available output devices:\n");
AudioDeviceID *devs;
size_t n_devs;
OSStatus err =
CA_GET_ARY(kAudioObjectSystemObject, kAudioHardwarePropertyDevices,
&devs, &n_devs);
CHECK_CA_ERROR("Failed to get list of output devices.");
for (int i = 0; i < n_devs; i++) {
char *name;
err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
if (err == noErr)
talloc_steal(devs, name);
else
name = "Unknown";
help = talloc_asprintf_append(
help, " * %s (id: %" PRIu32 ")\n", name, devs[i]);
return set_volume(ao, arg);
}
talloc_free(devs);
coreaudio_error:
MP_INFO(ao, "%s", help);
talloc_free(help);
return CONTROL_UNKNOWN;
}
static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd);
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd);
static bool init_chmap(struct ao *ao);
static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd);
static int init(struct ao *ao)
{
OSStatus err;
struct priv *p = ao->priv;
struct priv *p = ao->priv;
if (p->opt_list) print_list(ao);
struct priv_d *d = talloc_zero(p, struct priv_d);
*d = (struct priv_d) {
.muted = false,
.stream_asbd_changed = 0,
.hog_pid = -1,
.stream = 0,
.stream_idx = -1,
.changed_mixing = false,
};
p->digital = d;
if (p->opt_list) ca_print_device_list(ao);
ao->per_application_mixer = true;
ao->no_persistent_volume = true;
AudioDeviceID selected_device = 0;
if (p->opt_device_id < 0) {
// device not set by user, get the default one
err = CA_GET(kAudioObjectSystemObject,
kAudioHardwarePropertyDefaultOutputDevice,
&selected_device);
CHECK_CA_ERROR("could not get default audio device");
} else {
selected_device = p->opt_device_id;
}
OSStatus err = ca_select_device(ao, p->opt_device_id, &p->device);
CHECK_CA_ERROR("failed to select device");
if (mp_msg_test(ao->log, MSGL_V)) {
char *name;
err = CA_GET_STR(selected_device, kAudioObjectPropertyName, &name);
CHECK_CA_ERROR("could not get selected audio device name");
MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n",
name, selected_device);
talloc_free(name);
}
// Save selected device id
p->device = selected_device;
if (!init_chmap(ao))
goto coreaudio_error;
ao->format = af_fmt_from_planar(ao->format);
bool supports_digital = false;
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
if (AF_FORMAT_IS_AC3(ao->format)) {
if (ca_device_supports_digital(ao, selected_device))
supports_digital = true;
}
if (!supports_digital) {
AudioChannelLayout *layouts;
size_t n_layouts;
err = CA_GET_ARY_O(selected_device,
kAudioDevicePropertyPreferredChannelLayout,
&layouts, &n_layouts);
CHECK_CA_ERROR("could not get audio device prefered layouts");
struct mp_chmap_sel chmap_sel = {0};
for (int i = 0; i < n_layouts; i++) {
struct mp_chmap chmap = {0};
if (ca_layout_to_mp_chmap(ao, &layouts[i], &chmap))
mp_chmap_sel_add_map(&chmap_sel, &chmap);
}
talloc_free(layouts);
if (ao->channels.num < 3) {
struct mp_chmap chmap;
mp_chmap_from_channels(&chmap, ao->channels.num);
mp_chmap_sel_add_map(&chmap_sel, &chmap);
}
if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
MP_ERR(ao, "could not select a suitable channel map among the "
"hardware supported ones. Make sure to configure your "
"output device correctly in 'Audio MIDI Setup.app'\n");
goto coreaudio_error;
}
} // closes if (!supports_digital)
// Build ASBD for the input format
AudioStreamBasicDescription asbd;
asbd.mSampleRate = ao->samplerate;
asbd.mFormatID = supports_digital ?
kAudioFormat60958AC3 : kAudioFormatLinearPCM;
asbd.mChannelsPerFrame = ao->channels.num;
asbd.mBitsPerChannel = af_fmt2bits(ao->format);
asbd.mFormatFlags = kAudioFormatFlagIsPacked;
ca_fill_asbd(ao, &asbd);
if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F)
asbd.mFormatFlags |= kAudioFormatFlagIsFloat;
if (!init_audiounit(ao, asbd))
goto coreaudio_error;
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
asbd.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
asbd.mFormatFlags |= kAudioFormatFlagIsBigEndian;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerPacket = asbd.mBytesPerFrame =
asbd.mFramesPerPacket * asbd.mChannelsPerFrame *
(asbd.mBitsPerChannel / 8);
ca_print_asbd(ao, "source format:", &asbd);
if (supports_digital)
return init_digital(ao, asbd);
else
return init_lpcm(ao, asbd);
return CONTROL_OK;
coreaudio_error:
return CONTROL_ERROR;
}
static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd)
static bool init_chmap(struct ao *ao)
{
struct priv *p = ao->priv;
OSStatus err;
AudioChannelLayout *layouts;
size_t n_layouts;
err = CA_GET_ARY_O(p->device,
kAudioDevicePropertyPreferredChannelLayout,
&layouts, &n_layouts);
CHECK_CA_ERROR("could not get audio device prefered layouts");
struct mp_chmap_sel chmap_sel = {0};
for (int i = 0; i < n_layouts; i++) {
struct mp_chmap chmap = {0};
if (ca_layout_to_mp_chmap(ao, &layouts[i], &chmap))
mp_chmap_sel_add_map(&chmap_sel, &chmap);
}
talloc_free(layouts);
if (ao->channels.num < 3) {
struct mp_chmap chmap;
mp_chmap_from_channels(&chmap, ao->channels.num);
mp_chmap_sel_add_map(&chmap_sel, &chmap);
}
if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
MP_ERR(ao, "could not select a suitable channel map among the "
"hardware supported ones. Make sure to configure your "
"output device correctly in 'Audio MIDI Setup.app'\n");
goto coreaudio_error;
}
return true;
coreaudio_error:
return false;
}
static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd)
{
OSStatus err;
uint32_t size;
@@ -386,7 +237,6 @@ static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd)
err = AudioComponentInstanceNew(comp, &(p->audio_unit));
CHECK_CA_ERROR("unable to open audio component");
// Initialize AudioUnit
err = AudioUnitInitialize(p->audio_unit);
CHECK_CA_ERROR_L(coreaudio_error_component,
"unable to initialize audio unit");
@@ -399,7 +249,6 @@ static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd)
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"unable to set the input format on the audio unit");
//Set the Current Device to the Default Output Unit.
err = AudioUnitSetProperty(p->audio_unit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &p->device,
@@ -407,8 +256,7 @@ static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd)
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"can't link audio unit to selected device");
p->buffer = mp_ring_new(p, get_ring_size(ao));
print_buffer(ao, p->buffer);
p->hw_latency_us = ca_get_hardware_latency(ao);
AURenderCallbackStruct render_cb = (AURenderCallbackStruct) {
.inputProc = render_cb_lpcm,
@@ -423,279 +271,174 @@ static int init_lpcm(struct ao *ao, AudioStreamBasicDescription asbd)
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"unable to set render callback on audio unit");
reset(ao);
return CONTROL_OK;
return true;
coreaudio_error_audiounit:
AudioUnitUninitialize(p->audio_unit);
coreaudio_error_component:
AudioComponentInstanceDispose(p->audio_unit);
coreaudio_error:
return CONTROL_ERROR;
return false;
}
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
static void stop(struct ao *ao)
{
struct priv *p = ao->priv;
struct priv_d *d = p->digital;
OSStatus err = noErr;
uint32_t is_alive = 1;
err = CA_GET(p->device, kAudioDevicePropertyDeviceIsAlive, &is_alive);
CHECK_CA_WARN("could not check whether device is alive");
if (!is_alive)
MP_WARN(ao , "device is not alive\n");
p->is_digital = 1;
err = ca_lock_device(p->device, &d->hog_pid);
CHECK_CA_WARN("failed to set hogmode");
err = ca_disable_mixing(ao, p->device, &d->changed_mixing);
CHECK_CA_WARN("failed to disable mixing");
AudioStreamID *streams;
size_t n_streams;
/* Get a list of all the streams on this device. */
err = CA_GET_ARY_O(p->device, kAudioDevicePropertyStreams,
&streams, &n_streams);
CHECK_CA_ERROR("could not get number of streams");
for (int i = 0; i < n_streams && d->stream_idx < 0; i++) {
bool digital = ca_stream_supports_digital(ao, streams[i]);
if (digital) {
err = CA_GET(streams[i], kAudioStreamPropertyPhysicalFormat,
&d->original_asbd);
if (!CHECK_CA_WARN("could not get stream's physical format to "
"revert to, getting the next one"))
continue;
AudioStreamRangedDescription *formats;
size_t n_formats;
err = CA_GET_ARY(streams[i],
kAudioStreamPropertyAvailablePhysicalFormats,
&formats, &n_formats);
if (!CHECK_CA_WARN("could not get number of stream formats"))
continue; // try next one
int req_rate_format = -1;
int max_rate_format = -1;
d->stream = streams[i];
d->stream_idx = i;
for (int j = 0; j < n_formats; j++)
if (ca_format_is_digital(formats[j].mFormat)) {
// select the digital format that has exactly the same
// samplerate. If an exact match cannot be found, select
// the format with highest samplerate as backup.
if (formats[j].mFormat.mSampleRate == asbd.mSampleRate) {
req_rate_format = j;
break;
} else if (max_rate_format < 0 ||
formats[j].mFormat.mSampleRate >
formats[max_rate_format].mFormat.mSampleRate)
max_rate_format = j;
}
if (req_rate_format >= 0)
d->stream_asbd = formats[req_rate_format].mFormat;
else
d->stream_asbd = formats[max_rate_format].mFormat;
talloc_free(formats);
}
}
talloc_free(streams);
if (d->stream_idx < 0) {
MP_WARN(ao , "can't find any digital output stream format\n");
goto coreaudio_error;
}
if (!ca_change_format(ao, d->stream, d->stream_asbd))
goto coreaudio_error;
void *changed = (void *) &(d->stream_asbd_changed);
err = ca_enable_device_listener(p->device, changed);
CHECK_CA_ERROR("cannot install format change listener during init");
#if BYTE_ORDER == BIG_ENDIAN
if (!(p->stream_asdb.mFormatFlags & kAudioFormatFlagIsBigEndian))
#else
/* tell mplayer that we need a byteswap on AC3 streams, */
if (d->stream_asbd.mFormatID & kAudioFormat60958AC3)
ao->format = AF_FORMAT_AC3_LE;
else if (d->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
#endif
MP_WARN(ao, "stream has non-native byte order, output may fail\n");
ao->samplerate = d->stream_asbd.mSampleRate;
ao->bps = ao->samplerate *
(d->stream_asbd.mBytesPerPacket /
d->stream_asbd.mFramesPerPacket);
p->buffer = mp_ring_new(p, get_ring_size(ao));
print_buffer(ao, p->buffer);
err = AudioDeviceCreateIOProcID(p->device,
(AudioDeviceIOProc)render_cb_digital,
(void *)ao,
&d->render_cb);
CHECK_CA_ERROR("failed to register digital render callback");
reset(ao);
return CONTROL_TRUE;
coreaudio_error:
err = ca_unlock_device(p->device, &d->hog_pid);
CHECK_CA_WARN("can't release hog mode");
return CONTROL_ERROR;
OSStatus err = AudioOutputUnitStop(p->audio_unit);
CHECK_CA_WARN("can't stop audio unit");
}
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
struct priv_d *d = p->digital;
void *output_samples = data[0];
int num_bytes = samples * ao->sstride;
// Check whether we need to reset the digital output stream.
if (p->is_digital && d->stream_asbd_changed) {
d->stream_asbd_changed = 0;
if (ca_stream_supports_digital(ao, d->stream)) {
if (!ca_change_format(ao, d->stream, d->stream_asbd)) {
MP_WARN(ao , "can't restore digital output\n");
} else {
MP_WARN(ao, "restoring digital output succeeded.\n");
reset(ao);
}
}
}
int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
audio_resume(ao);
return wrote / ao->sstride;
}
static void reset(struct ao *ao)
static void start(struct ao *ao)
{
struct priv *p = ao->priv;
audio_pause(ao);
mp_ring_reset(p->buffer);
OSStatus err = AudioOutputUnitStart(p->audio_unit);
CHECK_CA_WARN("can't start audio unit");
}
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->buffer) / ao->sstride;
}
static float get_delay(struct ao *ao)
{
// FIXME: should also report the delay of coreaudio itself (hardware +
// internal buffers)
struct priv *p = ao->priv;
return mp_ring_buffered(p->buffer) / (float)ao->bps;
}
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
OSStatus err = noErr;
AudioOutputUnitStop(p->audio_unit);
AudioUnitUninitialize(p->audio_unit);
AudioComponentInstanceDispose(p->audio_unit);
}
if (!p->is_digital) {
AudioOutputUnitStop(p->audio_unit);
AudioUnitUninitialize(p->audio_unit);
AudioComponentInstanceDispose(p->audio_unit);
} else {
struct priv_d *d = p->digital;
// Channel Mapping functions
static const int speaker_map[][2] = {
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
{ kAudioChannelLabel_Right, MP_SPEAKER_ID_FR },
{ kAudioChannelLabel_Center, MP_SPEAKER_ID_FC },
{ kAudioChannelLabel_LFEScreen, MP_SPEAKER_ID_LFE },
{ kAudioChannelLabel_LeftSurround, MP_SPEAKER_ID_BL },
{ kAudioChannelLabel_RightSurround, MP_SPEAKER_ID_BR },
{ kAudioChannelLabel_LeftCenter, MP_SPEAKER_ID_FLC },
{ kAudioChannelLabel_RightCenter, MP_SPEAKER_ID_FRC },
{ kAudioChannelLabel_CenterSurround, MP_SPEAKER_ID_BC },
{ kAudioChannelLabel_LeftSurroundDirect, MP_SPEAKER_ID_SL },
{ kAudioChannelLabel_RightSurroundDirect, MP_SPEAKER_ID_SR },
{ kAudioChannelLabel_TopCenterSurround, MP_SPEAKER_ID_TC },
{ kAudioChannelLabel_VerticalHeightLeft, MP_SPEAKER_ID_TFL },
{ kAudioChannelLabel_VerticalHeightCenter, MP_SPEAKER_ID_TFC },
{ kAudioChannelLabel_VerticalHeightRight, MP_SPEAKER_ID_TFR },
{ kAudioChannelLabel_TopBackLeft, MP_SPEAKER_ID_TBL },
{ kAudioChannelLabel_TopBackCenter, MP_SPEAKER_ID_TBC },
{ kAudioChannelLabel_TopBackRight, MP_SPEAKER_ID_TBR },
void *changed = (void *) &(d->stream_asbd_changed);
err = ca_disable_device_listener(p->device, changed);
CHECK_CA_WARN("can't remove device listener, this may cause a crash");
// unofficial extensions
{ kAudioChannelLabel_RearSurroundLeft, MP_SPEAKER_ID_SDL },
{ kAudioChannelLabel_RearSurroundRight, MP_SPEAKER_ID_SDR },
{ kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
{ kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
{ kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
err = AudioDeviceStop(p->device, d->render_cb);
CHECK_CA_WARN("failed to stop audio device");
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
err = AudioDeviceDestroyIOProcID(p->device, d->render_cb);
CHECK_CA_WARN("failed to remove device render callback");
{ kAudioChannelLabel_Unknown, -1 },
};
if (!ca_change_format(ao, d->stream, d->original_asbd))
MP_WARN(ao, "can't revert to original device format");
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
{
for (int i = 0; speaker_map[i][1] >= 0; i++)
if (speaker_map[i][0] == label)
return speaker_map[i][1];
return -1;
}
err = ca_enable_mixing(ao, p->device, d->changed_mixing);
CHECK_CA_WARN("can't re-enable mixing");
static void ca_log_layout(struct ao *ao, AudioChannelLayout *layout)
{
if (!mp_msg_test(ao->log, MSGL_V))
return;
err = ca_unlock_device(p->device, &d->hog_pid);
CHECK_CA_WARN("can't release hog mode");
AudioChannelDescription *descs = layout->mChannelDescriptions;
MP_VERBOSE(ao, "layout: tag: <%d>, bitmap: <%d>, "
"descriptions <%d>\n",
layout->mChannelLayoutTag,
layout->mChannelBitmap,
layout->mNumberChannelDescriptions);
for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
AudioChannelDescription d = descs[i];
MP_VERBOSE(ao, " - description %d: label <%d, %d>, flags: <%u>, "
"coords: <%f, %f, %f>\n", i,
d.mChannelLabel,
ca_label_to_mp_speaker_id(d.mChannelLabel),
d.mChannelFlags,
d.mCoordinates[0],
d.mCoordinates[1],
d.mCoordinates[2]);
}
}
static void audio_pause(struct ao *ao)
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap)
{
struct priv *p = ao->priv;
OSErr err = noErr;
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
uint32_t layout_size = sizeof(layout);
OSStatus err;
if (p->paused)
return;
if (!p->is_digital) {
err = AudioOutputUnitStop(p->audio_unit);
CHECK_CA_WARN("can't stop audio unit");
} else {
struct priv_d *d = p->digital;
err = AudioDeviceStop(p->device, d->render_cb);
CHECK_CA_WARN("can't stop digital device");
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t),
&layout->mChannelBitmap,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions");
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag),
&layout->mChannelLayoutTag,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel tag to descriptions");
}
p->paused = true;
}
ca_log_layout(ao, layout);
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
OSErr err = noErr;
// If the channel layout uses channel descriptions, from my
// experiments there are there three possibile cases:
// * The description has a label kAudioChannelLabel_Unknown:
// Can't do anything about this (looks like non surround
// layouts are like this).
// * The description uses positional information: this in
// theory could be used but one would have to map spatial
// positions to labels which is not really feasible.
// * The description has a well known label which can be mapped
// to the waveextensible definition: this is the kind of
// descriptions we process here.
if (!p->paused)
return;
if (!p->is_digital) {
err = AudioOutputUnitStart(p->audio_unit);
CHECK_CA_WARN("can't start audio unit");
} else {
struct priv_d *d = p->digital;
err = AudioDeviceStart(p->device, d->render_cb);
CHECK_CA_WARN("can't start digital device");
for (int n = 0; n < layout->mNumberChannelDescriptions; n++) {
AudioChannelLabel label = layout->mChannelDescriptions[n].mChannelLabel;
uint8_t speaker = ca_label_to_mp_speaker_id(label);
if (label == kAudioChannelLabel_Unknown)
continue;
if (speaker < 0) {
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
"bitmap, skipping layout\n", label);
} else {
chmap->speaker[n] = speaker;
chmap->num = n + 1;
}
}
p->paused = false;
return chmap->num > 0;
coreaudio_error:
ca_log_layout(ao, layout);
return false;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio = {
.description = "CoreAudio (OS X Audio Output)",
.description = "CoreAudio AudioUnit",
.name = "coreaudio",
.uninit = uninit,
.init = init,
.play = play,
.control = control,
.get_space = get_space,
.get_delay = get_delay,
.reset = reset,
.pause = audio_pause,
.resume = audio_resume,
.pause = stop,
.resume = start,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_INT("device_id", opt_device_id, 0, OPTDEF_INT(-1)),
+683
View File
@@ -0,0 +1,683 @@
/*
* CoreAudio audio output driver for Mac OS X
*
* original copyright (C) Timothy J. Wood - Aug 2000
* ported to MPlayer libao2 by Dan Christiansen
*
* Chris Roccati
* Stefano Pigozzi
*
* The S/PDIF part of the code is based on the auhal audio output
* module from VideoLAN:
* Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org>
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* along with MPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* The MacOS X CoreAudio framework doesn't mesh as simply as some
* simpler frameworks do. This is due to the fact that CoreAudio pulls
* audio samples rather than having them pushed at it (which is nice
* when you are wanting to do good buffering of audio).
*/
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "misc/ring.h"
#include "common/msg.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
static void audio_pause(struct ao *ao);
static void audio_resume(struct ao *ao);
static void reset(struct ao *ao);
static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
{
switch (asbd.mFormatID)
case 'IAC3':
case 'iac3':
case kAudioFormat60958AC3:
case kAudioFormatAC3:
return true;
return false;
}
static bool ca_stream_supports_digital(struct ao *ao, AudioStreamID stream)
{
AudioStreamRangedDescription *formats = NULL;
size_t n_formats;
OSStatus err =
CA_GET_ARY(stream, kAudioStreamPropertyAvailablePhysicalFormats,
&formats, &n_formats);
CHECK_CA_ERROR("Could not get number of stream formats.");
for (int i = 0; i < n_formats; i++) {
AudioStreamBasicDescription asbd = formats[i].mFormat;
ca_print_asbd(ao, "supported format:", &(asbd));
if (ca_format_is_digital(asbd)) {
talloc_free(formats);
return true;
}
}
talloc_free(formats);
coreaudio_error:
return false;
}
static bool ca_device_supports_digital(struct ao *ao, AudioDeviceID device)
{
AudioStreamID *streams = NULL;
size_t n_streams;
/* Retrieve all the output streams. */
OSStatus err =
CA_GET_ARY_O(device, kAudioDevicePropertyStreams, &streams, &n_streams);
CHECK_CA_ERROR("could not get number of streams.");
for (int i = 0; i < n_streams; i++) {
if (ca_stream_supports_digital(ao, streams[i])) {
talloc_free(streams);
return true;
}
}
talloc_free(streams);
coreaudio_error:
return false;
}
static OSStatus ca_property_listener(
AudioObjectPropertySelector selector,
AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
void *talloc_ctx = talloc_new(NULL);
for (int i = 0; i < n_addresses; i++) {
if (addresses[i].mSelector == selector) {
if (data) *(volatile int *)data = 1;
break;
}
}
talloc_free(talloc_ctx);
return noErr;
}
static OSStatus ca_stream_listener(
AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
return ca_property_listener(kAudioStreamPropertyPhysicalFormat,
object, n_addresses, addresses, data);
}
static OSStatus ca_device_listener(
AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
return ca_property_listener(kAudioDevicePropertyDeviceHasChanged,
object, n_addresses, addresses, data);
}
static OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid) {
*pid = getpid();
OSStatus err = CA_SET(device, kAudioDevicePropertyHogMode, pid);
if (err != noErr)
*pid = -1;
return err;
}
static OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid) {
if (*pid == getpid()) {
*pid = -1;
return CA_SET(device, kAudioDevicePropertyHogMode, &pid);
}
return noErr;
}
static OSStatus ca_change_mixing(struct ao *ao, AudioDeviceID device,
uint32_t val, bool *changed) {
*changed = false;
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertySupportsMixing,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
if (AudioObjectHasProperty(device, &p_addr)) {
OSStatus err;
Boolean writeable = 0;
err = CA_SETTABLE(device, kAudioDevicePropertySupportsMixing,
&writeable);
if (!CHECK_CA_WARN("can't tell if mixing property is settable")) {
return err;
}
if (!writeable)
return noErr;
err = CA_SET(device, kAudioDevicePropertySupportsMixing, &val);
if (err != noErr)
return err;
if (!CHECK_CA_WARN("can't set mix mode")) {
return err;
}
*changed = true;
}
return noErr;
}
static OSStatus ca_disable_mixing(struct ao *ao,
AudioDeviceID device, bool *changed) {
return ca_change_mixing(ao, device, 0, changed);
}
static OSStatus ca_enable_mixing(struct ao *ao,
AudioDeviceID device, bool changed) {
if (changed) {
bool dont_care = false;
return ca_change_mixing(ao, device, 1, &dont_care);
}
return noErr;
}
static OSStatus ca_change_device_listening(AudioDeviceID device,
void *flag, bool enabled)
{
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertyDeviceHasChanged,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
if (enabled) {
return AudioObjectAddPropertyListener(
device, &p_addr, ca_device_listener, flag);
} else {
return AudioObjectRemovePropertyListener(
device, &p_addr, ca_device_listener, flag);
}
}
static OSStatus ca_enable_device_listener(AudioDeviceID device, void *flag) {
return ca_change_device_listening(device, flag, true);
}
static OSStatus ca_disable_device_listener(AudioDeviceID device, void *flag) {
return ca_change_device_listening(device, flag, false);
}
static bool ca_change_format(struct ao *ao, AudioStreamID stream,
AudioStreamBasicDescription change_format)
{
OSStatus err = noErr;
AudioObjectPropertyAddress p_addr;
volatile int stream_format_changed = 0;
ca_print_asbd(ao, "setting stream format:", &change_format);
/* Install the callback. */
p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioStreamPropertyPhysicalFormat,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
err = AudioObjectAddPropertyListener(stream, &p_addr, ca_stream_listener,
(void *)&stream_format_changed);
if (!CHECK_CA_WARN("can't add property listener during format change")) {
return false;
}
/* Change the format. */
err = CA_SET(stream, kAudioStreamPropertyPhysicalFormat, &change_format);
if (!CHECK_CA_WARN("error changing physical format")) {
return false;
}
/* The AudioStreamSetProperty is not only asynchronious,
* it is also not Atomic, in its behaviour.
* Therefore we check 5 times before we really give up. */
bool format_set = false;
for (int i = 0; !format_set && i < 5; i++) {
for (int j = 0; !stream_format_changed && j < 50; j++)
mp_sleep_us(10000);
if (stream_format_changed) {
stream_format_changed = 0;
} else {
MP_VERBOSE(ao, "reached timeout\n");
}
AudioStreamBasicDescription actual_format;
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
ca_print_asbd(ao, "actual format in use:", &actual_format);
if (actual_format.mSampleRate == change_format.mSampleRate &&
actual_format.mFormatID == change_format.mFormatID &&
actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
format_set = true;
}
}
err = AudioObjectRemovePropertyListener(stream, &p_addr, ca_stream_listener,
(void *)&stream_format_changed);
if (!CHECK_CA_WARN("can't remove property listener")) {
return false;
}
return format_set;
}
struct priv {
AudioDeviceID device; // selected device
bool paused;
struct mp_ring *buffer;
// digital render callback
AudioDeviceIOProcID render_cb;
// pid set for hog mode, (-1) means that hog mode on the device was
// released. hog mode is exclusive access to a device
pid_t hog_pid;
// stream selected for digital playback by the detection in init
AudioStreamID stream;
// stream index in an AudioBufferList
int stream_idx;
// format we changed the stream to: for the digital case each application
// sets the stream format for a device to what it needs
AudioStreamBasicDescription stream_asbd;
AudioStreamBasicDescription original_asbd;
bool changed_mixing;
int stream_asbd_changed;
bool muted;
// options
int opt_device_id;
int opt_list;
};
static int get_ring_size(struct ao *ao)
{
return af_fmt_seconds_to_bytes(
ao->format, 0.5, ao->channels.num, ao->samplerate);
}
static OSStatus render_cb_digital(
AudioDeviceID device, const AudioTimeStamp *ts,
const void *in_data, const AudioTimeStamp *in_ts,
AudioBufferList *out_data, const AudioTimeStamp *out_ts, void *ctx)
{
struct ao *ao = ctx;
struct priv *p = ao->priv;
AudioBuffer buf = out_data->mBuffers[p->stream_idx];
int requested = buf.mDataByteSize;
if (p->muted)
mp_ring_drain(p->buffer, requested);
else
mp_ring_read(p->buffer, buf.mData, requested);
return noErr;
}
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
ao_control_vol_t *control_vol;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
control_vol = (ao_control_vol_t *)arg;
// Digital output has no volume adjust.
int digitalvol = p->muted ? 0 : 100;
*control_vol = (ao_control_vol_t) {
.left = digitalvol, .right = digitalvol,
};
return CONTROL_TRUE;
case AOCONTROL_SET_VOLUME:
control_vol = (ao_control_vol_t *)arg;
// Digital output can not set volume. Here we have to return true
// to make mixer forget it. Else mixer will add a soft filter,
// that's not we expected and the filter not support ac3 stream
// will cause mplayer die.
// Although not support set volume, but at least we support mute.
// MPlayer set mute by set volume to zero, we handle it.
if (control_vol->left == 0 && control_vol->right == 0)
p->muted = true;
else
p->muted = false;
return CONTROL_TRUE;
} // end switch
return CONTROL_UNKNOWN;
}
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd);
static int init(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->opt_list) ca_print_device_list(ao);
OSStatus err = ca_select_device(ao, p->opt_device_id, &p->device);
CHECK_CA_ERROR("failed to select device");
ao->format = af_fmt_from_planar(ao->format);
bool supports_digital = false;
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
if (AF_FORMAT_IS_AC3(ao->format)) {
if (ca_device_supports_digital(ao, p->device))
supports_digital = true;
}
if (!supports_digital) {
MP_ERR(ao, "selected device doesn't support digital formats\n");
goto coreaudio_error;
} // closes if (!supports_digital)
// Build ASBD for the input format
AudioStreamBasicDescription asbd;
ca_fill_asbd(ao, &asbd);
return init_digital(ao, asbd);
coreaudio_error:
return CONTROL_ERROR;
}
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
{
struct priv *p = ao->priv;
OSStatus err = noErr;
uint32_t is_alive = 1;
err = CA_GET(p->device, kAudioDevicePropertyDeviceIsAlive, &is_alive);
CHECK_CA_WARN("could not check whether device is alive");
if (!is_alive)
MP_WARN(ao , "device is not alive\n");
err = ca_lock_device(p->device, &p->hog_pid);
CHECK_CA_WARN("failed to set hogmode");
err = ca_disable_mixing(ao, p->device, &p->changed_mixing);
CHECK_CA_WARN("failed to disable mixing");
AudioStreamID *streams;
size_t n_streams;
/* Get a list of all the streams on this device. */
err = CA_GET_ARY_O(p->device, kAudioDevicePropertyStreams,
&streams, &n_streams);
CHECK_CA_ERROR("could not get number of streams");
for (int i = 0; i < n_streams && p->stream_idx < 0; i++) {
bool digital = ca_stream_supports_digital(ao, streams[i]);
if (digital) {
err = CA_GET(streams[i], kAudioStreamPropertyPhysicalFormat,
&p->original_asbd);
if (!CHECK_CA_WARN("could not get stream's physical format to "
"revert to, getting the next one"))
continue;
AudioStreamRangedDescription *formats;
size_t n_formats;
err = CA_GET_ARY(streams[i],
kAudioStreamPropertyAvailablePhysicalFormats,
&formats, &n_formats);
if (!CHECK_CA_WARN("could not get number of stream formats"))
continue; // try next one
int req_rate_format = -1;
int max_rate_format = -1;
p->stream = streams[i];
p->stream_idx = i;
for (int j = 0; j < n_formats; j++)
if (ca_format_is_digital(formats[j].mFormat)) {
// select the digital format that has exactly the same
// samplerate. If an exact match cannot be found, select
// the format with highest samplerate as backup.
if (formats[j].mFormat.mSampleRate == asbd.mSampleRate) {
req_rate_format = j;
break;
} else if (max_rate_format < 0 ||
formats[j].mFormat.mSampleRate >
formats[max_rate_format].mFormat.mSampleRate)
max_rate_format = j;
}
if (req_rate_format >= 0)
p->stream_asbd = formats[req_rate_format].mFormat;
else
p->stream_asbd = formats[max_rate_format].mFormat;
talloc_free(formats);
}
}
talloc_free(streams);
if (p->stream_idx < 0) {
MP_WARN(ao , "can't find any digital output stream format\n");
goto coreaudio_error;
}
if (!ca_change_format(ao, p->stream, p->stream_asbd))
goto coreaudio_error;
void *changed = (void *) &(p->stream_asbd_changed);
err = ca_enable_device_listener(p->device, changed);
CHECK_CA_ERROR("cannot install format change listener during init");
#if BYTE_ORDER == BIG_ENDIAN
if (!(p->stream_asdb.mFormatFlags & kAudioFormatFlagIsBigEndian))
#else
/* tell mplayer that we need a byteswap on AC3 streams, */
if (p->stream_asbd.mFormatID & kAudioFormat60958AC3)
ao->format = AF_FORMAT_AC3_LE;
else if (p->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
#endif
MP_WARN(ao, "stream has non-native byte order, output may fail\n");
ao->samplerate = p->stream_asbd.mSampleRate;
ao->bps = ao->samplerate *
(p->stream_asbd.mBytesPerPacket /
p->stream_asbd.mFramesPerPacket);
p->buffer = mp_ring_new(p, get_ring_size(ao));
err = AudioDeviceCreateIOProcID(p->device,
(AudioDeviceIOProc)render_cb_digital,
(void *)ao,
&p->render_cb);
CHECK_CA_ERROR("failed to register digital render callback");
reset(ao);
return CONTROL_TRUE;
coreaudio_error:
err = ca_unlock_device(p->device, &p->hog_pid);
CHECK_CA_WARN("can't release hog mode");
return CONTROL_ERROR;
}
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
void *output_samples = data[0];
int num_bytes = samples * ao->sstride;
// Check whether we need to reset the digital output stream.
if (p->stream_asbd_changed) {
p->stream_asbd_changed = 0;
if (ca_stream_supports_digital(ao, p->stream)) {
if (!ca_change_format(ao, p->stream, p->stream_asbd)) {
MP_WARN(ao , "can't restore digital output\n");
} else {
MP_WARN(ao, "restoring digital output succeeded.\n");
reset(ao);
}
}
}
int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
audio_resume(ao);
return wrote / ao->sstride;
}
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
audio_pause(ao);
mp_ring_reset(p->buffer);
}
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->buffer) / ao->sstride;
}
static float get_delay(struct ao *ao)
{
// FIXME: should also report the delay of coreaudio itself (hardware +
// internal buffers)
struct priv *p = ao->priv;
return mp_ring_buffered(p->buffer) / (float)ao->bps;
}
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
OSStatus err = noErr;
void *changed = (void *) &(p->stream_asbd_changed);
err = ca_disable_device_listener(p->device, changed);
CHECK_CA_WARN("can't remove device listener, this may cause a crash");
err = AudioDeviceStop(p->device, p->render_cb);
CHECK_CA_WARN("failed to stop audio device");
err = AudioDeviceDestroyIOProcID(p->device, p->render_cb);
CHECK_CA_WARN("failed to remove device render callback");
if (!ca_change_format(ao, p->stream, p->original_asbd))
MP_WARN(ao, "can't revert to original device format");
err = ca_enable_mixing(ao, p->device, p->changed_mixing);
CHECK_CA_WARN("can't re-enable mixing");
err = ca_unlock_device(p->device, &p->hog_pid);
CHECK_CA_WARN("can't release hog mode");
}
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->paused)
return;
OSStatus err = AudioDeviceStop(p->device, p->render_cb);
CHECK_CA_WARN("can't stop digital device");
p->paused = true;
}
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
if (!p->paused)
return;
OSStatus err = AudioDeviceStart(p->device, p->render_cb);
CHECK_CA_WARN("can't start digital device");
p->paused = false;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio_exclusive = {
.description = "CoreAudio Exclusive Mode",
.name = "coreaudio_exclusive",
.uninit = uninit,
.init = init,
.play = play,
.control = control,
.get_space = get_space,
.get_delay = get_delay,
.reset = reset,
.pause = audio_pause,
.resume = audio_resume,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv){
.muted = false,
.stream_asbd_changed = 0,
.hog_pid = -1,
.stream = 0,
.stream_idx = -1,
.changed_mixing = false,
},
.options = (const struct m_option[]) {
OPT_INT("device_id", opt_device_id, 0, OPTDEF_INT(-1)),
OPT_FLAG("list", opt_list, 0),
{0}
},
};
+1
View File
@@ -37,6 +37,7 @@ OSStatus ca_set(AudioObjectID id, ca_scope scope, ca_sel selector,
#define CA_GET(id, sel, data) ca_get(id, CA_GLOBAL, sel, sizeof(*(data)), data)
#define CA_SET(id, sel, data) ca_set(id, CA_GLOBAL, sel, sizeof(*(data)), data)
#define CA_GET_O(id, sel, data) ca_get(id, CA_OUTPUT, sel, sizeof(*(data)), data)
OSStatus ca_get_ary(AudioObjectID id, ca_scope scope, ca_sel selector,
uint32_t element_size, void **data, size_t *elements);
+90 -374
View File
@@ -25,12 +25,75 @@
#include "audio/out/ao_coreaudio_utils.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "osdep/timer.h"
#include "audio/format.h"
void ca_print_device_list(struct ao *ao)
{
char *help = talloc_strdup(NULL, "Available output devices:\n");
AudioDeviceID *devs;
size_t n_devs;
OSStatus err =
CA_GET_ARY(kAudioObjectSystemObject, kAudioHardwarePropertyDevices,
&devs, &n_devs);
CHECK_CA_ERROR("Failed to get list of output devices.");
for (int i = 0; i < n_devs; i++) {
char *name;
err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
if (err == noErr)
talloc_steal(devs, name);
else
name = "Unknown";
help = talloc_asprintf_append(
help, " * %s (id: %" PRIu32 ")\n", name, devs[i]);
}
talloc_free(devs);
coreaudio_error:
MP_INFO(ao, "%s", help);
talloc_free(help);
}
OSStatus ca_select_device(struct ao *ao, int selection, AudioDeviceID *device)
{
OSStatus err = noErr;
*device = 0;
if (selection < 0) {
// device not set by user, get the default one
err = CA_GET(kAudioObjectSystemObject,
kAudioHardwarePropertyDefaultOutputDevice,
device);
CHECK_CA_ERROR("could not get default audio device");
} else {
*device = selection;
}
if (mp_msg_test(ao->log, MSGL_V)) {
char *name;
err = CA_GET_STR(*device, kAudioObjectPropertyName, &name);
CHECK_CA_ERROR("could not get selected audio device name");
MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n",
name, *device);
talloc_free(name);
}
coreaudio_error:
return err;
}
char *fourcc_repr(void *talloc_ctx, uint32_t code)
{
// Extract FourCC letters from the uint32_t and finde out if it's a valid
// code that is made of letters.
char fcc[4] = {
unsigned char fcc[4] = {
(code >> 24) & 0xFF,
(code >> 16) & 0xFF,
(code >> 8) & 0xFF,
@@ -39,7 +102,7 @@ char *fourcc_repr(void *talloc_ctx, uint32_t code)
bool valid_fourcc = true;
for (int i = 0; i < 4; i++)
if (!isprint(fcc[i]))
if (fcc[i] >= 32 && fcc[i] < 128)
valid_fourcc = false;
char *repr;
@@ -63,6 +126,31 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
return false;
}
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
{
asbd->mSampleRate = ao->samplerate;
asbd->mFormatID = AF_FORMAT_IS_AC3(ao->format) ?
kAudioFormat60958AC3 :
kAudioFormatLinearPCM;
asbd->mChannelsPerFrame = ao->channels.num;
asbd->mBitsPerChannel = af_fmt2bits(ao->format);
asbd->mFormatFlags = kAudioFormatFlagIsPacked;
if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F)
asbd->mFormatFlags |= kAudioFormatFlagIsFloat;
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
asbd->mFormatFlags |= kAudioFormatFlagIsBigEndian;
asbd->mFramesPerPacket = 1;
asbd->mBytesPerPacket = asbd->mBytesPerFrame =
asbd->mFramesPerPacket * asbd->mChannelsPerFrame *
(asbd->mBitsPerChannel / 8);
}
void ca_print_asbd(struct ao *ao, const char *description,
const AudioStreamBasicDescription *asbd)
{
@@ -87,375 +175,3 @@ void ca_print_asbd(struct ao *ao, const char *description,
talloc_free(format);
}
bool ca_format_is_digital(AudioStreamBasicDescription asbd)
{
switch (asbd.mFormatID)
case 'IAC3':
case 'iac3':
case kAudioFormat60958AC3:
case kAudioFormatAC3:
return true;
return false;
}
bool ca_stream_supports_digital(struct ao *ao, AudioStreamID stream)
{
AudioStreamRangedDescription *formats = NULL;
size_t n_formats;
OSStatus err =
CA_GET_ARY(stream, kAudioStreamPropertyAvailablePhysicalFormats,
&formats, &n_formats);
CHECK_CA_ERROR("Could not get number of stream formats.");
for (int i = 0; i < n_formats; i++) {
AudioStreamBasicDescription asbd = formats[i].mFormat;
ca_print_asbd(ao, "supported format:", &(asbd));
if (ca_format_is_digital(asbd)) {
talloc_free(formats);
return true;
}
}
talloc_free(formats);
coreaudio_error:
return false;
}
bool ca_device_supports_digital(struct ao *ao, AudioDeviceID device)
{
AudioStreamID *streams = NULL;
size_t n_streams;
/* Retrieve all the output streams. */
OSStatus err =
CA_GET_ARY_O(device, kAudioDevicePropertyStreams, &streams, &n_streams);
CHECK_CA_ERROR("could not get number of streams.");
for (int i = 0; i < n_streams; i++) {
if (ca_stream_supports_digital(ao, streams[i])) {
talloc_free(streams);
return true;
}
}
talloc_free(streams);
coreaudio_error:
return false;
}
OSStatus ca_property_listener(AudioObjectPropertySelector selector,
AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
void *talloc_ctx = talloc_new(NULL);
for (int i = 0; i < n_addresses; i++) {
if (addresses[i].mSelector == selector) {
if (data) *(volatile int *)data = 1;
break;
}
}
talloc_free(talloc_ctx);
return noErr;
}
OSStatus ca_stream_listener(AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
return ca_property_listener(kAudioStreamPropertyPhysicalFormat,
object, n_addresses, addresses, data);
}
OSStatus ca_device_listener(AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data)
{
return ca_property_listener(kAudioDevicePropertyDeviceHasChanged,
object, n_addresses, addresses, data);
}
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid) {
*pid = getpid();
OSStatus err = CA_SET(device, kAudioDevicePropertyHogMode, pid);
if (err != noErr)
*pid = -1;
return err;
}
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid) {
if (*pid == getpid()) {
*pid = -1;
return CA_SET(device, kAudioDevicePropertyHogMode, &pid);
}
return noErr;
}
static OSStatus ca_change_mixing(struct ao *ao, AudioDeviceID device,
uint32_t val, bool *changed) {
*changed = false;
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertySupportsMixing,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
if (AudioObjectHasProperty(device, &p_addr)) {
OSStatus err;
Boolean writeable = 0;
err = CA_SETTABLE(device, kAudioDevicePropertySupportsMixing,
&writeable);
if (!CHECK_CA_WARN("can't tell if mixing property is settable")) {
return err;
}
if (!writeable)
return noErr;
err = CA_SET(device, kAudioDevicePropertySupportsMixing, &val);
if (err != noErr)
return err;
if (!CHECK_CA_WARN("can't set mix mode")) {
return err;
}
*changed = true;
}
return noErr;
}
OSStatus ca_disable_mixing(struct ao *ao, AudioDeviceID device, bool *changed) {
return ca_change_mixing(ao, device, 0, changed);
}
OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed) {
if (changed) {
bool dont_care = false;
return ca_change_mixing(ao, device, 1, &dont_care);
}
return noErr;
}
static OSStatus ca_change_device_listening(AudioDeviceID device,
void *flag, bool enabled)
{
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertyDeviceHasChanged,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
if (enabled) {
return AudioObjectAddPropertyListener(
device, &p_addr, ca_device_listener, flag);
} else {
return AudioObjectRemovePropertyListener(
device, &p_addr, ca_device_listener, flag);
}
}
OSStatus ca_enable_device_listener(AudioDeviceID device, void *flag) {
return ca_change_device_listening(device, flag, true);
}
OSStatus ca_disable_device_listener(AudioDeviceID device, void *flag) {
return ca_change_device_listening(device, flag, false);
}
bool ca_change_format(struct ao *ao, AudioStreamID stream,
AudioStreamBasicDescription change_format)
{
OSStatus err = noErr;
AudioObjectPropertyAddress p_addr;
volatile int stream_format_changed = 0;
ca_print_asbd(ao, "setting stream format:", &change_format);
/* Install the callback. */
p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioStreamPropertyPhysicalFormat,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster,
};
err = AudioObjectAddPropertyListener(stream, &p_addr, ca_stream_listener,
(void *)&stream_format_changed);
if (!CHECK_CA_WARN("can't add property listener during format change")) {
return false;
}
/* Change the format. */
err = CA_SET(stream, kAudioStreamPropertyPhysicalFormat, &change_format);
if (!CHECK_CA_WARN("error changing physical format")) {
return false;
}
/* The AudioStreamSetProperty is not only asynchronious,
* it is also not Atomic, in its behaviour.
* Therefore we check 5 times before we really give up. */
bool format_set = false;
for (int i = 0; !format_set && i < 5; i++) {
for (int j = 0; !stream_format_changed && j < 50; j++)
mp_sleep_us(10000);
if (stream_format_changed) {
stream_format_changed = 0;
} else {
MP_VERBOSE(ao, "reached timeout\n");
}
AudioStreamBasicDescription actual_format;
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
ca_print_asbd(ao, "actual format in use:", &actual_format);
if (actual_format.mSampleRate == change_format.mSampleRate &&
actual_format.mFormatID == change_format.mFormatID &&
actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
format_set = true;
}
}
err = AudioObjectRemovePropertyListener(stream, &p_addr, ca_stream_listener,
(void *)&stream_format_changed);
if (!CHECK_CA_WARN("can't remove property listener")) {
return false;
}
return format_set;
}
static const int speaker_map[][2] = {
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
{ kAudioChannelLabel_Right, MP_SPEAKER_ID_FR },
{ kAudioChannelLabel_Center, MP_SPEAKER_ID_FC },
{ kAudioChannelLabel_LFEScreen, MP_SPEAKER_ID_LFE },
{ kAudioChannelLabel_LeftSurround, MP_SPEAKER_ID_BL },
{ kAudioChannelLabel_RightSurround, MP_SPEAKER_ID_BR },
{ kAudioChannelLabel_LeftCenter, MP_SPEAKER_ID_FLC },
{ kAudioChannelLabel_RightCenter, MP_SPEAKER_ID_FRC },
{ kAudioChannelLabel_CenterSurround, MP_SPEAKER_ID_BC },
{ kAudioChannelLabel_LeftSurroundDirect, MP_SPEAKER_ID_SL },
{ kAudioChannelLabel_RightSurroundDirect, MP_SPEAKER_ID_SR },
{ kAudioChannelLabel_TopCenterSurround, MP_SPEAKER_ID_TC },
{ kAudioChannelLabel_VerticalHeightLeft, MP_SPEAKER_ID_TFL },
{ kAudioChannelLabel_VerticalHeightCenter, MP_SPEAKER_ID_TFC },
{ kAudioChannelLabel_VerticalHeightRight, MP_SPEAKER_ID_TFR },
{ kAudioChannelLabel_TopBackLeft, MP_SPEAKER_ID_TBL },
{ kAudioChannelLabel_TopBackCenter, MP_SPEAKER_ID_TBC },
{ kAudioChannelLabel_TopBackRight, MP_SPEAKER_ID_TBR },
// unofficial extensions
{ kAudioChannelLabel_RearSurroundLeft, MP_SPEAKER_ID_SDL },
{ kAudioChannelLabel_RearSurroundRight, MP_SPEAKER_ID_SDR },
{ kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
{ kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
{ kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
{ kAudioChannelLabel_Unknown, -1 },
};
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
{
for (int i = 0; speaker_map[i][1] >= 0; i++)
if (speaker_map[i][0] == label)
return speaker_map[i][1];
return -1;
}
static void ca_log_layout(struct ao *ao, AudioChannelLayout *layout)
{
if (!mp_msg_test(ao->log, MSGL_V))
return;
AudioChannelDescription *descs = layout->mChannelDescriptions;
MP_VERBOSE(ao, "layout: tag: <%d>, bitmap: <%d>, "
"descriptions <%d>\n",
layout->mChannelLayoutTag,
layout->mChannelBitmap,
layout->mNumberChannelDescriptions);
for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
AudioChannelDescription d = descs[i];
MP_VERBOSE(ao, " - description %d: label <%d, %d>, flags: <%u>, "
"coords: <%f, %f, %f>\n", i,
d.mChannelLabel,
ca_label_to_mp_speaker_id(d.mChannelLabel),
d.mChannelFlags,
d.mCoordinates[0],
d.mCoordinates[1],
d.mCoordinates[2]);
}
}
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap)
{
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
uint32_t layout_size = sizeof(layout);
OSStatus err;
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t),
&layout->mChannelBitmap,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions");
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag),
&layout->mChannelLayoutTag,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel tag to descriptions");
}
ca_log_layout(ao, layout);
// If the channel layout uses channel descriptions, from my
// experiments there are there three possibile cases:
// * The description has a label kAudioChannelLabel_Unknown:
// Can't do anything about this (looks like non surround
// layouts are like this).
// * The description uses positional information: this in
// theory could be used but one would have to map spatial
// positions to labels which is not really feasible.
// * The description has a well known label which can be mapped
// to the waveextensible definition: this is the kind of
// descriptions we process here.
for (int n = 0; n < layout->mNumberChannelDescriptions; n++) {
AudioChannelLabel label = layout->mChannelDescriptions[n].mChannelLabel;
uint8_t speaker = ca_label_to_mp_speaker_id(label);
if (label == kAudioChannelLabel_Unknown)
continue;
if (speaker < 0) {
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
"bitmap, skipping layout\n", label);
} else {
chmap->speaker[n] = speaker;
chmap->num = n + 1;
}
}
return chmap->num > 0;
coreaudio_error:
ca_log_layout(ao, layout);
return false;
}
+4 -31
View File
@@ -46,38 +46,11 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
if (err != noErr) goto label; \
} while (0)
void ca_print_device_list(struct ao *ao);
OSStatus ca_select_device(struct ao *ao, int selection, AudioDeviceID *device);
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd);
void ca_print_asbd(struct ao *ao, const char *description,
const AudioStreamBasicDescription *asbd);
bool ca_format_is_digital(AudioStreamBasicDescription asbd);
bool ca_stream_supports_digital(struct ao *ao, AudioStreamID stream);
bool ca_device_supports_digital(struct ao *ao, AudioDeviceID device);
OSStatus ca_property_listener(AudioObjectPropertySelector selector,
AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data);
OSStatus ca_stream_listener(AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data);
OSStatus ca_device_listener(AudioObjectID object, uint32_t n_addresses,
const AudioObjectPropertyAddress addresses[],
void *data);
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_disable_mixing(struct ao *ao, AudioDeviceID device, bool *changed);
OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed);
OSStatus ca_enable_device_listener(AudioDeviceID device, void *flag);
OSStatus ca_disable_device_listener(AudioDeviceID device, void *flag);
bool ca_change_format(struct ao *ao, AudioStreamID stream,
AudioStreamBasicDescription change_format);
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap);
#endif /* MPV_COREAUDIO_UTILS_H */
+1 -1
View File
@@ -403,7 +403,7 @@ static int init(struct ao *ao)
ao->samplerate = rate;
ao->format = format;
ao->bps = ao->channels.num * rate * af_fmt2bps(format);
int buffersize = ao->bps; // space for 1 sec
int buffersize = ao->bps * 0.100; // space for 100ms
MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate,
ao->channels.num, af_fmt_to_str(format));
MP_VERBOSE(ao, "Buffersize:%d bytes (%d msec)\n",
+12 -10
View File
@@ -71,7 +71,7 @@ static void select_format(struct ao *ao, AVCodec *codec)
{
int fmt = af_from_avformat(*sampleformat);
if (!fmt) {
MP_WARN(ao, "unsupported lavc format %s",
MP_WARN(ao, "unsupported lavc format %s\n",
av_get_sample_fmt_name(*sampleformat));
continue;
}
@@ -114,12 +114,11 @@ static int init(struct ao *ao)
codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream);
// ac->stream->time_base.num = 1;
// ac->stream->time_base.den = ao->samplerate;
// doing this breaks mpeg2ts in ffmpeg
// which doesn't properly force the time base to be 90000
// furthermore, ffmpeg.c doesn't do this either and works
// TODO: Remove this redundancy with encode_lavc_alloc_stream also
// setting the time base.
// Using codec->time_bvase is deprecated, but needed for older lavf.
ac->stream->time_base.num = 1;
ac->stream->time_base.den = ao->samplerate;
ac->stream->codec->time_base.num = 1;
ac->stream->codec->time_base.den = ao->samplerate;
@@ -342,8 +341,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
double pts = ectx->last_audio_in_pts;
pts += ectx->samples_since_last_pts / ao->samplerate;
ectx->samples_since_last_pts += samples;
pts += ectx->samples_since_last_pts / (double)ao->samplerate;
size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1;
@@ -451,6 +449,10 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
talloc_free(tempdata);
int taken = FFMIN(bufpos, orig_samples);
ectx->samples_since_last_pts += taken;
pthread_mutex_unlock(&ectx->lock);
if (flags & AOPLAY_FINAL_CHUNK) {
@@ -463,7 +465,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
}
return FFMIN(bufpos, orig_samples);
return taken;
}
static void drain(struct ao *ao)
+1 -2
View File
@@ -90,7 +90,7 @@ static int init(struct ao *ao)
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
mp_chmap_from_channels(&ao->channels, 2);
priv->latency = priv->latency_sec * ao->samplerate;
@@ -210,7 +210,6 @@ const struct ao_driver audio_out_null = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.bufferlen = 0.2,
.latency_sec = 0.5,
.outburst = 256,
.speed = 1,
},
+1
View File
@@ -33,6 +33,7 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include "config.h"
#include "options/options.h"
+42 -26
View File
@@ -53,7 +53,6 @@ struct priv {
// temporary during control()
struct pa_sink_input_info pi;
bool broken_pause;
int retval;
// for wakeup handling
@@ -64,6 +63,7 @@ struct priv {
char *cfg_host;
char *cfg_sink;
int cfg_buffer;
int cfg_latency_hacks;
};
#define GENERIC_ERR_MSG(str) \
@@ -113,7 +113,7 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
pa_threaded_mainloop_signal(priv->mainloop, 0);
}
static int wait(struct ao *ao, pthread_mutex_t *lock)
static int wait_audio(struct ao *ao, pthread_mutex_t *lock)
{
struct priv *priv = ao->priv;
// We don't use this mutex, because pulse like to call stream_request_cb
@@ -280,26 +280,12 @@ static int init(struct ao *ao)
struct priv *priv = ao->priv;
char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL;
char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL;
const char *version = pa_get_library_version();
pthread_mutex_init(&priv->wakeup_lock, NULL);
pthread_cond_init(&priv->wakeup, NULL);
ao->per_application_mixer = true;
priv->broken_pause = false;
/* not sure which versions are affected, assume 0.9.11* to 0.9.14*
* known bad: 0.9.14, 0.9.13
* known good: 0.9.9, 0.9.10, 0.9.15
* To test: pause, wait ca. 5 seconds, framestep and see if MPlayer
* hangs somewhen. */
if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1'
&& version[5] <= '4') {
MP_WARN(ao, "working around probably broken pause functionality,\n"
" see http://www.pulseaudio.org/ticket/440\n");
priv->broken_pause = true;
}
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
MP_ERR(ao, "Failed to allocate main loop\n");
goto fail;
@@ -357,6 +343,8 @@ static int init(struct ao *ao)
goto unlock_and_fail;
}
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ICON_NAME,
PULSE_CLIENT_NAME);
if (!(priv->stream = pa_stream_new_with_proplist(priv->context,
"audio stream", &ss,
@@ -378,8 +366,13 @@ static int init(struct ao *ao)
.minreq = -1,
.fragsize = -1,
};
int flags = PA_STREAM_NOT_MONOTONIC;
if (!priv->cfg_latency_hacks)
flags |= PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
PA_STREAM_NOT_MONOTONIC, NULL, NULL) < 0)
flags, NULL, NULL) < 0)
goto unlock_and_fail;
/* Wait until the stream is ready */
@@ -463,12 +456,6 @@ static void pause(struct ao *ao)
// Resume the audio stream by uncorking it on the server
static void resume(struct ao *ao)
{
struct priv *priv = ao->priv;
/* Without this, certain versions will cause an infinite hang because
* pa_stream_writable_size returns 0 always.
* Note that this workaround causes A-V desync after pause. */
if (priv->broken_pause)
reset(ao);
cork(ao, false);
}
@@ -482,8 +469,7 @@ static int get_space(struct ao *ao)
return space / ao->sstride;
}
// Return the current latency in seconds
static float get_delay(struct ao *ao)
static float get_delay_hackfixed(struct ao *ao)
{
/* This code basically does what pa_stream_get_latency() _should_
* do, but doesn't due to multiple known bugs in PulseAudio (at
@@ -538,6 +524,34 @@ static float get_delay(struct ao *ao)
return latency / 1e6;
}
static float get_delay_pulse(struct ao *ao)
{
struct priv *priv = ao->priv;
pa_usec_t latency = (pa_usec_t) -1;
pa_threaded_mainloop_lock(priv->mainloop);
while (pa_stream_get_latency(priv->stream, &latency, NULL) < 0) {
if (pa_context_errno(priv->context) != PA_ERR_NODATA) {
GENERIC_ERR_MSG("pa_stream_get_latency() failed");
break;
}
/* Wait until latency data is available again */
pa_threaded_mainloop_wait(priv->mainloop);
}
pa_threaded_mainloop_unlock(priv->mainloop);
return latency == (pa_usec_t) -1 ? 0 : latency / 1000000.0;
}
// Return the current latency in seconds
static float get_delay(struct ao *ao)
{
struct priv *priv = ao->priv;
if (priv->cfg_latency_hacks) {
return get_delay_hackfixed(ao);
} else {
return get_delay_pulse(ao);
}
}
/* A callback function that is called when the
* pa_context_get_sink_input_info() operation completes. Saves the
* volume field of the specified structure to the global variable volume.
@@ -662,16 +676,18 @@ const struct ao_driver audio_out_pulse = {
.pause = pause,
.resume = resume,
.drain = drain,
.wait = wait,
.wait = wait_audio,
.wakeup = wakeup,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.cfg_buffer = 250,
.cfg_latency_hacks = 1,
},
.options = (const struct m_option[]) {
OPT_STRING("host", cfg_host, 0),
OPT_STRING("sink", cfg_sink, 0),
OPT_CHOICE_OR_INT("buffer", cfg_buffer, 0, 1, 2000, ({"native", -1})),
OPT_FLAG("latency-hacks", cfg_latency_hacks, 0),
{0}
},
};
+2 -1
View File
@@ -134,6 +134,8 @@ struct ao_driver {
float (*get_delay)(struct ao *ao);
// push based: block until all queued audio is played (optional)
void (*drain)(struct ao *ao);
// Optional. Return true if audio has stopped in any way.
bool (*get_eof)(struct ao *ao);
// Wait until the audio buffer needs to be refilled. The lock is the
// internal mutex usually protecting the internal AO state (and used to
// protect driver calls), and must be temporarily unlocked while waiting.
@@ -158,7 +160,6 @@ struct ao_driver {
// These functions can be called by AOs.
int ao_play_silence(struct ao *ao, int samples);
void ao_wait_drain(struct ao *ao);
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us);
struct pollfd;
int ao_wait_poll(struct ao *ao, struct pollfd *fds, int num_fds,
+18
View File
@@ -193,6 +193,22 @@ static void resume(struct ao *ao)
ao->driver->resume(ao);
}
static void drain(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
if (atomic_load(&p->state) == AO_STATE_PLAY)
mp_sleep_us(get_delay(ao) * 1000000);
reset(ao);
}
static bool get_eof(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
// For simplicity, ignore the latency. Otherwise, we would have to run an
// extra thread to time it.
return mp_ring_buffered(p->buffers[0]) == 0;
}
static void uninit(struct ao *ao)
{
ao->driver->uninit(ao);
@@ -212,10 +228,12 @@ const struct ao_driver ao_api_pull = {
.init = init,
.control = control,
.uninit = uninit,
.drain = drain,
.reset = reset,
.get_space = get_space,
.play = play,
.get_delay = get_delay,
.get_eof = get_eof,
.pause = pause,
.resume = resume,
.priv_size = sizeof(struct ao_pull_state),
+52 -10
View File
@@ -54,6 +54,7 @@ struct ao_push_state {
bool drain;
bool buffers_full;
bool avoid_ao_wait;
bool still_playing;
bool need_wakeup;
bool requested_data;
bool paused;
@@ -87,15 +88,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
return r;
}
static float get_delay(struct ao *ao)
static double unlocked_get_delay(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
pthread_mutex_lock(&p->lock);
double driver_delay = 0;
if (ao->driver->get_delay)
driver_delay = ao->driver->get_delay(ao);
double delay = driver_delay + mp_audio_buffer_seconds(p->buffer);
pthread_mutex_unlock(&p->lock);
if (delay >= AO_EOF_DELAY && p->expected_end_time) {
if (mp_time_sec() > p->expected_end_time) {
MP_ERR(ao, "Audio device EOF reporting is broken!\n");
@@ -106,6 +105,15 @@ static float get_delay(struct ao *ao)
return delay;
}
static float get_delay(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
pthread_mutex_lock(&p->lock);
float delay = unlocked_get_delay(ao);
pthread_mutex_unlock(&p->lock);
return delay;
}
static void reset(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
@@ -114,6 +122,7 @@ static void reset(struct ao *ao)
ao->driver->reset(ao);
mp_audio_buffer_clear(p->buffer);
p->paused = false;
p->still_playing = false;
wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
}
@@ -146,6 +155,10 @@ static void drain(struct ao *ao)
struct ao_push_state *p = ao->api_priv;
pthread_mutex_lock(&p->lock);
if (p->paused) {
pthread_mutex_unlock(&p->lock);
return;
}
p->final_chunk = true;
p->drain = true;
wakeup_playthread(ao);
@@ -154,7 +167,8 @@ static void drain(struct ao *ao)
pthread_mutex_unlock(&p->lock);
if (!ao->driver->drain)
ao_wait_drain(ao);
mp_sleep_us(get_delay(ao) * 1000000);
reset(ao);
}
static int unlocked_get_space(struct ao *ao)
@@ -185,6 +199,15 @@ static int get_space(struct ao *ao)
return space;
}
static bool get_eof(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
pthread_mutex_lock(&p->lock);
bool eof = !p->still_playing;
pthread_mutex_unlock(&p->lock);
return eof;
}
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct ao_push_state *p = ao->api_priv;
@@ -210,6 +233,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
p->expected_end_time = 0;
p->final_chunk = is_final;
p->paused = false;
p->still_playing |= write_samples > 0;
// If we don't have new data, the decoder thread basically promises it
// will send new data as soon as it's available.
@@ -261,6 +285,7 @@ static void ao_play_data(struct ao *ao)
// any new data (due to rounding to period boundaries).
p->buffers_full = max >= space && r <= 0;
p->avoid_ao_wait = (max == 0 && space > 0) || p->paused || stuck;
p->still_playing |= r > 0;
MP_TRACE(ao, "in=%d, space=%d r=%d flags=%d aw=%d full=%d f=%d\n", max,
space, r, flags, p->avoid_ao_wait, p->buffers_full, p->final_chunk);
}
@@ -314,8 +339,23 @@ static void *playthread(void *arg)
// The most important part is that the decoder is woken up, so
// that the decoder will wake up us in turn.
MP_TRACE(ao, "buffer inactive.\n");
mp_input_wakeup(ao->input_ctx);
pthread_cond_wait(&p->wakeup, &p->lock);
bool was_playing = p->still_playing;
double timeout = -1;
if (p->still_playing && !p->paused) {
timeout = unlocked_get_delay(ao);
if (timeout < AO_EOF_DELAY)
p->still_playing = false;
}
if (!p->requested_data || (was_playing && !p->still_playing))
mp_input_wakeup(ao->input_ctx);
if (p->still_playing && timeout > 0) {
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
} else {
pthread_cond_wait(&p->wakeup, &p->lock);
}
} else {
if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) {
// Fallback to guessing.
@@ -365,11 +405,12 @@ static int init(struct ao *ao)
mp_audio_buffer_reinit_fmt(p->buffer, ao->format,
&ao->channels, ao->samplerate);
mp_audio_buffer_preallocate_min(p->buffer, ao->buffer);
if (pthread_create(&p->thread, NULL, playthread, ao)) {
ao->driver->uninit(ao);
return -1;
}
if (pthread_create(&p->thread, NULL, playthread, ao))
goto err;
return 0;
err:
ao->driver->uninit(ao);
return -1;
}
const struct ao_driver ao_api_push = {
@@ -383,6 +424,7 @@ const struct ao_driver ao_api_push = {
.pause = audio_pause,
.resume = resume,
.drain = drain,
.get_eof = get_eof,
.priv_size = sizeof(struct ao_push_state),
};
+5 -4
View File
@@ -17,8 +17,8 @@
*/
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,6 +28,7 @@
#include "talloc.h"
#include "common/common.h"
#include "misc/ctype.h"
#include "bstr/bstr.h"
int bstrcmp(struct bstr str1, struct bstr str2)
@@ -104,7 +105,7 @@ int bstr_find(struct bstr haystack, struct bstr needle)
struct bstr bstr_lstrip(struct bstr str)
{
while (str.len && isspace(*str.start)) {
while (str.len && mp_isspace(*str.start)) {
str.start++;
str.len--;
}
@@ -114,7 +115,7 @@ struct bstr bstr_lstrip(struct bstr str)
struct bstr bstr_strip(struct bstr str)
{
str = bstr_lstrip(str);
while (str.len && isspace(str.start[str.len - 1]))
while (str.len && mp_isspace(str.start[str.len - 1]))
str.len--;
return str;
}
@@ -242,7 +243,7 @@ bool bstr_eatstart(struct bstr *s, struct bstr prefix)
void bstr_lower(struct bstr str)
{
for (int i = 0; i < str.len; i++)
str.start[i] = tolower(str.start[i]);
str.start[i] = mp_tolower(str.start[i]);
}
int bstr_sscanf(struct bstr str, const char *format, ...)
+45 -1
View File
@@ -19,6 +19,9 @@
#include <libavutil/common.h>
#include <libavutil/log.h>
#include <libavutil/dict.h>
#include <libavutil/opt.h>
#include <libavutil/error.h>
#include <libavcodec/avcodec.h>
#include "common/common.h"
@@ -64,7 +67,6 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
avctx->channels = st->channels;
avctx->block_align = st->block_align;
avctx->channel_layout = st->channel_layout;
avctx->audio_service_type = st->audio_service_type;
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
}
@@ -181,3 +183,45 @@ const char *mp_codec_from_av_codec_id(int codec_id)
}
return name;
}
// kv is in the format as by OPT_KEYVALUELIST(): kv[0]=key0, kv[1]=val0, ...
// Copy them to the dict.
void mp_set_avdict(AVDictionary **dict, char **kv)
{
for (int n = 0; kv && kv[n * 2]; n++)
av_dict_set(dict, kv[n * 2 + 0], kv[n * 2 + 1], 0);
}
// For use with libav* APIs that take AVDictionaries of options.
// Print options remaining in the dict as unset.
void mp_avdict_print_unset(struct mp_log *log, int msgl, AVDictionary *dict)
{
AVDictionaryEntry *t = NULL;
while ((t = av_dict_get(dict, "", t, AV_DICT_IGNORE_SUFFIX)))
mp_msg(log, msgl, "Could not set AVOption %s='%s'\n", t->key, t->value);
}
// kv is in the format as by OPT_KEYVALUELIST(): kv[0]=key0, kv[1]=val0, ...
// Set these options on given avobj (using av_opt_set..., meaning avobj must
// point to a struct that has AVClass as first member).
// Options which fail to set (error or not found) are printed to log.
// Returns: >=0 success, <0 failed to set an option
int mp_set_avopts(struct mp_log *log, void *avobj, char **kv)
{
int success = 0;
for (int n = 0; kv && kv[n * 2]; n++) {
char *k = kv[n * 2 + 0];
char *v = kv[n * 2 + 1];
int r = av_opt_set(avobj, k, v, AV_OPT_SEARCH_CHILDREN);
if (r == AVERROR_OPTION_NOT_FOUND) {
mp_err(log, "AVOption '%s' not found.\n", k);
success = -1;
} else if (r < 0) {
char errstr[80];
av_strerror(r, errstr, sizeof(errstr));
mp_err(log, "Could not set AVOption %s='%s' (%s)\n", k, v, errstr);
success = -1;
}
}
return success;
}
+5
View File
@@ -26,6 +26,8 @@
struct mp_decoder_list;
struct demux_packet;
struct AVDictionary;
struct mp_log;
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size);
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
@@ -36,5 +38,8 @@ void mp_set_avcodec_threads(AVCodecContext *avctx, int threads);
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type);
int mp_codec_to_av_codec_id(const char *codec);
const char *mp_codec_from_av_codec_id(int codec_id);
void mp_set_avdict(struct AVDictionary **dict, char **kv);
void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d);
int mp_set_avopts(struct mp_log *log, void *avobj, char **kv);
#endif
-55
View File
@@ -1,55 +0,0 @@
/*
* AVOption parsing helper
* Copyright (C) 2008 Michael Niedermayer
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <string.h>
#include <libavutil/opt.h>
#include "av_opts.h"
int parse_avopts(void *v, char *str){
char *start;
if (!str)
return 0;
start= str= strdup(str);
while(str && *str){
char *next_opt, *arg;
next_opt= strchr(str, ',');
if(next_opt) *next_opt++= 0;
arg = strchr(str, '=');
if(arg) *arg++= 0;
if (av_opt_set(v, str, arg, AV_OPT_SEARCH_CHILDREN) < 0) {
free(start);
return -1;
}
str= next_opt;
}
free(start);
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
/*
* AVOption parsing helper
* Copyright (C) 2008 Michael Niedermayer
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_AV_OPTS_H
#define MPLAYER_AV_OPTS_H
/**
* Parses str and sets AVOptions in v accordingly.
*/
int parse_avopts(void *v, char *str);
#endif /* MPLAYER_AV_OPTS_H */
+4 -5
View File
@@ -590,11 +590,8 @@ AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx,
}
avcodec_get_context_defaults3(stream->codec, ctx->vc);
// stream->time_base = ctx->timebase;
// doing this breaks mpeg2ts in ffmpeg
// which doesn't properly force the time base to be 90000
// furthermore, ffmpeg.c doesn't do this either and works
// Using codec->time_base is deprecated, but needed for older lavf.
stream->time_base = ctx->timebase;
stream->codec->time_base = ctx->timebase;
ctx->voptions = NULL;
@@ -626,6 +623,8 @@ AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx,
}
avcodec_get_context_defaults3(stream->codec, ctx->ac);
// Using codec->time_base is deprecated, but needed for older lavf.
stream->time_base = ctx->timebase;
stream->codec->time_base = ctx->timebase;
ctx->aoptions = NULL;
+5 -1
View File
@@ -429,7 +429,7 @@ void mp_msg_update_msglevels(struct mpv_global *global)
root->show_time = opts->msg_time;
if (root->use_terminal) {
root->color = opts->msg_color && isatty(fileno(stdout));
root->termosd = !opts->slave_mode && isatty(fileno(stderr));
root->termosd = isatty(fileno(stderr));
}
talloc_free(root->msglevels);
@@ -469,6 +469,10 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
{
struct mp_log_root *root = global->log->root;
#if !HAVE_ATOMICS
return NULL;
#endif
pthread_mutex_lock(&mp_msg_lock);
struct mp_log_buffer *buffer = talloc_ptrtype(NULL, buffer);
+9 -3
View File
@@ -215,6 +215,15 @@ void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
}
}
void playlist_append_entries(struct playlist *pl, struct playlist *source_pl)
{
while (source_pl->first) {
struct playlist_entry *e = source_pl->first;
playlist_unlink(source_pl, e);
playlist_add(pl, e);
}
}
// Return number of entries between list start and e.
// Return -1 if e is not on the list, or if e is NULL.
int playlist_entry_to_index(struct playlist *pl, struct playlist_entry *e)
@@ -277,9 +286,6 @@ struct playlist *playlist_parse_file(const char *file, struct mpv_global *global
if (ret && !ret->first)
mp_warn(log, "Warning: empty playlist\n");
if (ret)
playlist_add_base_path(ret, mp_dirname(file));
talloc_free(log);
return ret;
}
+1
View File
@@ -74,6 +74,7 @@ void playlist_shuffle(struct playlist *pl);
struct playlist_entry *playlist_get_next(struct playlist *pl, int direction);
void playlist_add_base_path(struct playlist *pl, bstr base_path);
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
void playlist_append_entries(struct playlist *pl, struct playlist *source_pl);
int playlist_entry_to_index(struct playlist *pl, struct playlist_entry *e);
int playlist_entry_count(struct playlist *pl);
+19
View File
@@ -61,6 +61,25 @@ void mp_tags_clear(struct mp_tags *tags)
talloc_free_children(tags);
}
struct mp_tags *mp_tags_dup(void *tparent, struct mp_tags *tags)
{
struct mp_tags *new = talloc_zero(tparent, struct mp_tags);
MP_RESIZE_ARRAY(new, new->keys, tags->num_keys);
MP_RESIZE_ARRAY(new, new->values, tags->num_keys);
new->num_keys = tags->num_keys;
for (int n = 0; n < tags->num_keys; n++) {
new->keys[n] = talloc_strdup(new, tags->keys[n]);
new->values[n] = talloc_strdup(new, tags->values[n]);
}
return new;
}
void mp_tags_merge(struct mp_tags *tags, struct mp_tags *src)
{
for (int n = 0; n < src->num_keys; n++)
mp_tags_set_str(tags, src->keys[n], src->values[n]);
}
void mp_tags_copy_from_av_dictionary(struct mp_tags *tags,
struct AVDictionary *av_dict)
{
+2
View File
@@ -14,6 +14,8 @@ void mp_tags_set_bstr(struct mp_tags *tags, bstr key, bstr value);
char *mp_tags_get_str(struct mp_tags *tags, const char *key);
char *mp_tags_get_bstr(struct mp_tags *tags, bstr key);
void mp_tags_clear(struct mp_tags *tags);
struct mp_tags *mp_tags_dup(void *tparent, struct mp_tags *tags);
void mp_tags_merge(struct mp_tags *tags, struct mp_tags *src);
struct AVDictionary;
void mp_tags_copy_from_av_dictionary(struct mp_tags *tags,
struct AVDictionary *av_dict);
+13 -2
View File
@@ -22,6 +22,8 @@
#include <inttypes.h>
#include "config.h"
#define HAVE_ATOMICS 1
#if HAVE_STDATOMIC
#include <stdatomic.h>
#else
@@ -58,8 +60,17 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
__sync_fetch_and_add(&(a)->v, b)
#else
# error "this should have been a configuration error, report a bug please"
#endif
// This is extremely wrong. The build system actually disables code that has
// a serious dependency on working atomics, so this is barely ok.
#define atomic_load(p) ((p)->v)
#define atomic_store(p, val) ((p)->v = (val))
#define atomic_fetch_add(a, b) (((a)->v += (b)) - (b))
#undef HAVE_ATOMICS
#define HAVE_ATOMICS 0
#endif /* no atomics */
#endif /* else HAVE_STDATOMIC */
+680 -487
View File
File diff suppressed because it is too large. Load diff
+46 -52
View File
@@ -31,14 +31,18 @@
#include "packet.h"
#include "stheader.h"
struct MPOpts;
#define MAX_PACKS 4096
#define MAX_PACK_BYTES 0x8000000 // 128 MiB
// Maximum total size of packets queued - if larger, no new packets are read,
// and the demuxer pretends EOF was reached.
#define MAX_PACKS 16000
#define MAX_PACK_BYTES (400 * 1024 * 1024)
// Minimum total size of packets queued - the demuxer thread will read more
// packets, until either number or total size of the packets exceed the minimum.
// This can actually be configured with command line options.
#define MIN_PACKS 64
#define MIN_PACK_BYTES (5 * 1024 * 1024)
enum demuxer_type {
DEMUXER_TYPE_GENERIC = 0,
DEMUXER_TYPE_TV,
DEMUXER_TYPE_MATROSKA,
DEMUXER_TYPE_EDL,
DEMUXER_TYPE_CUE,
@@ -48,15 +52,13 @@ enum demuxer_type {
#define DEMUXER_CTRL_NOTIMPL -1
#define DEMUXER_CTRL_DONTKNOW 0
#define DEMUXER_CTRL_OK 1
#define DEMUXER_CTRL_GUESS 2
enum demux_ctrl {
DEMUXER_CTRL_SWITCHED_TRACKS = 1,
DEMUXER_CTRL_GET_TIME_LENGTH,
DEMUXER_CTRL_GET_START_TIME,
DEMUXER_CTRL_RESYNC,
DEMUXER_CTRL_IDENTIFY_PROGRAM,
DEMUXER_CTRL_STREAM_CTRL, // stupid workaround for legacy TV code
DEMUXER_CTRL_STREAM_CTRL,
};
struct demux_ctrl_stream_ctrl {
@@ -87,6 +89,13 @@ enum demux_check {
DEMUX_CHECK_NORMAL, // normal, safe detection
};
enum demux_event {
DEMUX_EVENT_INIT = 1 << 0, // complete (re-)initialization
DEMUX_EVENT_STREAMS = 1 << 1, // a stream was added
DEMUX_EVENT_METADATA = 1 << 2, // metadata or stream_metadata changed
DEMUX_EVENT_ALL = 0xFFFF,
};
#define MAX_SH_STREAMS 256
struct demuxer;
@@ -105,7 +114,7 @@ typedef struct demuxer_desc {
// The following functions are all optional
int (*fill_buffer)(struct demuxer *demuxer); // 0 on EOF, otherwise 1
void (*close)(struct demuxer *demuxer);
void (*seek)(struct demuxer *demuxer, float rel_seek_secs, int flags);
void (*seek)(struct demuxer *demuxer, double rel_seek_secs, int flags);
int (*control)(struct demuxer *demuxer, int cmd, void *arg);
} demuxer_desc_t;
@@ -169,21 +178,18 @@ typedef struct demuxer {
const demuxer_desc_t *desc; ///< Demuxer description structure
const char *filetype; // format name when not identified by demuxer (libavformat)
int64_t filepos; // input stream current pos.
struct stream *stream;
double stream_pts; // current stream pts, if applicable (e.g. dvd)
char *filename; // same as stream->url
enum demuxer_type type;
int seekable; // flag
/* Set if using absolute seeks for small movements is OK (no pts resets
* that would make pts ambigious, preferably supports back/forward flags */
bool accurate_seek;
double start_time;
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
bool warned_queue_overflow;
// Bitmask of DEMUX_EVENT_*
int events;
struct sh_stream **streams;
int num_streams;
bool stream_autoselect;
struct demux_edition *editions;
int num_editions;
@@ -199,19 +205,24 @@ typedef struct demuxer {
// for trivial demuxers which just read the whole file for codec to use
struct bstr file_contents;
struct replaygain_data *replaygain_data;
// If the file is a playlist file
struct playlist *playlist;
struct mp_tags *metadata;
char *previous_metadata;
void *priv; // demuxer-specific internal data
struct MPOpts *opts;
struct mpv_global *global;
struct mp_log *log, *glog;
struct demuxer_params *params;
struct demux_internal *in; // internal to demux.c
// Since the demuxer can run in its own thread, and the stream is not
// thread-safe, only the demuxer is allowed to access the stream directly.
// You can freely use demux_stream_control() to send STREAM_CTRLs, or use
// demux_pause() to get exclusive access to the stream.
struct stream *stream;
} demuxer_t;
typedef struct {
@@ -219,27 +230,17 @@ typedef struct {
int aid, vid, sid; //audio, video and subtitle id
} demux_program_t;
struct demux_packet *new_demux_packet(size_t len);
// data must already have suitable padding
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
struct demux_packet *new_demux_packet_from(void *data, size_t len);
void demux_packet_shorten(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);
struct demux_packet *demux_copy_packet(struct demux_packet *dp);
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
void free_demuxer(struct demuxer *demuxer);
int demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
demux_packet_t *dp);
int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp);
struct demux_packet *demux_read_packet(struct sh_stream *sh);
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt);
bool demux_stream_is_selected(struct sh_stream *stream);
double demux_get_next_pts(struct sh_stream *sh);
bool demux_has_packet(struct sh_stream *sh);
bool demux_stream_eof(struct sh_stream *sh);
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
@@ -247,11 +248,14 @@ struct demuxer *demux_open(struct stream *stream, char *force_format,
struct demuxer_params *params,
struct mpv_global *global);
void demux_start_thread(struct demuxer *demuxer);
void demux_stop_thread(struct demuxer *demuxer);
void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *ctx);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, float rel_seek_secs, int flags);
char *demux_info_get(struct demuxer *demuxer, const char *opt);
bool demux_info_update(struct demuxer *demuxer);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
@@ -259,7 +263,7 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
struct sh_stream *stream);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected);
void demuxer_enable_autoselect(struct demuxer *demuxer);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
void demuxer_help(struct mp_log *log);
@@ -269,28 +273,18 @@ int demuxer_add_chapter(struct demuxer *demuxer, struct bstr name,
uint64_t start, uint64_t end, uint64_t demuxer_id);
double demuxer_get_time_length(struct demuxer *demuxer);
double demuxer_get_start_time(struct demuxer *demuxer);
/// Get current angle index.
int demuxer_get_current_angle(struct demuxer *demuxer);
/// Set angle.
int demuxer_set_angle(struct demuxer *demuxer, int angle);
/// Get number of angles.
int demuxer_angles_count(struct demuxer *demuxer);
int demux_stream_control(demuxer_t *demuxer, int ctrl, void *arg);
void demux_pause(demuxer_t *demuxer);
void demux_unpause(demuxer_t *demuxer);
void demux_changed(demuxer_t *demuxer, int events);
void demux_update(demuxer_t *demuxer);
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id);
bool demuxer_stream_is_selected(struct demuxer *d, struct sh_stream *stream);
bool demuxer_stream_has_packets_queued(struct demuxer *d, struct sh_stream *stream);
void demux_packet_list_sort(struct demux_packet **pkts, int num_pkts);
void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts,
int *current, float rel_seek_secs, int flags);
double demux_packet_list_duration(struct demux_packet **pkts, int num_pkts);
struct demux_packet *demux_packet_list_fill(struct demux_packet **pkts,
int num_pkts, int *current);
bool demux_matroska_uid_cmp(struct matroska_segment_uid *a,
struct matroska_segment_uid *b);
+354
View File
@@ -0,0 +1,354 @@
/*
* 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 <string.h>
#include <math.h>
#include <assert.h>
#include "common/common.h"
#include "common/msg.h"
#include "stream/stream.h"
#include "demux.h"
#include "stheader.h"
#include "video/csputils.h"
struct priv {
struct demuxer *slave;
// streams[slave_stream_index] == our_stream
struct sh_stream **streams;
int num_streams;
// This contains each DVD sub stream, or NULL. Needed because DVD packets
// can come arbitrarily late in the MPEG stream, so the slave demuxer
// might add the streams only later.
struct sh_stream *dvd_subs[32];
// Used to rewrite the raw MPEG timestamps to playback time.
double base_time; // playback display start time of current segment
double base_dts; // packet DTS that maps to base_time
double last_dts; // DTS of previously demuxed packet
double seek_pts;
bool seek_reinit; // needs reinit after seek
};
// If the timestamp difference between subsequent packets is this big, assume
// a reset. It should be big enough to account for 1. low video framerates and
// large audio frames, and 2. bad interleaving.
#define DTS_RESET_THRESHOLD 5.0
static void reselect_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
for (int n = 0; n < MPMIN(p->slave->num_streams, p->num_streams); n++) {
if (p->streams[n]) {
demuxer_select_track(p->slave, p->slave->streams[n],
demux_stream_is_selected(p->streams[n]));
}
}
}
static void get_disc_lang(struct stream *stream, struct sh_stream *sh)
{
struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
if (stream->uncached_type == STREAMTYPE_DVD && sh->type == STREAM_SUB)
req.id = req.id & 0x1F; // mpeg ID to index
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
if (req.name[0])
sh->lang = talloc_strdup(sh, req.name);
}
static void add_dvd_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct stream *stream = demuxer->stream;
if (stream->uncached_type != STREAMTYPE_DVD)
return;
struct stream_dvd_info_req info;
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
for (int n = 0; n < MPMIN(32, info.num_subs); n++) {
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
if (!sh)
break;
sh->demuxer_id = n + 0x20;
sh->codec = "dvd_subtitle";
get_disc_lang(stream, sh);
// p->streams _must_ match with p->slave->streams, so we can't add
// it yet - it has to be done when the real stream appears, which
// could be right on start, or any time later.
p->dvd_subs[n] = sh;
// emulate the extradata
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
csp.int_bits_in = 8;
csp.int_bits_out = 8;
float cmatrix[3][4];
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
char *s = talloc_strdup(sh, "");
s = talloc_asprintf_append(s, "palette: ");
for (int i = 0; i < 16; i++) {
int color = info.palette[i];
int c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
mp_map_int_color(cmatrix, 8, c);
color = (c[2] << 16) | (c[1] << 8) | c[0];
if (i != 0)
s = talloc_asprintf_append(s, ", ");
s = talloc_asprintf_append(s, "%06x", color);
}
s = talloc_asprintf_append(s, "\n");
sh->sub->extradata = s;
sh->sub->extradata_len = strlen(s);
}
}
}
static void add_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
for (int n = p->num_streams; n < p->slave->num_streams; n++) {
struct sh_stream *src = p->slave->streams[n];
if (src->sub) {
struct sh_stream *sub = NULL;
if (src->demuxer_id >= 0x20 && src->demuxer_id <= 0x3F)
sub = p->dvd_subs[src->demuxer_id - 0x20];
if (sub) {
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sub);
continue;
}
}
struct sh_stream *sh = new_sh_stream(demuxer, src->type);
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
// Copy all stream fields that might be relevant
sh->codec = talloc_strdup(sh, src->codec);
sh->format = src->format;
sh->lav_headers = src->lav_headers;
if (sh && src->video) {
double ar;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
== STREAM_OK)
sh->video->aspect = ar;
}
if (sh && src->audio)
sh->audio = src->audio;
}
reselect_streams(demuxer);
}
static void d_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
{
struct priv *p = demuxer->priv;
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
demux_seek(p->slave, rel_seek_secs, flags);
return;
}
double pts = p->seek_pts;
if (flags & SEEK_ABSOLUTE)
pts = 0.0f;
if (flags & SEEK_FACTOR) {
double tmp = 0;
stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
pts += tmp * rel_seek_secs;
} else {
pts += rel_seek_secs;
}
MP_VERBOSE(demuxer, "seek to: %f\n", pts);
stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts);
demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
p->seek_pts = pts;
p->seek_reinit = true;
}
static void reset_pts(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
double base;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_TIME, &base) < 1)
base = 0;
MP_VERBOSE(demuxer, "reset to time: %f\n", base);
p->base_dts = p->last_dts = MP_NOPTS_VALUE;
p->base_time = base;
p->seek_reinit = false;
}
static int d_fill_buffer(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct demux_packet *pkt = demux_read_any_packet(p->slave);
if (!pkt)
return 0;
demux_update(p->slave);
if (p->seek_reinit)
reset_pts(demuxer);
add_streams(demuxer);
if (pkt->stream >= p->num_streams) { // out of memory?
talloc_free(pkt);
return 0;
}
struct sh_stream *sh = p->streams[pkt->stream];
if (!demux_stream_is_selected(sh)) {
talloc_free(pkt);
return 1;
}
MP_TRACE(demuxer, "ipts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
if (sh->type == STREAM_SUB) {
if (p->base_dts == MP_NOPTS_VALUE)
MP_WARN(demuxer, "subtitle packet along PTS reset\n");
} else if (pkt->dts != MP_NOPTS_VALUE) {
// Use the very first DTS to rebase the start time of the MPEG stream
// to the playback time.
if (p->base_dts == MP_NOPTS_VALUE)
p->base_dts = pkt->dts;
if (p->last_dts == MP_NOPTS_VALUE)
p->last_dts = pkt->dts;
if (fabs(p->last_dts - pkt->dts) >= DTS_RESET_THRESHOLD) {
MP_WARN(demuxer, "PTS discontinuity: %f->%f\n", p->last_dts, pkt->dts);
p->base_time += p->last_dts - p->base_dts;
p->base_dts = pkt->dts - pkt->duration;
}
p->last_dts = pkt->dts;
}
if (p->base_dts != MP_NOPTS_VALUE) {
double delta = -p->base_dts + p->base_time;
if (pkt->pts != MP_NOPTS_VALUE)
pkt->pts += delta;
if (pkt->dts != MP_NOPTS_VALUE)
pkt->dts += delta;
}
MP_TRACE(demuxer, "opts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
if (pkt->pts != MP_NOPTS_VALUE)
p->seek_pts = pkt->pts;
demux_add_packet(sh, pkt);
return 1;
}
static void add_stream_chapters(struct demuxer *demuxer)
{
int num = 0;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &num) < 1)
return;
for (int n = 0; n < num; n++) {
double p = n;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p) < 1)
continue;
demuxer_add_chapter(demuxer, bstr0(""), p * 1e9, 0, 0);
}
}
static int d_open(demuxer_t *demuxer, enum demux_check check)
{
struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
if (check != DEMUX_CHECK_FORCE)
return -1;
char *demux = "+lavf";
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA)
demux = "+rawaudio";
char *t = NULL;
stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t);
if (t) {
mp_tags_set_bstr(demuxer->metadata, bstr0("TITLE"), bstr0(t));
talloc_free(t);
}
// Initialize the playback time. We need to read _some_ data to get the
// correct stream-layer time (at least with libdvdnav).
stream_peek(demuxer->stream, 1);
reset_pts(demuxer);
p->slave = demux_open(demuxer->stream, demux, NULL, demuxer->global);
if (!p->slave)
return -1;
// So that we don't miss initial packets of delayed subtitle streams.
demux_set_stream_autoselect(p->slave, true);
// Can be seekable even if the stream isn't.
demuxer->seekable = true;
add_dvd_streams(demuxer);
add_streams(demuxer);
add_stream_chapters(demuxer);
return 0;
}
static void d_close(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
free_demuxer(p->slave);
}
static int d_control(demuxer_t *demuxer, int cmd, void *arg)
{
struct priv *p = demuxer->priv;
switch (cmd) {
case DEMUXER_CTRL_GET_TIME_LENGTH: {
double len;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) < 1)
break;
*(double *)arg = len;
return DEMUXER_CTRL_OK;
}
case DEMUXER_CTRL_RESYNC:
demux_flush(p->slave);
break; // relay to slave demuxer
case DEMUXER_CTRL_SWITCHED_TRACKS:
reselect_streams(demuxer);
return DEMUXER_CTRL_OK;
}
return demux_control(p->slave, cmd, arg);
}
const demuxer_desc_t demuxer_desc_disc = {
.name = "disc",
.desc = "CD/DVD/BD wrapper",
.fill_buffer = d_fill_buffer,
.open = d_open,
.close = d_close,
.seek = d_seek,
.control = d_control,
};
+61 -85
View File
@@ -18,12 +18,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// #include <stdio.h>
#include <stdlib.h>
// #include <unistd.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include "config.h"
@@ -45,7 +44,6 @@
#include "options/options.h"
#include "common/msg.h"
#include "common/tags.h"
#include "common/av_opts.h"
#include "common/av_common.h"
#include "bstr/bstr.h"
@@ -54,6 +52,7 @@
#include "stheader.h"
#include "options/m_option.h"
#define INITIAL_PROBE_SIZE STREAM_BUFFER_SIZE
#define PROBE_BUF_SIZE FFMIN(STREAM_MAX_BUFFER_SIZE, 2 * 1024 * 1024)
@@ -71,7 +70,7 @@ struct demux_lavf_opts {
int allow_mimetype;
char *format;
char *cryptokey;
char *avopt;
char **avopts;
int genptsmode;
};
@@ -87,7 +86,7 @@ const struct m_sub_options demux_lavf_conf = {
OPT_STRING("cryptokey", cryptokey, 0),
OPT_CHOICE("genpts-mode", genptsmode, 0,
({"lavf", 1}, {"no", 0})),
OPT_STRING("o", avopt, 0),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
.size = sizeof(struct demux_lavf_opts),
@@ -107,6 +106,7 @@ typedef struct lavf_priv {
int num_streams;
int cur_program;
char *mime_type;
bool merge_track_metadata;
} lavf_priv_t;
struct format_hack {
@@ -129,6 +129,9 @@ static const struct format_hack format_hacks[] = {
static const char *const format_blacklist[] = {
"tty", // Useless non-sense, sometimes breaks MLP2 subreader.c fallback
// Image demuxers, disabled in favor of demux_mf:
"image2", "image2pipe",
"bmp_pipe", "dpx_pipe", "exr_pipe", "j2k_pipe", "png_pipe", "tiff_pipe",
0
};
@@ -140,8 +143,8 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
ret = stream_read(stream, buf, size);
MP_DBG(demuxer, "%d=mp_read(%p, %p, %d), pos: %"PRId64", eof:%d\n",
ret, stream, buf, size, stream_tell(stream), stream->eof);
MP_TRACE(demuxer, "%d=mp_read(%p, %p, %d), pos: %"PRId64", eof:%d\n",
ret, stream, buf, size, stream_tell(stream), stream->eof);
return ret;
}
@@ -150,10 +153,7 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
struct demuxer *demuxer = opaque;
struct stream *stream = demuxer->stream;
int64_t current_pos;
if (stream_manages_timeline(stream))
return -1;
MP_DBG(demuxer, "mp_seek(%p, %"PRId64", %d)\n",
stream, pos, whence);
MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %d)\n", stream, pos, whence);
if (whence == SEEK_END || whence == AVSEEK_SIZE) {
int64_t end;
if (stream_control(stream, STREAM_CTRL_GET_SIZE, &end) != STREAM_OK)
@@ -182,14 +182,15 @@ static int64_t mp_read_seek(void *opaque, int stream_idx, int64_t ts, int flags)
{
struct demuxer *demuxer = opaque;
struct stream *stream = demuxer->stream;
struct lavf_priv *priv = demuxer->priv;
AVStream *st = priv->avfc->streams[stream_idx];
double pts = (double)ts * st->time_base.num / st->time_base.den;
int ret = stream_control(stream, STREAM_CTRL_SEEK_TO_TIME, &pts);
if (ret < 0)
ret = AVERROR(ENOSYS);
return ret;
struct stream_avseek cmd = {
.stream_index = stream_idx,
.timestamp = ts,
.flags = flags,
};
int ret = stream_control(stream, STREAM_CTRL_AVSEEK, &cmd);
return ret < 1 ? AVERROR(ENOSYS) : 0;
}
static void list_formats(struct demuxer *demuxer)
@@ -264,8 +265,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
MP_FATAL(demuxer, "Unknown lavf format %s\n", format);
return -1;
}
MP_INFO(demuxer, "Forced lavf %s demuxer\n",
priv->avif->long_name);
MP_VERBOSE(demuxer, "Forced lavf %s demuxer\n", priv->avif->long_name);
goto success;
}
@@ -393,13 +393,13 @@ static void select_tracks(struct demuxer *demuxer, int start)
for (int n = start; n < priv->num_streams; n++) {
struct sh_stream *stream = priv->streams[n];
AVStream *st = priv->avfc->streams[n];
bool selected = stream && demuxer_stream_is_selected(demuxer, stream) &&
bool selected = stream && demux_stream_is_selected(stream) &&
!stream->attached_picture;
st->discard = selected ? AVDISCARD_DEFAULT : AVDISCARD_ALL;
}
}
static void export_replaygain(demuxer_t *demuxer, AVStream *st)
static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
{
#if HAVE_AVCODEC_REPLAYGAIN_SIDE_DATA
for (int i = 0; i < st->nb_side_data; i++) {
@@ -425,7 +425,7 @@ static void export_replaygain(demuxer_t *demuxer, AVStream *st)
rgain->album_peak = (av_rgain->album_peak != 0.0) ?
av_rgain->album_peak / 100000.0f : 1.0;
demuxer->replaygain_data = rgain;
sh->replaygain_data = rgain;
}
#endif
}
@@ -454,7 +454,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
sh_audio->samplerate = codec->sample_rate;
sh_audio->bitrate = codec->bit_rate;
export_replaygain(demuxer, st);
export_replaygain(demuxer, sh_audio, st);
break;
}
@@ -504,7 +504,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
#if HAVE_AV_DISPLAYMATRIX
uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (sd)
sh_video->rotate = av_display_rotation_get((uint32_t *)sd);
sh_video->rotate = -av_display_rotation_get((uint32_t *)sd);
#else
AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
if (rot && rot->value) {
@@ -519,9 +519,6 @@ static void handle_stream(demuxer_t *demuxer, int i)
// This also applies to vfw-muxed mkv, but we can't detect these easily.
sh_video->avi_dts = matches_avinputformat_name(priv, "avi");
MP_DBG(demuxer, "aspect= %d*%d/(%d*%d)\n",
codec->width, codec->sample_aspect_ratio.num,
codec->height, codec->sample_aspect_ratio.den);
break;
}
case AVMEDIA_TYPE_SUBTITLE: {
@@ -558,11 +555,18 @@ static void handle_stream(demuxer_t *demuxer, int i)
AVDictionaryEntry *ftag = av_dict_get(st->metadata, "filename",
NULL, 0);
char *filename = ftag ? ftag->value : NULL;
if (st->codec->codec_id == AV_CODEC_ID_TTF)
demuxer_add_attachment(demuxer, bstr0(filename),
bstr0("application/x-truetype-font"),
char *mimetype = NULL;
switch (st->codec->codec_id) {
case AV_CODEC_ID_TTF: mimetype = "application/x-truetype-font"; break;
#if LIBAVFORMAT_VERSION_MICRO >= 100
case AV_CODEC_ID_OTF: mimetype = "application/vnd.ms-opentype"; break;
#endif
}
if (mimetype) {
demuxer_add_attachment(demuxer, bstr0(filename), bstr0(mimetype),
(struct bstr){codec->extradata,
codec->extradata_size});
}
break;
}
default: ;
@@ -589,6 +593,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
}
select_tracks(demuxer, i);
demux_changed(demuxer, DEMUX_EVENT_STREAMS);
}
// Add any new streams that might have been added
@@ -602,16 +607,18 @@ static void add_new_streams(demuxer_t *demuxer)
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
{
#if HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
lavf_priv_t *priv = demuxer->priv;
int md_size;
const uint8_t *md;
md = av_packet_get_side_data(pkt, AV_PKT_DATA_METADATA_UPDATE, &md_size);
if (md) {
if (md && priv->merge_track_metadata) {
AVDictionary *dict = NULL;
av_packet_unpack_dictionary(md, md_size, &dict);
if (dict) {
mp_tags_clear(demuxer->metadata);
mp_tags_copy_from_av_dictionary(demuxer->metadata, dict);
av_dict_free(&dict);
demux_changed(demuxer, DEMUX_EVENT_METADATA);
}
}
#endif
@@ -651,7 +658,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
if (lavfdopts->probesize) {
if (av_opt_set_int(avfc, "probesize", lavfdopts->probesize, 0) < 0)
MP_ERR(demuxer, "demux_lavf, couldn't set option probesize to %u\n",
MP_ERR(demuxer, "couldn't set option probesize to %u\n",
lavfdopts->probesize);
}
@@ -666,14 +673,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
"analyzeduration to %f\n", analyze_duration);
}
if (lavfdopts->avopt) {
if (parse_avopts(avfc, lavfdopts->avopt) < 0) {
MP_ERR(demuxer, "Your options /%s/ look like gibberish to me pal\n",
lavfdopts->avopt);
return -1;
}
}
if ((priv->avif->flags & AVFMT_NOFILE) ||
demuxer->stream->type == STREAMTYPE_AVDEVICE)
{
@@ -707,27 +706,25 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
av_dict_set(&dopts, "rtsp_transport", transport, 0);
}
mp_set_avdict(&dopts, lavfdopts->avopts);
if (avformat_open_input(&avfc, priv->filename, priv->avif, &dopts) < 0) {
MP_ERR(demuxer, "LAVF_header: avformat_open_input() failed\n");
MP_ERR(demuxer, "avformat_open_input() failed\n");
av_dict_free(&dopts);
return -1;
}
t = NULL;
while ((t = av_dict_get(dopts, "", t, AV_DICT_IGNORE_SUFFIX))) {
MP_VERBOSE(demuxer, "[lavf] Could not set demux option %s=%s\n",
t->key, t->value);
}
mp_avdict_print_unset(demuxer->log, MSGL_V, dopts);
av_dict_free(&dopts);
priv->avfc = avfc;
if (avformat_find_stream_info(avfc, NULL) < 0) {
MP_ERR(demuxer, "LAVF_header: av_find_stream_info() failed\n");
MP_ERR(demuxer, "av_find_stream_info() failed\n");
return -1;
}
MP_VERBOSE(demuxer, "demux_lavf: avformat_find_stream_info() "
"finished after %"PRId64" bytes.\n", stream_tell(demuxer->stream));
MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
" bytes.\n", stream_tell(demuxer->stream));
for (i = 0; i < avfc->nb_chapters; i++) {
AVChapter *c = avfc->chapters[i];
@@ -748,27 +745,18 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
// Often useful with OGG audio-only files, which have metadata in the audio
// track metadata instead of the main metadata.
if (demuxer->num_streams == 1) {
priv->merge_track_metadata = true;
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->streams[n]->metadata);
}
}
if (avfc->nb_programs) {
int p;
for (p = 0; p < avfc->nb_programs; p++) {
AVProgram *program = avfc->programs[p];
t = av_dict_get(program->metadata, "title", NULL, 0);
MP_INFO(demuxer, "LAVF: Program %d %s\n",
program->id, t ? t->value : "");
MP_VERBOSE(demuxer, "PROGRAM_ID=%d\n", program->id);
}
}
MP_VERBOSE(demuxer, "LAVF: build %d\n", LIBAVFORMAT_BUILD);
demuxer->ts_resets_possible = priv->avif->flags & AVFMT_TS_DISCONT;
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
return 0;
}
@@ -781,12 +769,12 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
{
lavf_priv_t *priv = demux->priv;
demux_packet_t *dp;
MP_DBG(demux, "demux_lavf_fill_buffer()\n");
AVPacket *pkt = talloc(NULL, AVPacket);
if (av_read_frame(priv->avfc, pkt) < 0) {
int r = av_read_frame(priv->avfc, pkt);
if (r < 0) {
talloc_free(pkt);
return 0; // eof
return r == AVERROR(EAGAIN) ? 1 : -1; // eof
}
talloc_set_destructor(pkt, destroy_avpacket);
@@ -797,7 +785,7 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
struct sh_stream *stream = priv->streams[pkt->stream_index];
AVStream *st = priv->avfc->streams[pkt->stream_index];
if (!demuxer_stream_is_selected(demux, stream)) {
if (!demux_stream_is_selected(stream)) {
talloc_free(pkt);
return 1; // don't signal EOF if skipping a packet
}
@@ -820,27 +808,19 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
dp->duration = pkt->convergence_duration * av_q2d(st->time_base);
dp->pos = pkt->pos;
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
// Use only one stream for stream_pts, otherwise PTS might be jumpy.
if (stream->type == STREAM_VIDEO) {
double pts;
if (stream_control(demux->stream, STREAM_CTRL_GET_CURRENT_TIME, &pts) > 0)
dp->stream_pts = pts;
}
if (dp->pts != MP_NOPTS_VALUE) {
priv->last_pts = dp->pts * AV_TIME_BASE;
} else if (dp->dts != MP_NOPTS_VALUE) {
priv->last_pts = dp->dts * AV_TIME_BASE;
}
demuxer_add_packet(demux, stream, dp);
demux_add_packet(stream, dp);
return 1;
}
static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, int flags)
static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
{
lavf_priv_t *priv = demuxer->priv;
int avsflags = 0;
MP_DBG(demuxer, "demux_seek_lavf(%p, %f, %d)\n",
demuxer, rel_seek_secs, flags);
if (flags & SEEK_ABSOLUTE)
priv->last_pts = 0;
@@ -926,11 +906,6 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
*((double *)arg) = (double)priv->avfc->duration / AV_TIME_BASE;
return DEMUXER_CTRL_OK;
case DEMUXER_CTRL_GET_START_TIME:
*((double *)arg) = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
return DEMUXER_CTRL_OK;
case DEMUXER_CTRL_SWITCHED_TRACKS:
{
select_tracks(demuxer, 0);
@@ -1006,11 +981,12 @@ redo:
* call the new API instead of relying on av_seek_frame() to do this
* for us.
*/
stream_drop_buffers(demuxer->stream);
avio_flush(priv->avfc->pb);
av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
AVSEEK_FLAG_BYTE);
avio_flush(priv->avfc->pb);
// avio_flush() is designed for write-only streams, and does the wrong
// thing when reading. Flush it manually instead.
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end;
stream_drop_buffers(demuxer->stream);
return DEMUXER_CTRL_OK;
default:
return DEMUXER_CTRL_NOTIMPL;
+4 -3
View File
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -36,7 +37,7 @@
#define MF_MAX_FILE_SIZE (1024 * 1024 * 256)
static void demux_seek_mf(demuxer_t *demuxer, float rel_seek_secs, int flags)
static void demux_seek_mf(demuxer_t *demuxer, double rel_seek_secs, int flags)
{
mf_t *mf = demuxer->priv;
int newpos = (flags & SEEK_ABSOLUTE) ? 0 : mf->curr_frame - 1;
@@ -48,7 +49,7 @@ static void demux_seek_mf(demuxer_t *demuxer, float rel_seek_secs, int flags)
if (newpos < 0)
newpos = 0;
if (newpos >= mf->nr_of_files)
newpos = mf->nr_of_files - 1;
newpos = mf->nr_of_files;
mf->curr_frame = newpos;
}
@@ -79,7 +80,7 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer)
memcpy(dp->buffer, data.start, data.len);
dp->pts = mf->curr_frame / mf->sh->fps;
dp->keyframe = true;
demuxer_add_packet(demuxer, demuxer->streams[0], dp);
demux_add_packet(demuxer->streams[0], dp);
}
talloc_free(data.start);
}
+149 -123
View File
@@ -23,7 +23,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <assert.h>
@@ -128,13 +127,13 @@ typedef struct mkv_track {
double ra_pts; /* previous audio timestamp */
/** realaudio descrambling */
int sub_packet_size; ///< sub packet size, per stream
int sub_packet_h; ///< number of coded frames per block
int coded_framesize; ///< coded frame size, per stream
int audiopk_size; ///< audio packet size
uint16_t sub_packet_size; ///< sub packet size, per stream
uint16_t sub_packet_h; ///< number of coded frames per block
uint32_t coded_framesize; ///< coded frame size, per stream
uint16_t audiopk_size; ///< audio packet size
unsigned char *audio_buf; ///< place to store reordered audio data
double *audio_timestamp; ///< timestamp for each audio packet
int sub_packet_cnt; ///< number of subpacket already received
uint16_t sub_packet_cnt; ///< number of subpacket already received
int audio_filepos; ///< file position of first audio packet in block
/* generic content encoding support */
@@ -142,7 +141,7 @@ typedef struct mkv_track {
int num_encodings;
/* latest added index entry for this track */
int last_index_entry;
size_t last_index_entry;
} mkv_track_t;
typedef struct mkv_index {
@@ -167,7 +166,7 @@ typedef struct mkv_demuxer {
uint64_t cluster_end;
mkv_index_t *indexes;
int num_indexes;
size_t num_indexes;
bool index_complete;
uint64_t deferred_cues;
@@ -192,19 +191,6 @@ typedef struct mkv_demuxer {
// (Subtitle packets added before first A/V keyframe packet is found with seek.)
#define NUM_SUB_PREROLL_PACKETS 500
/**
* \brief ensures there is space for at least one additional element
* \param array array to grow
* \param nelem current number of elements in array
* \param elsize size of one array element
*/
static void *grow_array(void *array, int nelem, size_t elsize)
{
if (!(nelem & 31))
array = realloc(array, (nelem + 32) * elsize);
return array;
}
#define AAC_SYNC_EXTENSION_TYPE 0x02b7
static int aac_get_sample_rate_index(uint32_t sample_rate)
{
@@ -258,6 +244,12 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
zstream.avail_out = size;
int result;
do {
if (size >= INT_MAX - 4000) {
talloc_free(dest);
dest = NULL;
inflateEnd(&zstream);
goto error;
}
size += 4000;
dest = talloc_realloc_size(track->parser_tmp, dest, size);
zstream.next_out = (Bytef *) (dest + zstream.total_out);
@@ -279,6 +271,9 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
} else if (enc->comp_algo == 2) {
/* lzo encoded track */
int out_avail;
int maxlen = INT_MAX - AV_LZO_OUTPUT_PADDING;
if (size >= maxlen / 3)
goto error;
int dstlen = size * 3;
dest = NULL;
@@ -297,6 +292,11 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
goto error;
}
mp_dbg(log, "lzo decompression buffer too small.\n");
if (dstlen >= maxlen / 2) {
talloc_free(dest);
dest = NULL;
goto error;
}
dstlen *= 2;
}
size = dstlen - out_avail;
@@ -311,6 +311,8 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
error:
if (src != dest && src != orig_src)
talloc_free(src);
if (!size)
dest = NULL;
return (bstr){dest, size};
}
@@ -433,7 +435,7 @@ static void parse_trackencodings(struct demuxer *demuxer,
if (e.order >= ce[i].order)
break;
}
ce = talloc_realloc_size(track, ce, (n_enc + 1) * sizeof(*ce));
ce = talloc_realloc(track, ce, mkv_content_encoding_t, n_enc + 1);
memmove(ce + i + 1, ce + i, (n_enc - i) * sizeof(*ce));
memcpy(ce + i, &e, sizeof(e));
}
@@ -459,13 +461,11 @@ static void parse_trackaudio(struct demuxer *demuxer, struct mkv_track *track,
}
if (audio->n_bit_depth) {
track->a_bps = audio->bit_depth;
MP_VERBOSE(demuxer, "| + Bit depth: %u\n",
track->a_bps);
MP_VERBOSE(demuxer, "| + Bit depth: %u\n", track->a_bps);
}
if (audio->n_channels) {
track->a_channels = audio->channels;
MP_VERBOSE(demuxer, "| + Channels: %u\n",
track->a_channels);
MP_VERBOSE(demuxer, "| + Channels: %u\n", track->a_channels);
} else {
track->a_channels = 1;
}
@@ -476,38 +476,33 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
{
if (video->n_frame_rate) {
track->v_frate = video->frame_rate;
MP_VERBOSE(demuxer, "| + Frame rate: %f\n",
track->v_frate);
MP_VERBOSE(demuxer, "| + Frame rate: %f\n", track->v_frate);
if (track->v_frate > 0)
track->default_duration = 1 / track->v_frate;
}
if (video->n_display_width) {
track->v_dwidth = video->display_width;
track->v_dwidth_set = true;
MP_VERBOSE(demuxer, "| + Display width: %u\n",
track->v_dwidth);
MP_VERBOSE(demuxer, "| + Display width: %u\n", track->v_dwidth);
}
if (video->n_display_height) {
track->v_dheight = video->display_height;
track->v_dheight_set = true;
MP_VERBOSE(demuxer, "| + Display height: %u\n",
track->v_dheight);
MP_VERBOSE(demuxer, "| + Display height: %u\n", track->v_dheight);
}
if (video->n_pixel_width) {
track->v_width = video->pixel_width;
MP_VERBOSE(demuxer, "| + Pixel width: %u\n",
track->v_width);
MP_VERBOSE(demuxer, "| + Pixel width: %u\n", track->v_width);
}
if (video->n_pixel_height) {
track->v_height = video->pixel_height;
MP_VERBOSE(demuxer, "| + Pixel height: %u\n",
track->v_height);
MP_VERBOSE(demuxer, "| + Pixel height: %u\n", track->v_height);
}
if (video->n_colour_space && video->colour_space.len == 4) {
uint8_t *d = (uint8_t *)&video->colour_space.start[0];
track->colorspace = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
(unsigned int)track->colorspace);
(unsigned int)track->colorspace);
}
}
@@ -525,14 +520,13 @@ static void parse_trackentry(struct demuxer *demuxer,
struct ebml_track_entry *entry)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
struct mkv_track *track = talloc_zero_size(NULL, sizeof(*track));
track->last_index_entry = -1;
struct mkv_track *track = talloc_zero(NULL, struct mkv_track);
track->last_index_entry = (size_t)-1;
track->parser_tmp = talloc_new(track);
track->tnum = entry->track_number;
if (track->tnum) {
MP_VERBOSE(demuxer, "| + Track number: %u\n",
track->tnum);
MP_VERBOSE(demuxer, "| + Track number: %u\n", track->tnum);
} else {
MP_ERR(demuxer, "Missing track number!\n");
}
@@ -540,8 +534,7 @@ static void parse_trackentry(struct demuxer *demuxer,
if (entry->n_name) {
track->name = talloc_strndup(track, entry->name.start,
entry->name.len);
MP_VERBOSE(demuxer, "| + Name: %s\n",
track->name);
MP_VERBOSE(demuxer, "| + Name: %s\n", track->name);
}
track->type = entry->track_type;
@@ -577,35 +570,31 @@ static void parse_trackentry(struct demuxer *demuxer,
if (!strcmp(track->codec_id, MKV_V_MSCOMP)
|| !strcmp(track->codec_id, MKV_A_ACM))
track->ms_compat = 1;
MP_VERBOSE(demuxer, "| + Codec ID: %s\n",
track->codec_id);
MP_VERBOSE(demuxer, "| + Codec ID: %s\n", track->codec_id);
} else {
MP_ERR(demuxer, "Missing codec ID!\n");
track->codec_id = "";
}
if (entry->n_codec_private) {
if (entry->n_codec_private && entry->codec_private.len <= 0x10000000) {
int len = entry->codec_private.len;
track->private_data = talloc_size(track, len + AV_LZO_INPUT_PADDING);
memcpy(track->private_data, entry->codec_private.start, len);
track->private_size = len;
MP_VERBOSE(demuxer, "| + CodecPrivate, length %u\n",
track->private_size);
MP_VERBOSE(demuxer, "| + CodecPrivate, length %u\n", track->private_size);
}
if (entry->n_language) {
track->language = talloc_strndup(track, entry->language.start,
entry->language.len);
MP_VERBOSE(demuxer, "| + Language: %s\n",
track->language);
MP_VERBOSE(demuxer, "| + Language: %s\n", track->language);
} else {
track->language = talloc_strdup(track, "eng");
}
if (entry->n_flag_default) {
track->default_track = entry->flag_default;
MP_VERBOSE(demuxer, "| + Default flag: %u\n",
track->default_track);
MP_VERBOSE(demuxer, "| + Default flag: %u\n", track->default_track);
} else {
track->default_track = 1;
}
@@ -618,7 +607,7 @@ static void parse_trackentry(struct demuxer *demuxer,
if (!track->v_frate)
track->v_frate = 1e9 / entry->default_duration;
MP_VERBOSE(demuxer, "| + Default duration: %.3fms ( = %.3f fps)\n",
entry->default_duration / 1000000.0, track->v_frate);
entry->default_duration / 1000000.0, track->v_frate);
}
}
@@ -640,8 +629,8 @@ static int demux_mkv_read_tracks(demuxer_t *demuxer)
if (ebml_read_element(s, &parse_ctx, &tracks, &ebml_tracks_desc) < 0)
return -1;
mkv_d->tracks = talloc_size(mkv_d,
tracks.n_track_entry * sizeof(*mkv_d->tracks));
mkv_d->tracks = talloc_zero_array(mkv_d, struct mkv_track*,
tracks.n_track_entry);
for (int i = 0; i < tracks.n_track_entry; i++) {
MP_VERBOSE(demuxer, "| + a track...\n");
parse_trackentry(demuxer, &tracks.track_entry[i]);
@@ -655,8 +644,8 @@ static void cue_index_add(demuxer_t *demuxer, int track_id, uint64_t filepos,
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
mkv_d->indexes = grow_array(mkv_d->indexes, mkv_d->num_indexes,
sizeof(mkv_index_t));
MP_TARRAY_GROW(mkv_d, mkv_d->indexes, mkv_d->num_indexes);
mkv_d->indexes[mkv_d->num_indexes].tnum = track_id;
mkv_d->indexes[mkv_d->num_indexes].timecode = timecode;
mkv_d->indexes[mkv_d->num_indexes].filepos = filepos;
@@ -670,7 +659,7 @@ static void add_block_position(demuxer_t *demuxer, struct mkv_track *track,
if (mkv_d->index_complete || !track)
return;
if (track->last_index_entry >= 0) {
if (track->last_index_entry != (size_t)-1) {
mkv_index_t *index = &mkv_d->indexes[track->last_index_entry];
// filepos is always the cluster position, which can contain multiple
// blocks with different timecodes - one is enough.
@@ -781,8 +770,7 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
}
if (wanted_edition >= 0 && wanted_edition < num_editions) {
selected_edition = wanted_edition;
MP_VERBOSE(demuxer, "User-specified edition: %d\n",
selected_edition);
MP_VERBOSE(demuxer, "User-specified edition: %d\n", selected_edition);
} else {
for (int i = 0; i < num_editions; i++) {
if (wanted_edition_uid &&
@@ -811,10 +799,10 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
int warn_level = idx == selected_edition ? MSGL_WARN : MSGL_V;
if (editions[idx].n_edition_flag_default)
MP_VERBOSE(demuxer, "Default edition flag: %"PRIu64
"\n", editions[idx].edition_flag_default);
"\n", editions[idx].edition_flag_default);
if (editions[idx].n_edition_flag_ordered)
MP_VERBOSE(demuxer, "Ordered chapter flag: %"PRIu64
"\n", editions[idx].edition_flag_ordered);
"\n", editions[idx].edition_flag_ordered);
int chapter_count = editions[idx].n_chapter_atom;
@@ -831,8 +819,7 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
struct bstr name = { "(unnamed)", 9 };
if (!ca->n_chapter_time_start)
MP_MSG(demuxer, warn_level,
"Chapter lacks start time\n");
MP_MSG(demuxer, warn_level, "Chapter lacks start time\n");
chapter.start = ca->chapter_time_start;
chapter.end = ca->chapter_time_end;
@@ -841,8 +828,7 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
MP_MSG(demuxer, warn_level, "Multiple chapter "
"names not supported, picking first\n");
if (!ca->chapter_display[0].n_chap_string)
MP_MSG(demuxer, warn_level, "Malformed chapter "
"name entry\n");
MP_MSG(demuxer, warn_level, "Malformed chapter name entry\n");
else
name = ca->chapter_display[0].chap_string;
}
@@ -983,7 +969,7 @@ static int demux_mkv_read_attachments(demuxer_t *demuxer)
struct bstr mime = attachment->file_mime_type;
demuxer_add_attachment(demuxer, name, mime, attachment->file_data);
MP_VERBOSE(demuxer, "Attachment: %.*s, %.*s, %zu bytes\n",
BSTR_P(name), BSTR_P(mime), attachment->file_data.len);
BSTR_P(name), BSTR_P(mime), attachment->file_data.len);
}
talloc_free(parse_ctx.talloc_ctx);
@@ -1241,6 +1227,11 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
}
}
if (extradata_size > 0x1000000) {
MP_WARN(demuxer, "Invalid CodecPrivate\n");
return 1;
}
sh = new_sh_stream(demuxer, STREAM_VIDEO);
if (!sh)
return 1;
@@ -1465,6 +1456,8 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize)
goto error;
if (!track->ms_compat) {
if (track->private_size > 0x1000000)
goto error;
sh_a->wf->cbSize = track->private_size;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
@@ -1499,10 +1492,18 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
src += RAPROPERTIES5_SIZE;
}
if (track->audiopk_size == 0 || track->sub_packet_size == 0 ||
track->sub_packet_h == 0 || track->coded_framesize == 0)
goto error;
if (track->coded_framesize > 0x40000000)
goto error;
src += 3;
if (version == 5)
src++;
codecdata_length = AV_RB32(src);
if (codecdata_length < 0 || codecdata_length > 0x1000000)
goto error;
src += 4;
sh_a->wf->cbSize = codecdata_length;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
@@ -1527,9 +1528,9 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
sh_a->wf->nBlockAlign = track->coded_framesize;
audiobuf:
track->audio_buf =
talloc_size(demuxer, track->sub_packet_h * track->audiopk_size);
talloc_array_size(track, track->sub_packet_h, track->audiopk_size);
track->audio_timestamp =
talloc_size(demuxer, track->sub_packet_h * sizeof(double));
talloc_array(track, double, track->sub_packet_h);
break;
}
@@ -1631,6 +1632,9 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
}
}
if (track->private_size > 0x10000000)
return 1;
bstr in = (bstr){track->private_data, track->private_size};
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
if (!sh)
@@ -1654,10 +1658,8 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
sh->title = talloc_strdup(sh, track->name);
sh->default_track = track->default_track;
if (!subtitle_type) {
MP_ERR(demuxer, "Subtitle type '%s' is not supported.\n",
track->codec_id);
}
if (!subtitle_type)
MP_ERR(demuxer, "Subtitle type '%s' is not supported.\n", track->codec_id);
return 0;
}
@@ -1674,7 +1676,7 @@ static int read_ebml_header(demuxer_t *demuxer)
return 0;
if (ebml_master.doc_type.start == NULL) {
MP_VERBOSE(demuxer, "File has EBML header but no doctype."
" Assuming \"matroska\".\n");
" Assuming \"matroska\".\n");
} else if (bstrcmp(ebml_master.doc_type, bstr0("matroska")) != 0
&& bstrcmp(ebml_master.doc_type, bstr0("webm")) != 0) {
MP_DBG(demuxer, "no head found\n");
@@ -1683,8 +1685,8 @@ static int read_ebml_header(demuxer_t *demuxer)
}
if (ebml_master.doc_type_read_version > 2) {
MP_WARN(demuxer, "This looks like a Matroska file, "
"but we don't support format version %"PRIu64"\n",
ebml_master.doc_type_read_version);
"but we don't support format version %"PRIu64"\n",
ebml_master.doc_type_read_version);
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
@@ -1696,7 +1698,7 @@ static int read_ebml_header(demuxer_t *demuxer)
&& ebml_master.ebml_max_id_length != 4))
{
MP_WARN(demuxer, "This looks like a Matroska file, "
"but the header has bad parameters\n");
"but the header has bad parameters\n");
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
@@ -1982,71 +1984,88 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
dp->pos = mkv_d->last_filepos;
dp->keyframe = keyframe;
demuxer_add_packet(demuxer, track->stream, dp);
demux_add_packet(track->stream, dp);
}
static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
int sps = track->sub_packet_size;
int sph = track->sub_packet_h;
int cfs = track->coded_framesize;
int w = track->audiopk_size;
int spc = track->sub_packet_cnt;
uint16_t sps = track->sub_packet_size;
uint16_t sph = track->sub_packet_h;
uint32_t cfs = track->coded_framesize; // restricted to [1,0x40000000]
uint16_t w = track->audiopk_size;
uint16_t spc = track->sub_packet_cnt;
uint8_t *buffer = data.start;
uint32_t size = data.len;
demux_packet_t *dp;
// track->audio_buf allocation size
size_t audiobuf_size = sph * w;
if ((track->a_formattag == MP_FOURCC('2', '8', '_', '8'))
|| (track->a_formattag == MP_FOURCC('c', 'o', 'o', 'k'))
|| (track->a_formattag == MP_FOURCC('a', 't', 'r', 'c'))
|| (track->a_formattag == MP_FOURCC('s', 'i', 'p', 'r'))) {
// if(!block_bref)
// spc = track->sub_packet_cnt = 0;
|| (track->a_formattag == MP_FOURCC('s', 'i', 'p', 'r')))
{
switch (track->a_formattag) {
case MP_FOURCC('2', '8', '_', '8'):
for (int x = 0; x < sph / 2; x++)
memcpy(track->audio_buf + x * 2 * w + spc * cfs,
buffer + cfs * x, cfs);
for (int x = 0; x < sph / 2; x++) {
uint64_t dst_offset = x * 2 * w + spc * (uint64_t)cfs;
if (dst_offset + cfs > audiobuf_size)
goto error;
uint64_t src_offset = x * (uint64_t)cfs;
if (src_offset + cfs > size)
goto error;
memcpy(track->audio_buf + dst_offset, buffer + src_offset, cfs);
}
break;
case MP_FOURCC('c', 'o', 'o', 'k'):
case MP_FOURCC('a', 't', 'r', 'c'):
for (int x = 0; x < w / sps; x++)
memcpy(track->audio_buf +
sps * (sph * x + ((sph + 1) / 2) * (spc & 1) +
(spc >> 1)), buffer + sps * x, sps);
for (int x = 0; x < w / sps; x++) {
uint32_t dst_offset = sps * (sph * x + ((sph + 1) / 2) * (spc & 1)
+ (spc >> 1));
if (dst_offset + sps > audiobuf_size)
goto error;
uint32_t src_offset = sps * x;
if (src_offset + sps > size)
goto error;
memcpy(track->audio_buf + dst_offset, buffer + src_offset, sps);
}
break;
case MP_FOURCC('s', 'i', 'p', 'r'):
if (spc * w + w > audiobuf_size || w > size)
goto error;
memcpy(track->audio_buf + spc * w, buffer, w);
if (spc == sph - 1) {
int n;
int bs = sph * w * 2 / 96; // nibbles per subpacket
// Perform reordering
for (n = 0; n < 38; n++) {
int j;
int i = bs * sipr_swaps[n][0];
int o = bs * sipr_swaps[n][1];
// swap nibbles of block 'i' with 'o' TODO: optimize
for (j = 0; j < bs; j++) {
unsigned int i = bs * sipr_swaps[n][0]; // 77 max
unsigned int o = bs * sipr_swaps[n][1]; // 95 max
// swap nibbles of block 'i' with 'o'
for (int j = 0; j < bs; j++) {
if (i / 2 >= audiobuf_size || o / 2 >= audiobuf_size)
goto error;
int x = (i & 1) ?
(track->audio_buf[i >> 1] >> 4) :
(track->audio_buf[i >> 1] & 0x0F);
(track->audio_buf[i / 2] >> 4) :
(track->audio_buf[i / 2] & 0x0F);
int y = (o & 1) ?
(track->audio_buf[o >> 1] >> 4) :
(track->audio_buf[o >> 1] & 0x0F);
(track->audio_buf[o / 2] >> 4) :
(track->audio_buf[o / 2] & 0x0F);
if (o & 1)
track->audio_buf[o >> 1] =
(track->audio_buf[o >> 1] & 0x0F) | (x << 4);
track->audio_buf[o / 2] =
(track->audio_buf[o / 2] & 0x0F) | (x << 4);
else
track->audio_buf[o >> 1] =
(track->audio_buf[o >> 1] & 0xF0) | x;
track->audio_buf[o / 2] =
(track->audio_buf[o / 2] & 0xF0) | x;
if (i & 1)
track->audio_buf[i >> 1] =
(track->audio_buf[i >> 1] & 0x0F) | (y << 4);
track->audio_buf[i / 2] =
(track->audio_buf[i / 2] & 0x0F) | (y << 4);
else
track->audio_buf[i >> 1] =
(track->audio_buf[i >> 1] & 0xF0) | y;
track->audio_buf[i / 2] =
(track->audio_buf[i / 2] & 0xF0) | y;
++i;
++o;
}
@@ -2060,8 +2079,11 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
if (track->sub_packet_cnt == 0)
track->audio_filepos = mkv_d->last_filepos;
if (++(track->sub_packet_cnt) == sph) {
int apk_usize = track->stream->audio->wf->nBlockAlign;
track->sub_packet_cnt = 0;
// apk_usize has same range as coded_framesize in worst case
uint32_t apk_usize = track->stream->audio->wf->nBlockAlign;
if (apk_usize > audiobuf_size)
goto error;
// Release all the audio packets
for (int x = 0; x < sph * w / apk_usize; x++) {
dp = new_demux_packet_from(track->audio_buf + x * apk_usize,
@@ -2072,7 +2094,7 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
track->audio_timestamp[x * apk_usize / w];
dp->pos = track->audio_filepos; // all equal
dp->keyframe = !x; // Mark first packet as keyframe
demuxer_add_packet(demuxer, track->stream, dp);
demux_add_packet(track->stream, dp);
}
}
} else { // Not a codec that requires reordering
@@ -2085,8 +2107,11 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
dp->pos = mkv_d->last_filepos;
dp->keyframe = keyframe;
demuxer_add_packet(demuxer, track->stream, dp);
demux_add_packet(track->stream, dp);
}
return;
error:
MP_ERR(demuxer, "RealAudio decrypting error.\n");
}
static void mkv_seek_reset(demuxer_t *demuxer)
@@ -2154,6 +2179,9 @@ static int libav_parse_wavpack(mkv_track_t *track, uint8_t *src,
if (blocksize > srclen)
goto fail;
if (dstlen > 0x10000000 || blocksize > 0x10000000)
goto fail;
tmp = talloc_realloc(track->parser_tmp, dst, uint8_t,
dstlen + blocksize + 32);
if (!tmp)
@@ -2223,7 +2251,7 @@ static bool mkv_parse_packet(mkv_track_t *track, bstr *raw, bstr *out)
int len = av_parser_parse2(track->av_parser, track->av_parser_codec,
&data, &size, raw->start, raw->len,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (len < 0)
if (len < 0 || len > 0x10000000)
return false;
*raw = bstr_cut(*raw, len);
if (size) {
@@ -2334,7 +2362,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
uint32_t lace_size[MAX_NUM_LACES];
bool use_this_block = tc >= mkv_d->skip_to_timecode;
if (!demuxer_stream_is_selected(demuxer, stream))
if (!demux_stream_is_selected(stream))
return 0;
if (demux_mkv_read_block_lacing(&data, &laces, lace_size)) {
@@ -2398,7 +2426,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
if (track->ms_compat)
MPSWAP(double, dp->pts, dp->dts);
dp->duration = block_duration / 1e9;
demuxer_add_packet(demuxer, stream, dp);
demux_add_packet(stream, dp);
}
talloc_free_children(track->parser_tmp);
}
@@ -2640,7 +2668,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
min_diff = -min_diff;
min_diff = FFMAX(min_diff, 1);
for (int i = 0; i < mkv_d->num_indexes; i++) {
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
target_timecode -
@@ -2661,7 +2689,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
uint64_t seek_pos = index->filepos;
if (flags & SEEK_SUBPREROLL) {
uint64_t prev_target = 0;
for (int i = 0; i < mkv_d->num_indexes; i++) {
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
uint64_t index_pos = mkv_d->indexes[i].filepos;
if (index_pos > prev_target && index_pos < seek_pos)
@@ -2678,7 +2706,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
return index;
}
static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
int64_t old_pos = stream_tell(demuxer->stream);
@@ -2688,7 +2716,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
mkv_seek_reset(demuxer);
for (int i = 0; i < mkv_d->num_tracks; i++) {
mkv_track_t *track = mkv_d->tracks[i];
if (demuxer_stream_is_selected(demuxer, track->stream)) {
if (demux_stream_is_selected(track->stream)) {
st_active[track->stream->type] = true;
if (track->type == MATROSKA_TRACK_VIDEO)
v_tnum = track->tnum;
@@ -2745,7 +2773,6 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
stream_t *s = demuxer->stream;
uint64_t target_filepos;
mkv_index_t *index = NULL;
int i;
read_deferred_cues(demuxer);
@@ -2759,7 +2786,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
stream_control(s, STREAM_CTRL_GET_SIZE, &size);
target_filepos = (uint64_t) (size * rel_seek_secs);
for (i = 0; i < mkv_d->num_indexes; i++)
for (size_t i = 0; i < mkv_d->num_indexes; i++)
if (mkv_d->indexes[i].tnum == v_tnum)
if ((index == NULL)
|| ((mkv_d->indexes[i].filepos >= target_filepos)
@@ -2807,7 +2834,6 @@ static void mkv_free(struct demuxer *demuxer)
mkv_seek_reset(demuxer);
for (int i = 0; i < mkv_d->num_tracks; i++)
demux_mkv_free_trackentry(mkv_d->tracks[i]);
free(mkv_d->indexes);
}
const demuxer_desc_t demuxer_desc_matroska = {
+2
View File
@@ -15,6 +15,8 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <strings.h>
#include "common/common.h"
#include "options/options.h"
#include "common/msg.h"
+2 -2
View File
@@ -232,12 +232,12 @@ static int raw_fill_buffer(demuxer_t *demuxer)
int len = stream_read(demuxer->stream, dp->buffer, dp->len);
demux_packet_shorten(dp, len);
demuxer_add_packet(demuxer, demuxer->streams[0], dp);
demux_add_packet(demuxer->streams[0], dp);
return 1;
}
static void raw_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
static void raw_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
{
struct priv *p = demuxer->priv;
stream_t *s = demuxer->stream;
+51 -3
View File
@@ -26,7 +26,6 @@
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <libavutil/common.h>
#include <libavutil/avstring.h>
@@ -112,6 +111,55 @@ struct readline_args {
/* Maximal length of line of a subtitle */
#define LINE_LEN 1000
static double demux_packet_list_duration(struct demux_packet **pkts, int num_pkts)
{
if (num_pkts > 0)
return pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration;
return 0;
}
static void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts,
int *current, double rel_seek_secs, int flags)
{
double ref_time = 0;
if (*current >= 0 && *current < num_pkts) {
ref_time = pkts[*current]->pts;
} else if (*current == num_pkts && num_pkts > 0) {
ref_time = pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration;
}
if (flags & SEEK_ABSOLUTE)
ref_time = 0;
if (flags & SEEK_FACTOR) {
ref_time += demux_packet_list_duration(pkts, num_pkts) * rel_seek_secs;
} else {
ref_time += rel_seek_secs;
}
// Could do binary search, but it's probably not worth the complexity.
int last_index = 0;
for (int n = 0; n < num_pkts; n++) {
if (pkts[n]->pts > ref_time)
break;
last_index = n;
}
*current = last_index;
}
static struct demux_packet *demux_packet_list_fill(struct demux_packet **pkts,
int num_pkts, int *current)
{
if (*current < 0)
*current = 0;
if (*current >= num_pkts)
return NULL;
struct demux_packet *new = talloc(NULL, struct demux_packet);
*new = *pkts[*current];
*current += 1;
return new;
}
static int eol(char p) {
return p=='\r' || p=='\n' || p=='\0';
}
@@ -752,10 +800,10 @@ static int d_fill_buffer(struct demuxer *demuxer)
struct priv *p = demuxer->priv;
struct demux_packet *dp = demux_packet_list_fill(p->pkts, p->num_pkts,
&p->current);
return demuxer_add_packet(demuxer, p->sh, dp);
return demux_add_packet(p->sh, dp);
}
static void d_seek(struct demuxer *demuxer, float secs, int flags)
static void d_seek(struct demuxer *demuxer, double secs, int flags)
{
struct priv *p = demuxer->priv;
demux_packet_list_seek(p->pkts, p->num_pkts, &p->current, secs, flags);
+249
View File
@@ -0,0 +1,249 @@
#include <assert.h>
#include "common/common.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "options/options.h"
#include "demux.h"
#include "audio/format.h"
#include "video/img_fourcc.h"
#include "stream/stream.h"
#include "stream/tv.h"
static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
{
tvi_handle_t *tvh;
sh_video_t *sh_video;
sh_audio_t *sh_audio = NULL;
const tvi_functions_t *funcs;
if (check > DEMUX_CHECK_REQUEST || demuxer->stream->type != STREAMTYPE_TV)
return -1;
tv_param_t *params = m_sub_options_copy(demuxer, &tv_params_conf,
demuxer->opts->tv_params);
struct tv_stream_params *sparams = demuxer->stream->priv;
if (sparams->channel && sparams->channel[0]) {
talloc_free(params->channel);
params->channel = talloc_strdup(NULL, sparams->channel);
}
if (sparams->input >= 0)
params->input = sparams->input;
assert(demuxer->priv==NULL);
if(!(tvh=tv_begin(params, demuxer->log))) return -1;
if (!tvh->functions->init(tvh->priv)) return -1;
tvh->demuxer = demuxer;
if (!open_tv(tvh)){
tv_uninit(tvh);
return -1;
}
funcs = tvh->functions;
demuxer->priv=tvh;
struct sh_stream *sh_v = new_sh_stream(demuxer, STREAM_VIDEO);
sh_video = sh_v->video;
/* get IMAGE FORMAT */
int fourcc;
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &fourcc);
if (fourcc == MP_FOURCC_MJPEG) {
sh_v->codec = "mjpeg";
} else {
sh_v->codec = "rawvideo";
sh_v->format = fourcc;
}
/* set FPS and FRAMETIME */
if(!sh_video->fps)
{
float tmp;
if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &tmp) != TVI_CONTROL_TRUE)
sh_video->fps = 25.0f; /* on PAL */
else sh_video->fps = tmp;
}
if (tvh->tv_param->fps != -1.0f)
sh_video->fps = tvh->tv_param->fps;
/* If playback only mode, go to immediate mode, fail silently */
if(tvh->tv_param->immediate == 1)
{
funcs->control(tvh->priv, TVI_CONTROL_IMMEDIATE, 0);
tvh->tv_param->audio = 0;
}
/* set width */
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_video->disp_w);
/* set height */
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_video->disp_h);
demuxer->seekable = 0;
/* here comes audio init */
if (tvh->tv_param->audio && funcs->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
{
int audio_format;
/* yeah, audio is present */
funcs->control(tvh->priv, TVI_CONTROL_AUD_SET_SAMPLERATE,
&tvh->tv_param->audiorate);
if (funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_FORMAT, &audio_format) != TVI_CONTROL_TRUE)
goto no_audio;
switch(audio_format)
{
case AF_FORMAT_U8:
case AF_FORMAT_S8:
case AF_FORMAT_U16_LE:
case AF_FORMAT_U16_BE:
case AF_FORMAT_S16_LE:
case AF_FORMAT_S16_BE:
case AF_FORMAT_S32_LE:
case AF_FORMAT_S32_BE:
break;
case AF_FORMAT_MPEG2:
default:
MP_ERR(tvh, "Audio type '%s' unsupported!\n",
af_fmt_to_str(audio_format));
goto no_audio;
}
struct sh_stream *sh_a = new_sh_stream(demuxer, STREAM_AUDIO);
sh_audio = sh_a->audio;
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLERATE,
&sh_audio->samplerate);
int nchannels = sh_audio->channels.num;
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS,
&nchannels);
mp_chmap_from_channels(&sh_audio->channels, nchannels);
sh_a->codec = "mp-pcm";
sh_a->format = audio_format;
int samplesize = af_fmt2bps(audio_format);
int block_align = samplesize * sh_audio->channels.num;
int bytes_per_second = sh_audio->samplerate * block_align;
sh_audio->bitrate = bytes_per_second * 8;
// emulate WF for win32 codecs:
sh_audio->wf = talloc_zero(sh_audio, MP_WAVEFORMATEX);
sh_audio->wf->wFormatTag = sh_a->format;
sh_audio->wf->nChannels = sh_audio->channels.num;
sh_audio->wf->wBitsPerSample = samplesize * 8;
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
sh_audio->wf->nBlockAlign = block_align;
sh_audio->wf->nAvgBytesPerSec = bytes_per_second;
MP_VERBOSE(tvh, " TV audio: %d channels, %d bits, %d Hz\n",
sh_audio->wf->nChannels, sh_audio->wf->wBitsPerSample,
sh_audio->wf->nSamplesPerSec);
}
no_audio:
if(!(funcs->start(tvh->priv))){
// start failed :(
tv_uninit(tvh);
return -1;
}
/* set color eq */
tv_set_color_options(tvh, TV_COLOR_BRIGHTNESS, tvh->tv_param->brightness);
tv_set_color_options(tvh, TV_COLOR_HUE, tvh->tv_param->hue);
tv_set_color_options(tvh, TV_COLOR_SATURATION, tvh->tv_param->saturation);
tv_set_color_options(tvh, TV_COLOR_CONTRAST, tvh->tv_param->contrast);
if(tvh->tv_param->gain!=-1)
if(funcs->control(tvh->priv,TVI_CONTROL_VID_SET_GAIN,&tvh->tv_param->gain)!=TVI_CONTROL_TRUE)
MP_WARN(tvh, "Unable to set gain control!\n");
return 0;
}
static void demux_close_tv(demuxer_t *demuxer)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demuxer->priv);
if (!tvh) return;
tv_uninit(tvh);
free(tvh);
demuxer->priv=NULL;
}
static int demux_tv_fill_buffer(demuxer_t *demux)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demux->priv);
demux_packet_t* dp;
unsigned int len=0;
struct sh_stream *want_audio = NULL, *want_video = NULL;
for (int n = 0; n < demux->num_streams; n++) {
struct sh_stream *sh = demux->streams[n];
if (!demux_has_packet(sh) && demux_stream_is_selected(sh)) {
if (sh->type == STREAM_AUDIO)
want_audio = sh;
if (sh->type == STREAM_VIDEO)
want_video = sh;
}
}
/* ================== ADD AUDIO PACKET =================== */
if (want_audio && tvh->tv_param->audio &&
tvh->functions->control(tvh->priv,
TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
{
len = tvh->functions->get_audio_framesize(tvh->priv);
dp=new_demux_packet(len);
dp->keyframe = true;
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
demux_add_packet(want_audio, dp);
}
/* ================== ADD VIDEO PACKET =================== */
if (want_video && tvh->functions->control(tvh->priv,
TVI_CONTROL_IS_VIDEO, 0) == TVI_CONTROL_TRUE)
{
len = tvh->functions->get_video_framesize(tvh->priv);
dp=new_demux_packet(len);
dp->keyframe = true;
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
demux_add_packet(want_video, dp);
}
if (tvh->tv_param->scan) tv_scan(tvh);
return 1;
}
static int demux_tv_control(demuxer_t *demuxer, int cmd, void *arg)
{
tvi_handle_t *tvh=(tvi_handle_t*)(demuxer->priv);
if (cmd != DEMUXER_CTRL_STREAM_CTRL)
return DEMUXER_CTRL_NOTIMPL;
struct demux_ctrl_stream_ctrl *ctrl = arg;
ctrl->res = tv_stream_control(tvh, ctrl->ctrl, ctrl->arg);
return DEMUXER_CTRL_OK;
}
const demuxer_desc_t demuxer_desc_tv = {
.name = "tv",
.desc = "TV card demuxer",
.fill_buffer = demux_tv_fill_buffer,
.control = demux_tv_control,
.open = demux_open_tv,
.close = demux_close_tv,
};
+15 -17
View File
@@ -37,10 +37,6 @@
#include "compat/mpbswap.h"
#include "common/msg.h"
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
// Whether the id is a known Matroska level 1 element (allowed as element on
// global file level, after the level 0 MATROSKA_ID_SEGMENT).
// This (intentionally) doesn't include "global" elements.
@@ -364,7 +360,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
char *s = target;
uint8_t *end = data + size;
uint8_t *p = data;
int num_elems[MAX_EBML_SUBELEMENTS] = {};
int num_elems[MAX_EBML_SUBELEMENTS] = {0};
while (p < end) {
uint8_t *startp = p;
int len;
@@ -390,6 +386,10 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
if (type->fields[i].id == id) {
field_idx = i;
num_elems[i]++;
if (num_elems[i] >= 0x70000000) {
MP_ERR(ctx, "Too many EBML subelements.\n");
goto other_error;
}
break;
}
@@ -409,8 +409,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
continue;
past_end_error:
MP_DBG(ctx, "Subelement headers go "
"past end of containing element\n");
MP_DBG(ctx, "Subelement headers go past end of containing element\n");
other_error:
ctx->has_errors = true;
end = startp;
@@ -532,8 +531,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
subelptr = (fieldtype *) fieldptr
GETPTR(uintptr, uint64_t);
if (length < 1 || length > 8) {
MP_DBG(ctx, "uint invalid length %"PRIu64
"\n", length);
MP_DBG(ctx, "uint invalid length %"PRIu64"\n", length);
goto error;
}
*uintptr = ebml_parse_uint(data, length);
@@ -544,8 +542,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
int64_t *sintptr;
GETPTR(sintptr, int64_t);
if (length < 1 || length > 8) {
MP_DBG(ctx, "sint invalid length %"PRIu64
"\n", length);
MP_DBG(ctx, "sint invalid length %"PRIu64"\n", length);
goto error;
}
*sintptr = ebml_parse_sint(data, length);
@@ -556,8 +553,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
double *floatptr;
GETPTR(floatptr, double);
if (length != 4 && length != 8) {
MP_DBG(ctx, "float invalid length %"PRIu64
"\n", length);
MP_DBG(ctx, "float invalid length %"PRIu64"\n", length);
goto error;
}
*floatptr = ebml_parse_float(data, length);
@@ -566,16 +562,18 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
case EBML_TYPE_STR:
case EBML_TYPE_BINARY:;
if (length > 0x80000000) {
MP_ERR(ctx, "Not reading overly long EBML element.\n");
break;
}
struct bstr *strptr;
GETPTR(strptr, struct bstr);
strptr->start = data;
strptr->len = length;
if (ed->type == EBML_TYPE_STR)
MP_DBG(ctx, "string \"%.*s\"\n",
BSTR_P(*strptr));
MP_DBG(ctx, "string \"%.*s\"\n", BSTR_P(*strptr));
else
MP_DBG(ctx, "binary %zd bytes\n",
strptr->len);
MP_DBG(ctx, "binary %zd bytes\n", strptr->len);
break;
case EBML_TYPE_EBML_ID:;
-12
View File
@@ -28,7 +28,6 @@
struct mp_log;
/* EBML version supported */
#define EBML_VERSION 1
@@ -69,7 +68,6 @@ struct ebml_parse_ctx {
#define EBML_ID_INVALID 0xffffffff
/* matroska track types */
#define MATROSKA_TRACK_VIDEO 0x01 /* rectangle-shaped pictures aka video */
#define MATROSKA_TRACK_AUDIO 0x02 /* anything you can hear */
@@ -78,18 +76,8 @@ struct ebml_parse_ctx {
#define MATROSKA_TRACK_SUBTITLE 0x11 /* text-subtitles */
#define MATROSKA_TRACK_CONTROL 0x20 /* control-codes for menu or other stuff*/
#ifndef UINT64_MAX
#define UINT64_MAX 18446744073709551615ULL
#endif
#ifndef INT64_MAX
#define INT64_MAX 9223372036854775807LL
#endif
#define EBML_UINT_INVALID UINT64_MAX
#define EBML_INT_INVALID INT64_MAX
#define EBML_FLOAT_INVALID -1000000000.0
bool ebml_is_mkv_level1_id(uint32_t id);
uint32_t ebml_read_id (stream_t *s);
+2 -2
View File
@@ -16,7 +16,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -30,6 +29,7 @@
#include "talloc.h"
#include "common/msg.h"
#include "stream/stream.h"
#include "misc/ctype.h"
#include "options/path.h"
#include "mf.h"
@@ -55,7 +55,7 @@ mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filename)
while (fgets(fname, 512, lst_f)) {
/* remove spaces from end of fname */
char *t = fname + strlen(fname) - 1;
while (t > fname && isspace((unsigned char)*t))
while (t > fname && mp_isspace(*t))
*(t--) = 0;
if (!mp_path_exists(fname)) {
mp_verbose(log, "file not found: '%s'\n", fname);
+122
View File
@@ -0,0 +1,122 @@
/*
* 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 <stdio.h>
#include <string.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
#include "common/av_common.h"
#include "common/common.h"
#include "packet.h"
static void packet_destroy(void *ptr)
{
struct demux_packet *dp = ptr;
talloc_free(dp->avpacket);
av_free(dp->allocation);
}
static struct demux_packet *create_packet(size_t len)
{
if (len > 1000000000) {
fprintf(stderr, "Attempt to allocate demux packet over 1 GB!\n");
abort();
}
struct demux_packet *dp = talloc(NULL, struct demux_packet);
talloc_set_destructor(dp, packet_destroy);
*dp = (struct demux_packet) {
.len = len,
.pts = MP_NOPTS_VALUE,
.dts = MP_NOPTS_VALUE,
.duration = -1,
.pos = -1,
.stream = -1,
};
return dp;
}
struct demux_packet *new_demux_packet(size_t len)
{
struct demux_packet *dp = create_packet(len);
dp->buffer = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE);
if (!dp->buffer) {
fprintf(stderr, "Memory allocation failure!\n");
abort();
}
memset(dp->buffer + len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
dp->allocation = dp->buffer;
return dp;
}
// data must already have suitable padding, and does not copy the data
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len)
{
struct demux_packet *dp = create_packet(len);
dp->buffer = data;
return dp;
}
struct demux_packet *new_demux_packet_from(void *data, size_t len)
{
struct demux_packet *dp = new_demux_packet(len);
memcpy(dp->buffer, data, len);
return dp;
}
void demux_packet_shorten(struct demux_packet *dp, size_t len)
{
assert(len <= dp->len);
dp->len = len;
memset(dp->buffer + dp->len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
}
void free_demux_packet(struct demux_packet *dp)
{
talloc_free(dp);
}
static void destroy_avpacket(void *pkt)
{
av_free_packet(pkt);
}
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
{
struct demux_packet *new = NULL;
if (dp->avpacket) {
assert(dp->buffer == dp->avpacket->data);
assert(dp->len == dp->avpacket->size);
AVPacket *newavp = talloc_zero(NULL, AVPacket);
talloc_set_destructor(newavp, destroy_avpacket);
av_init_packet(newavp);
if (av_packet_ref(newavp, dp->avpacket) < 0)
abort();
new = new_demux_packet_fromdata(newavp->data, newavp->size);
new->avpacket = newavp;
}
if (!new) {
new = new_demux_packet(dp->len);
memcpy(new->buffer, dp->buffer, new->len);
}
new->pts = dp->pts;
new->dts = dp->dts;
new->duration = dp->duration;
return new;
}
+9 -1
View File
@@ -20,6 +20,7 @@
#define MPLAYER_DEMUX_PACKET_H
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
// Holds one packet/frame/whatever
@@ -28,7 +29,6 @@ typedef struct demux_packet {
double pts;
double dts;
double duration;
double stream_pts;
int64_t pos; // position in source file byte stream
unsigned char *buffer;
bool keyframe;
@@ -38,4 +38,12 @@ typedef struct demux_packet {
struct AVPacket *avpacket; // original libavformat packet (demux_lavf)
} demux_packet_t;
struct demux_packet *new_demux_packet(size_t len);
// data must already have suitable padding
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
struct demux_packet *new_demux_packet_from(void *data, size_t len);
void demux_packet_shorten(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);
struct demux_packet *demux_copy_packet(struct demux_packet *dp);
#endif /* MPLAYER_DEMUX_PACKET_H */
+1 -1
View File
@@ -31,7 +31,6 @@ struct demuxer;
struct sh_stream {
enum stream_type type;
struct demuxer *demuxer;
// Index into demuxer->streams.
int index;
// Demuxer/format specific ID. Corresponds to the stream IDs as encoded in
@@ -73,6 +72,7 @@ typedef struct sh_audio {
// note codec extradata may be either under "wf" or "codecdata"
unsigned char *codecdata;
int codecdata_len;
struct replaygain_data *replaygain_data;
} sh_audio_t;
typedef struct sh_video {
+11 -5
View File
@@ -2,7 +2,7 @@
# mpv configuration file
#
# Configuration files are read system-wide from /usr/local/etc/mpv.conf
# and per-user from ~/.mpv/config, where per-user settings override
# and per-user from ~/.config/mpv/mpv.conf, where per-user settings override
# system-wide settings, all of which are overridden by the command line.
#
# Configuration file settings and the command line options use the same
@@ -80,11 +80,17 @@
# Use 8MB input cache for everything, even local files.
#cache=8192
#
# Prefill 20% of the cache before starting playback.
#cache-min=20.0
# If a seek is issued, and the target is 1024KB past the cached range, then
# keep reading until the seek target is hit, instead of doing a real seek.
#cache-seek-min=1024
#
# Prefill 50% of the cache before restarting playback after the cache emptied.
#cache-seek-min=50
# Disable the behavior that the player will pause if the cache goes below a
# certain fill size.
#cache-pause-below=no
#
# Enable demuxer-based cache. This reads ahead a number of packets, and the
# player won't freeze completely if network is slow during playback.
#demuxer-thread=yes
# Display English subtitles if available.
#slang=en
+45 -52
View File
@@ -1,43 +1,29 @@
# mpv input control file
# mpv keybindings
#
# You are able to redefine default keyboard/joystick/mouse/LIRC bindings, or
# add new ones here.
# See DOCS/man/input.rst for possible commands that can be bound.
# Also see mpv --input-cmdlist for other possible options.
# The file should be placed in the $HOME/.mpv directory.
#
# mpv --input-test --force-window --idle can be used to test which commands
# keys are bound to.
#
# If you wish to unbind a key, bind it to the 'ignore' command:
# KEY ignore
#
# Note that merely removing default key bindings from this file won't remove
# the default bindings mpv was compiled with, unless
# --no-input-default-bindings
# is specified.
# Location of user-defined bindings: ~/.config/mpv/input.conf
#
# Lines starting with # are comments. Use SHARP to assign the # key.
# Copy this file and uncomment and edit the bindings you want to change.
#
# List of commands and further details: DOCS/man/input.rst
# List of special keys: --input-keylist
# Keybindings testing mode: mpv --input-test --force-window --idle
#
# Use 'ignore' to unbind a key fully (e.g. 'ctrl+a ignore').
#
# Strings need to be quoted and escaped:
# KEY show_text "This is a single backslash: \\ and a quote: \" !"
#
# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
# modifiers Shift, Ctrl, Alt and Meta, but note that currently reading
# key combinations is only supported through the video windows of certain
# output drivers (not in output windows of other drivers or in a terminal).
# All lines in this file are commented. If you want to remap a key, copy the
# file to ~/.mpv/input.conf, and uncomment and edit the binding you want to
# change.
# the modifiers Shift, Ctrl, Alt and Meta (may not work on the terminal).
#
# The default keybindings are hardcoded into the mpv binary.
# You can disable them completely with: --no-input-default-bindings
# Developer note:
# etc/input.conf from the mpv source tree is baked into the mpv binary, with
# all entries magically uncommented and parsed on initialization. Thus, this
# file defines the mpv default key bindings. If this file is read as normal
# config file, all lines starting with '#' are treated as comments (and this
# file, if unedited, doesn't define any bindings).
# Lines that have a space after the '#' are always ignored as real comments.
# On compilation, this file is baked into the mpv binary, and all lines are
# uncommented (unless '#' is followed by a space) - thus this file defines the
# default key bindings.
# If this is enabled, treat all the following bindings as default.
#default-bindings start
@@ -59,8 +45,8 @@
#AXIS_RIGHT seek -5
## Seek units are in seconds, but note that these are limited by keyframes
#RIGHT seek 10
#LEFT seek -10
#RIGHT seek 5
#LEFT seek -5
#UP seek 60
#DOWN seek -60
# Do smaller, always exact (non-keyframe-limited), seeks with shift.
@@ -69,10 +55,10 @@
#Shift+LEFT no-osd seek -1 - exact
#Shift+UP no-osd seek 5 - exact
#Shift+DOWN no-osd seek -5 - exact
#PGUP seek 600
#PGDWN seek -600
#+ add audio-delay 0.100 # this changes audio/video sync
#- add audio-delay -0.100
#PGUP add chapter 1 # skip to next chapter
#PGDWN add chapter -1 # skip to previous chapter
#Shift+PGUP seek 600
#Shift+PGDWN seek -600
#[ multiply speed 0.9091 # scale playback speed
#] multiply speed 1.1
#{ multiply speed 0.5
@@ -90,17 +76,18 @@
#> playlist_next # skip to next file
#ENTER playlist_next force # skip to next file or quit
#< playlist_prev # skip to previous file
#o osd # cycle through OSD mode
#I show_text "${filename}" # display filename in osd
#O osd # cycle through OSD mode
#o show_progress
#P show_progress
#I show_text "${filename}" # display filename in osd
#z add sub-delay -0.1 # subtract 100 ms delay from subs
#x add sub-delay +0.1 # add
#ctrl++ add audio-delay 0.100 # this changes audio/video sync
#ctrl+- add audio-delay -0.100
#9 add volume -1
#/ add volume -1
#0 add volume 1
#* add volume 1
#( add balance -0.1 # adjust audio balance in favor of left
#) add balance 0.1 # right
#m cycle mute
#1 add contrast -1
#2 add contrast 1
@@ -111,10 +98,8 @@
#7 add saturation -1
#8 add saturation 1
#d cycle framedrop # cycle through framedrop modes
# toggle deinterlacer; requires either vdpau output, -vf yadif or kerndeint
# toggle deinterlacer (automatically inserts or removes required filter)
#D cycle deinterlace
#c cycle colormatrix
# Next 3 currently only work with --no-ass
#r add sub-pos -1 # move subtitles up
#t add sub-pos +1 # down
#v cycle sub-visibility
@@ -122,10 +107,8 @@
#V cycle ass-vsfilter-aspect-compat
#j cycle sub # cycle through subtitles
#J cycle sub down # ...backwards
#F cycle sub-forced-only
#SHARP cycle audio # switch audio streams
#_ cycle video
#TAB cycle program
#T cycle ontop # toggle video window ontop of other windows
#f cycle fullscreen # toggle fullscreen
#s screenshot # take a screenshot
@@ -148,14 +131,7 @@
#MUTE cycle mute
#CLOSE_WIN quit
#CLOSE_WIN {encode} quit
#! add chapter -1 # skip to previous chapter
#@ add chapter 1 # next
#E cycle edition # next edition
#A cycle angle
#U stop
#l cycle quvi-format 1
#L cycle quvi-format -1
# Apple Remote section
#AR_PLAY cycle pause
@@ -205,6 +181,12 @@
#h cycle tv-channel -1 # previous channel
#k cycle tv-channel +1 # next channel
#
# Legacy bindings (may or may not be removed in the future)
#
#! add chapter -1 # skip to previous chapter
#@ add chapter 1 # next
#
# Not assigned by default
# (not an exhaustive list of unbound commands)
@@ -215,3 +197,14 @@
# ? sub_step -1 # immediately display next subtitle
# ? sub_step +1 # previous
# ? cycle_values window-scale 0.5 2 1 # switch between 1/2, 2x, unresized window size
# ? cycle colormatrix
# ? add audio-delay 0.100 # this changes audio/video sync
# ? add audio-delay -0.100
# ? cycle angle # switch DVD/Bluray angle
# ? add balance -0.1 # adjust audio balance in favor of left
# ? add balance 0.1 # right
# ? cycle sub-forced-only # toggle DVD forced subs
# ? cycle program # cycle transport stream programs
# ? stop # stop playback (quit or enter idle mode)
# ? cycle quvi-format 1 # cycle streaming video quality (semi-broken)
# ? cycle quvi-format -1
+153
View File
@@ -0,0 +1,153 @@
##
## MPlayer-style key bindings
##
## Save it as ~/.config/input.conf to use it.
##
## Generally, it's recommended to use this as reference-only.
##
RIGHT seek +10
LEFT seek -10
DOWN seek -60
UP seek +60
PGUP seek 600
PGDWN seek -600
m cycle mute
# cycle audio # switch audio streams
+ add audio-delay 0.100
= add audio-delay 0.100
- add audio-delay -0.100
[ multiply speed 0.9091 # scale playback speed
] multiply speed 1.1
{ multiply speed 0.5
} multiply speed 2.0
BS set speed 1.0 # reset speed to normal
q quit
ESC quit
ENTER playlist_next force # skip to next file
p cycle pause
. frame_step # advance one frame and pause
SPACE cycle pause
HOME set playlist-pos 1 # not the same as MPlayer
#END pt_up_step -1
> playlist_next # skip to next file
< playlist_prev # previous
#INS alt_src_step 1
#DEL alt_src_step -1
o osd
I show_text "${filename}" # display filename in osd
P show_progress
z add sub-delay -0.1 # subtract 100 ms delay from subs
x add sub-delay +0.1 # add
9 add volume -1
/ add volume -1
0 add volume 1
* add volume 1
1 add contrast -1
2 add contrast 1
3 add brightness -1
4 add brightness 1
5 add hue -1
6 add hue 1
7 add saturation -1
8 add saturation 1
( add balance -0.1 # adjust audio balance in favor of left
) add balance +0.1 # right
d cycle framedrop
D cycle deinterlace # toggle deinterlacer (auto-inserted filter)
r add sub-pos -1 # move subtitles up
t add sub-pos +1 # down
#? sub_step +1 # immediately display next subtitle
#? sub_step -1 # previous
#? add sub-scale +0.1 # increase subtitle font size
#? add sub-scale -0.1 # decrease subtitle font size
f cycle fullscreen
T cycle ontop # toggle video window ontop of other windows
w add panscan -0.1 # zoom out with -panscan 0 -fs
e add panscan +0.1 # in
c cycle stream-capture # save (and append) file/stream to stream.dump with -capture
s screenshot # take a screenshot (if you want PNG, use "--screenshot-format=png")
S screenshot - each-frame # S will take a png screenshot of every frame
h cycle tv-channel 1
l cycle tv-channel -1
n cycle tv-norm
#b tv_step_chanlist
##
## Joystick section
## WARNING: joystick support has to be explicitly enabled at
## compiletime with --enable-joystick
##
JOY_RIGHT seek 10
JOY_LEFT seek -10
JOY_UP seek 60
JOY_DOWN seek -60
JOY_BTN0 cycle pause
JOY_BTN1 osd
JOY_BTN2 add volume 1
JOY_BTN3 add volume -1
#JOY_BTN4 set_menu main
##
## Apple Remote section
##
## To use OSD menu with Apple Remote, set key AR_MENU to any OSD menu command,
## or just comment out the 'AR_MENU osd' line and uncomment the line after it.
##
AR_PLAY cycle pause
AR_PLAY_HOLD quit
AR_NEXT seek 30
AR_NEXT_HOLD seek 120
AR_PREV seek -10
AR_PREV_HOLD seek -120
#AR_MENU menu up
#AR_MENU menu cancel
AR_MENU_HOLD cycle mute
AR_VUP add volume 1
AR_VDOWN add volume -1
##
## DVD and Bluray menus
##
## Unlike MPlayer, input doesn't require blocking normal keys when no menu
## is active. The "discnav-menu" input section is active only when a menu
## is shown, while "discnav" is always active when a DVD/Bluray is played.
##
## The bindings were adjusted according to this.
##
UP {discnav-menu} discnav up # DVDNav UP
DOWN {discnav-menu} discnav down # DVDNav DOWN
LEFT {discnav-menu} discnav left # DVDNav LEFT
RIGHT {discnav-menu} discnav right # DVDNav RIGHT
ESC {discnav} discnav menu # DVDNav MENU
ENTER {discnav-menu} discnav select # DVDNav SELECT (ok)
BS {discnav-menu} discnav prev # DVDNav PREVIOUS menu (in the order chapter->title->root)
AR_VUP {discnav-menu} discnav up # DVDNav UP
AR_VDOWN {discnav-menu} discnav down # DVDNav DOWN
AR_PREV {discnav-menu} discnav left # DVDNav LEFT
AR_NEXT {discnav-menu} discnav right # DVDNav RIGHT
AR_MENU {discnav} discnav menu # DVDNav MENU
AR_PLAY {discnav-menu} discnav select # DVDNav SELECT (ok)
#? add chapter -1 # skip to previous dvd chapter
#? add chapter +1 # next
##
## Advanced seek
## Uncomment the following lines to be able to seek to n% of the media with
## the Fx keys.
##
#F1 seek 10 absolute-percent
#F2 seek 20 absolute-percent
#F3 seek 30 absolute-percent
#F4 seek 40 absolute-percent
#F5 seek 50 absolute-percent
#F6 seek 60 absolute-percent
#F7 seek 70 absolute-percent
#F8 seek 80 absolute-percent
#F9 seek 90 absolute-percent
+1 -1
View File
@@ -23,5 +23,5 @@ Exec=mpv --no-terminal --force-window -- %U
Terminal=false
NoDisplay=true
Categories=AudioVideo;Audio;Video;Player;TV;
MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;
MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb
+2 -1
View File
@@ -111,7 +111,8 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_LOADFILE, "loadfile", {
ARG_STRING,
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
{"append", 1}, {"1", 1})),
{"append", 1}, {"1", 1},
{"append-play", 2})),
OPT_KEYVALUELIST(ARG(str_list), MP_CMD_OPT_ARG),
}},
{ MP_CMD_LOADLIST, "loadlist", {
+1 -1
View File
@@ -306,7 +306,7 @@ struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, int def_flags,
bstr args[MP_CMD_MAX_ARGS];
int num = 0;
for (; argv[num]; num++) {
if (num > MP_CMD_MAX_ARGS) {
if (num >= MP_CMD_MAX_ARGS) {
mp_err(log, "%s: too many arguments.\n", location);
return NULL;
}
+71 -28
View File
@@ -28,7 +28,6 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <ctype.h>
#include <pthread.h>
#include <assert.h>
@@ -123,6 +122,7 @@ struct input_ctx {
bool mainthread_set;
struct mp_log *log;
struct mpv_global *global;
struct input_opts *opts;
bool using_alt_gr;
bool using_ar;
@@ -160,6 +160,9 @@ struct input_ctx {
// Unlike mouse_x/y, this can be used to resolve mouse click bindings.
int mouse_vo_x, mouse_vo_y;
bool mouse_mangle, mouse_src_mangle;
struct mp_rect mouse_src, mouse_dst;
bool test;
bool default_bindings;
@@ -170,10 +173,6 @@ struct input_ctx {
struct active_section active_sections[MAX_ACTIVE_SECTIONS];
int num_active_sections;
// Used to track whether we managed to read something while checking
// events sources. If yes, the sources may have more queued.
bool got_new_events;
unsigned int mouse_event_counter;
struct input_fd fds[MP_MAX_FDS];
@@ -181,6 +180,7 @@ struct input_ctx {
struct cmd_queue cmd_queue;
bool need_wakeup;
bool in_select;
int wakeup_pipe[2];
};
@@ -207,6 +207,7 @@ struct input_opts {
int use_appleremote;
int use_media_keys;
int default_bindings;
int enable_mouse_movements;
int test;
};
@@ -226,6 +227,7 @@ const struct m_sub_options input_config = {
OPT_FLAG("lirc", use_lirc, CONF_GLOBAL),
OPT_FLAG("right-alt-gr", use_alt_gr, CONF_GLOBAL),
OPT_INTRANGE("key-fifo-size", key_fifo_size, CONF_GLOBAL, 2, 65000),
OPT_FLAG("cursor", enable_mouse_movements, CONF_GLOBAL),
#if HAVE_LIRC
OPT_STRING("lirc-conf", lirc_configfile, CONF_GLOBAL),
#endif
@@ -243,6 +245,7 @@ const struct m_sub_options input_config = {
.ar_rate = 40,
.use_lirc = 1,
.use_alt_gr = 1,
.enable_mouse_movements = 1,
#if HAVE_COCOA
.use_appleremote = 1,
.use_media_keys = 1,
@@ -539,7 +542,7 @@ static void update_mouse_section(struct input_ctx *ictx)
struct mp_cmd *cmd = get_cmd_from_keys(ictx, old, MP_KEY_MOUSE_LEAVE);
if (cmd)
queue_add_tail(&ictx->cmd_queue, cmd);
ictx->got_new_events = true;
mp_input_wakeup(ictx);
}
}
@@ -557,9 +560,10 @@ static void release_down_cmd(struct input_ctx *ictx, bool drop_current)
if (!drop_current && ictx->current_down_cmd &&
ictx->current_down_cmd->key_up_follows)
{
memset(ictx->key_history, 0, sizeof(ictx->key_history));
ictx->current_down_cmd->key_up_follows = false;
queue_add_tail(&ictx->cmd_queue, ictx->current_down_cmd);
ictx->got_new_events = true;
mp_input_wakeup(ictx);
} else {
talloc_free(ictx->current_down_cmd);
}
@@ -596,15 +600,10 @@ static struct mp_cmd *resolve_key(struct input_ctx *ictx, int code)
{
update_mouse_section(ictx);
struct mp_cmd *cmd = get_cmd_from_keys(ictx, NULL, code);
if (cmd && cmd->id != MP_CMD_IGNORE) {
memset(ictx->key_history, 0, sizeof(ictx->key_history));
if (!should_drop_cmd(ictx, cmd))
return cmd;
talloc_free(cmd);
return NULL;
}
talloc_free(cmd);
key_buf_add(ictx->key_history, code);
if (cmd && cmd->id != MP_CMD_IGNORE && !should_drop_cmd(ictx, cmd))
return cmd;
talloc_free(cmd);
return NULL;
}
@@ -632,7 +631,7 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale)
if (MP_KEY_DEPENDS_ON_MOUSE_POS(unmod))
ictx->mouse_event_counter++;
ictx->got_new_events = true;
mp_input_wakeup(ictx);
struct mp_cmd *cmd = NULL;
@@ -673,6 +672,8 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale)
return;
}
memset(ictx->key_history, 0, sizeof(ictx->key_history));
cmd->scale = scale;
if (cmd->key_up_follows)
@@ -688,12 +689,14 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale)
release_down_cmd(ictx, false);
return;
}
if (!ictx->opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod))
return;
if (unmod == MP_KEY_MOUSE_LEAVE) {
update_mouse_section(ictx);
struct mp_cmd *cmd = get_cmd_from_keys(ictx, NULL, code);
if (cmd)
queue_add_tail(&ictx->cmd_queue, cmd);
ictx->got_new_events = true;
mp_input_wakeup(ictx);
return;
}
double now = mp_time_sec();
@@ -741,11 +744,50 @@ void mp_input_put_axis(struct input_ctx *ictx, int direction, double value)
input_unlock(ictx);
}
void mp_input_set_mouse_transform(struct input_ctx *ictx, struct mp_rect *dst,
struct mp_rect *src)
{
input_lock(ictx);
ictx->mouse_mangle = dst || src;
if (ictx->mouse_mangle) {
ictx->mouse_dst = *dst;
ictx->mouse_src_mangle = !!src;
if (ictx->mouse_src_mangle)
ictx->mouse_src = *src;
}
input_unlock(ictx);
}
bool mp_input_mouse_enabled(struct input_ctx *ictx)
{
input_lock(ictx);
bool r = ictx->opts->enable_mouse_movements;
input_unlock(ictx);
return r;
}
void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y)
{
input_lock(ictx);
MP_DBG(ictx, "mouse move %d/%d\n", x, y);
if (!ictx->opts->enable_mouse_movements) {
input_unlock(ictx);
return;
}
if (ictx->mouse_mangle) {
struct mp_rect *src = &ictx->mouse_src;
struct mp_rect *dst = &ictx->mouse_dst;
x = MPCLAMP(x, dst->x0, dst->x1) - dst->x0;
y = MPCLAMP(y, dst->y0, dst->y1) - dst->y0;
if (ictx->mouse_src_mangle) {
x = x * 1.0 / (dst->x1 - dst->x0) * (src->x1 - src->x0) + src->x0;
y = y * 1.0 / (dst->y1 - dst->y0) * (src->y1 - src->y0) + src->y0;
}
MP_DBG(ictx, "-> %d/%d\n", x, y);
}
ictx->mouse_event_counter++;
ictx->mouse_vo_x = x;
ictx->mouse_vo_y = y;
@@ -769,7 +811,7 @@ void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y)
talloc_free(tail);
}
queue_add_tail(&ictx->cmd_queue, cmd);
ictx->got_new_events = true;
mp_input_wakeup(ictx);
}
}
input_unlock(ictx);
@@ -951,7 +993,7 @@ static void read_cmd_fd(struct input_ctx *ictx, struct input_fd *cmd_fd)
int r;
char *text;
while ((r = read_cmd(cmd_fd, &text)) >= 0) {
ictx->got_new_events = true;
mp_input_wakeup(ictx);
struct mp_cmd *cmd = mp_input_parse_cmd(ictx, bstr0(text), "<pipe>");
talloc_free(text);
if (cmd)
@@ -1059,9 +1101,9 @@ static void read_events(struct input_ctx *ictx, int time)
time = FFMAX(time, 0);
while (1) {
if (ictx->got_new_events)
if (ictx->need_wakeup)
time = 0;
ictx->got_new_events = false;
ictx->need_wakeup = false;
remove_dead_fds(ictx);
@@ -1072,13 +1114,13 @@ static void read_events(struct input_ctx *ictx, int time)
}
}
if (ictx->got_new_events)
if (ictx->need_wakeup)
time = 0;
input_wait_read(ictx, time);
// Read until all input FDs are empty
if (!ictx->got_new_events)
// Read until no new wakeups happen.
if (!ictx->need_wakeup)
break;
}
}
@@ -1086,7 +1128,6 @@ static void read_events(struct input_ctx *ictx, int time)
int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
{
input_lock(ictx);
ictx->got_new_events = true;
if (cmd)
queue_add_tail(&ictx->cmd_queue, cmd);
input_unlock(ictx);
@@ -1453,6 +1494,7 @@ static int parse_config_file(struct input_ctx *ictx, char *file, bool warn)
MP_ERR(ictx, "Can't open input config file %s.\n", file);
goto done;
}
stream_skip_bom(s);
bstr data = stream_read_complete(s, tmp, 1000000);
if (data.start) {
MP_VERBOSE(ictx, "Parsing input config file %s\n", file);
@@ -1490,6 +1532,7 @@ struct input_ctx *mp_input_init(struct mpv_global *global)
struct input_ctx *ictx = talloc_ptrtype(NULL, ictx);
*ictx = (struct input_ctx){
.global = global,
.opts = input_conf,
.log = mp_log_new(ictx, global->log, "input"),
.key_fifo_size = input_conf->key_fifo_size,
.doubleclick_time = input_conf->doubleclick_time,
@@ -1523,7 +1566,7 @@ struct input_ctx *mp_input_init(struct mpv_global *global)
#ifndef __MINGW32__
int ret = mp_make_wakeup_pipe(ictx->wakeup_pipe);
if (ret < 0)
MP_ERR(ictx, "Failed to initialize wakeup pipe: %s\n", strerror(-ret));
MP_ERR(ictx, "Failed to initialize wakeup pipe.\n");
else
mp_input_add_fd(ictx, ictx->wakeup_pipe[0], true, NULL, read_wakeup,
NULL, NULL);
@@ -1635,7 +1678,7 @@ void mp_input_wakeup(struct input_ctx *ictx)
{
input_lock(ictx);
bool send_wakeup = ictx->in_select;
ictx->got_new_events = true;
ictx->need_wakeup = true;
pthread_cond_signal(&ictx->wakeup);
input_unlock(ictx);
// Safe without locking
@@ -1649,7 +1692,7 @@ void mp_input_wakeup_nolock(struct input_ctx *ictx)
write(ictx->wakeup_pipe[1], &(char){0}, 1);
} else {
// Not race condition free. Done for the sake of jackaudio+windows.
ictx->got_new_events = true;
ictx->need_wakeup = true;
pthread_cond_signal(&ictx->wakeup);
}
}
+10
View File
@@ -133,6 +133,16 @@ void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y);
void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y);
// Return whether we want/accept mouse input.
bool mp_input_mouse_enabled(struct input_ctx *ictx);
/* Make mp_input_set_mouse_pos() mangle the mouse coordinates. Hack for certain
* VOs. dst=NULL, src=NULL reset it. src can be NULL.
*/
struct mp_rect;
void mp_input_set_mouse_transform(struct input_ctx *ictx, struct mp_rect *dst,
struct mp_rect *src);
// As for the cmd one you usually don't need this function.
void mp_input_rm_key_fd(struct input_ctx *ictx, int fd);
+1
View File
@@ -18,6 +18,7 @@
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include "bstr/bstr.h"
#include "common/common.h"
+3
View File
@@ -222,6 +222,9 @@
(MP_KEY_IS_MOUSE_BTN_SINGLE(code) || MP_KEY_IS_MOUSE_BTN_DBL(code) || \
(code) == MP_KEY_MOUSE_MOVE)
#define MP_KEY_IS_MOUSE(code) \
(MP_KEY_DEPENDS_ON_MOUSE_POS(code) || (code) == MP_KEY_MOUSE_LEAVE)
// Emit a command even on key-up (normally key-up is ignored). This means by
// default they binding will be triggered on key-up instead of key-down.
// This is a fixed part of the keycode, not a modifier than can change.
+1 -1
View File
@@ -71,7 +71,7 @@ int mp_input_lirc_init(struct input_ctx *ictx, struct mp_log *log,
.log = log,
.lirc_config = lirc_config,
};
mp_input_add_fd(ictx, lirc_sock, 0, mp_input_lirc_read, NULL, mp_input_lirc_close, ctx);
mp_input_add_fd(ictx, lirc_sock, 1, mp_input_lirc_read, NULL, mp_input_lirc_close, ctx);
return lirc_sock;
}
+31 -6
View File
@@ -114,7 +114,7 @@ extern "C" {
* handler is process-wide, and there's no proper way to share it with other
* xlib users within the same process. This might confuse GUI toolkits.
* - mpv uses some other libraries that are not library-safe, such as Fribidi
* (used through libass), LittleCMS, ALSA, FFmpeg, and possibly more.
* (used through libass), ALSA, FFmpeg, and possibly more.
* - The FPU precision must be set at least to double precision.
* - On Windows, mpv will call timeBeginPeriod(1).
* - On memory exhaustion, mpv will kill the process.
@@ -131,6 +131,24 @@ extern "C" {
* aspect ratio of the window and the video mismatch).
*
* On OSX, embedding is not yet possible, because Cocoa makes this non-trivial.
*
* Compatibility
* -------------
*
* mpv development doesn't stand still, and changes to mpv internals as well as
* to its interface can cause compatibility issues to client API users.
*
* The API is versioned (see MPV_CLIENT_API_VERSION), and changes to it are
* documented in DOCS/client-api-changes.rst. The C API itself will probably
* remain compatible for a long time, but the functionality exposed by it
* could change more rapidly. For example, it's possible that options are
* renamed, or change the set of allowed values.
*
* Defensive programming should be used to potentially deal with the fact that
* options, commands, and properties could disappear, change their value range,
* or change the underlying datatypes. It might be a good idea to prefer
* MPV_FORMAT_STRING over other types to decouple your code from potential
* mpv changes.
*/
/**
@@ -139,8 +157,12 @@ extern "C" {
* the API becomes incompatible to previous versions, the major version
* number is incremented. This affects only C part, and not properties and
* options.
*
* You can use MPV_MAKE_VERSION() and compare the result with integer
* relational operators (<, >, <=, >=).
*/
#define MPV_CLIENT_API_VERSION 0x00010000UL
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 4)
/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
@@ -896,8 +918,10 @@ typedef enum mpv_event_id {
*/
MPV_EVENT_UNPAUSE = 13,
/**
* Sent every time after a video frame is displayed (or in lower frequency
* if there is no video, or playback is paused).
* Sent every time after a video frame is displayed. Note that currently,
* this will be sent in lower frequency if there is no video, or playback
* is paused - but that will be removed in the future, and it will be
* restricted to video frames only.
*/
MPV_EVENT_TICK = 14,
/**
@@ -912,8 +936,8 @@ typedef enum mpv_event_id {
/**
* Triggered by the script_message input command. The command uses the
* first argument of the command as client name (see mpv_client_name()) to
* dispatch the message, and passes along the all arguments starting from
* the seconand argument as strings.
* dispatch the message, and passes along all arguments starting from the
* second argument as strings.
* See also mpv_event and mpv_event_client_message.
*/
MPV_EVENT_CLIENT_MESSAGE = 16,
@@ -960,6 +984,7 @@ typedef enum mpv_event_id {
* Happens when the current chapter changes.
*/
MPV_EVENT_CHAPTER_CHANGE = 23
// Internal note: adjust INTERNAL_EVENT_BASE when adding new events.
} mpv_event_id;
/**
+34
View File
@@ -0,0 +1,34 @@
mpv_client_api_version
mpv_client_name
mpv_command
mpv_command_async
mpv_command_string
mpv_create
mpv_detach_destroy
mpv_error_string
mpv_event_name
mpv_free
mpv_free_node_contents
mpv_get_property
mpv_get_property_async
mpv_get_property_osd_string
mpv_get_property_string
mpv_get_time_us
mpv_get_wakeup_pipe
mpv_initialize
mpv_load_config_file
mpv_observe_property
mpv_request_event
mpv_request_log_messages
mpv_resume
mpv_set_option
mpv_set_option_string
mpv_set_property
mpv_set_property_async
mpv_set_property_string
mpv_set_wakeup_callback
mpv_suspend
mpv_terminate_destroy
mpv_unobserve_property
mpv_wait_event
mpv_wakeup
+1
View File
@@ -8,4 +8,5 @@ Description: mpv media player client library
Version: @VERSION@
Requires:
Libs: -L${libdir} -lmpv
Libs.private: @PRIV_LIBS@
Cflags: -I${includedir}
+31 -4
View File
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <assert.h>
#include "config.h"
@@ -80,11 +81,24 @@ bool mp_charset_requires_guess(const char *user_cp)
// Note that "utf8" is the UTF-8 codepage, while "utf8:..." specifies UTF-8
// by default, plus a codepage that is used if the input is not UTF-8.
return bstrcasecmp0(res[0], "enca") == 0 ||
bstrcasecmp0(res[0], "auto") == 0 ||
bstrcasecmp0(res[0], "guess") == 0 ||
(r > 1 && bstrcasecmp0(res[0], "utf-8") == 0) ||
(r > 1 && bstrcasecmp0(res[0], "utf8") == 0);
}
static const char *const utf_bom[3] = {"\xEF\xBB\xBF", "\xFF\xFE", "\xFE\xFF"};
static const char *const utf_enc[3] = {"utf-8", "utf-16le", "utf-16be"};
static const char *ms_bom_guess(bstr buf)
{
for (int n = 0; n < 3; n++) {
if (bstr_startswith0(buf, utf_bom[n]))
return utf_enc[n];
}
return NULL;
}
#if HAVE_ENCA
static const char *enca_guess(struct mp_log *log, bstr buf, const char *language)
{
@@ -102,8 +116,7 @@ static const char *enca_guess(struct mp_log *log, bstr buf, const char *language
detected_cp = tmp;
enca_analyser_free(analyser);
} else {
mp_err(log, "ENCA doesn't know language '%s'\n",
language);
mp_err(log, "ENCA doesn't know language '%s'\n", language);
size_t langcnt;
const char **languages = enca_get_languages(&langcnt);
mp_err(log, "ENCA supported languages:");
@@ -143,6 +156,15 @@ const char *mp_charset_guess(struct mp_log *log, bstr buf, const char *user_cp,
if (!mp_charset_requires_guess(user_cp))
return user_cp;
bool use_auto = strcasecmp(user_cp, "auto") == 0;
if (use_auto) {
#if HAVE_ENCA
user_cp = "enca";
#else
user_cp = "UTF-8:UTF-8-BROKEN";
#endif
}
// Do our own UTF-8 detection, because at least ENCA seems to get it
// wrong sometimes (suggested by divVerent).
int r = bstr_validate_utf8(buf);
@@ -159,6 +181,12 @@ const char *mp_charset_guess(struct mp_log *log, bstr buf, const char *user_cp,
const char *res = NULL;
if (use_auto) {
res = ms_bom_guess(buf);
if (res)
type = bstr0("auto");
}
#if HAVE_ENCA
if (bstrcasecmp0(type, "enca") == 0)
res = enca_guess(log, buf, lang);
@@ -173,8 +201,7 @@ const char *mp_charset_guess(struct mp_log *log, bstr buf, const char *user_cp,
}
if (res) {
mp_dbg(log, "%.*s detected charset: '%s'\n",
BSTR_P(type), res);
mp_dbg(log, "%.*s detected charset: '%s'\n", BSTR_P(type), res);
} else {
res = fallback;
mp_dbg(log, "Detection with %.*s failed: fallback to %s\n",
+19
View File
@@ -0,0 +1,19 @@
#ifndef MP_CTYPE_H_
#define MP_CTYPE_H_
// Roughly follows C semantics, but doesn't account for EOF, allows char as
// parameter, and is locale independent (always uses "C" locale).
static inline int mp_isprint(char c) { return (unsigned char)c >= 32; }
static inline int mp_isspace(char c) { return c == ' ' || c == '\f' || c == '\n' ||
c == '\r' || c == '\t' || c =='\v'; }
static inline int mp_isupper(char c) { return c >= 'A' && c <= 'Z'; }
static inline int mp_islower(char c) { return c >= 'a' && c <= 'z'; }
static inline int mp_isdigit(char c) { return c >= '0' && c <= '9'; }
static inline int mp_isalpha(char c) { return mp_isupper(c) || mp_islower(c); }
static inline int mp_isalnum(char c) { return mp_isalpha(c) || mp_isdigit(c); }
static inline char mp_tolower(char c) { return mp_isupper(c) ? c - 'A' + 'a' : c; }
static inline char mp_toupper(char c) { return mp_islower(c) ? c - 'a' + 'A' : c; }
#endif
Loaded 100 of 218 files, more files were not shown because too many files have changed in this diff. Show more