Compare commits

..
40 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
601 changed files with 47814 additions and 58453 deletions

No files matched your search

+1 -2
View File
@@ -14,6 +14,7 @@
/input/input_conf.h
/tags
/TAGS
/video/out/gl_video_shaders.h
/video/out/x11_icon.inc
/demux/ebml_defs.c
/demux/ebml_types.h
@@ -27,8 +28,6 @@
/DOCS/man/mpv.toc
/waf
/waf-*
/waf3-*
/build
/.waf*
/.lock-waf_*
+5 -27
View File
@@ -4,23 +4,17 @@ os:
- linux
- osx
env:
matrix:
- LIBAV=libav-stable
- LIBAV=libav-git
- LIBAV=ffmpeg-stable
- LIBAV=ffmpeg-git
global:
# Coverity token
- secure: "H21mSRlMhk4BKS0xHZvCFGJxteCP0hRVUxTuNfM2Z9HBsyutuLEYMtViLO86VtM+Tqla3xXPzUdS4ozLwI72Ax/5ZUDXACROj73yW6QhFB5D6rLut12+FjqC7M33Qv2hl0xwgNBmR5dsm1ToP37+Wn+ecJQNvN8fkTXF+HVzOEw="
- LIBAV=libav-stable
- LIBAV=libav-git
- LIBAV=ffmpeg-stable
- LIBAV=ffmpeg-git
compiler:
- clang
- gcc
branches:
only:
- master
- ci
- coverity_scan
matrix:
exclude:
@@ -30,12 +24,6 @@ matrix:
env: LIBAV=libav-git
- os: osx
env: LIBAV=ffmpeg-git
- os: osx
compiler: gcc
- os: linux
env: LIBAV=ffmpeg-stable
- os: linux
compiler: clang
before_install: ./travis-deps libass-stable $LIBAV
script:
@@ -47,16 +35,6 @@ notifications:
email: false
irc:
channels:
- "irc.freenode.org#mpv-devel"
- "irc.freenode.org#mpv-player-dev"
on_success: change
on_failure: always
addons:
coverity_scan:
project:
name: "mpv-player/mpv"
description: "Build submitted via Travis CI"
notification_email: mpv-team@googlegroups.com
build_command_prepend: "./bootstrap.py && ./waf configure"
build_command: "./waf build"
branch_pattern: coverity_scan
+2 -10
View File
@@ -2,15 +2,9 @@ mpv is a fork of mplayer2, which is a fork of MPlayer.
mpv as a whole is licensed as GPL version 2 or later (see LICENSE). Most source
files are GPLv2+, but some files are available under a more liberal license,
such as LGPLv2.1+, BSD, MIT, ISC, and possibly others. Look at the copyright
such as LGPLv2+, BSD, MIT, ISC, and possibly others. Look at the copyright
header of each source file, and grep the sources for "Copyright" if you need
to know details. Files without Copyright notice are licensed as LGPLv2.1+.
All new contributions must be LGPLv2.1+ licensed, or if the changes are done on
GPL code, must come with the implicit agreement that the project can relicense
the code to LGPLv2.1+ at a later point without asking the contributor. (This
is a safeguard for making potential relicensing of the project to LGPLv2.1+
easier.) Using a more liberal license compatible to LGPLv2.1+ is also ok.
to know details. Files without Copyright notice are licensed as LGPLv2+.
For information about authors and contributors, consult the git log, which
contains the complete SVN and CVS history as well.
@@ -23,5 +17,3 @@ MPlayer as a whole is licensed under GPLv2 (incompatible to GPLv3!), because
some files are licensed to GPLv2 (and _not_ any later version of the license).
In particular, this affects the file libmpdemux/demux_ty_osd.c. It is disabled
under mplayer2, and has been removed from mpv.
"v2.1+" in this context means "version 2.1 or later".
+6 -73
View File
@@ -4,7 +4,9 @@ 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, you should also look at ``interface-changes.rst``.
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),
@@ -18,83 +20,14 @@ The version number is the same as used for MPV_CLIENT_API_VERSION (see
Also, read the section ``Compatibility`` in ``client.h``.
Options, commands, properties
=============================
Changes to these are not listed here, but in ``interface-changes.rst``. (Before
client API version 1.17, they were listed here partially.)
This listing includes changes to the bare C API and behavior only, not what
you can access with them.
API changes
===========
::
--- mpv 0.11.0 ---
1.19 - add "GL_MP_D3D_interfaces" pseudo extension to make it possible to
use DXVA2 in OpenGL fullscreen mode in some situations
- mpv_request_log_messages() now accepts "terminal-default" as parameter
1.18 - add MPV_END_FILE_REASON_REDIRECT, and change behavior of
MPV_EVENT_END_FILE accordingly
- a bunch of interface-changes.rst changes
1.17 - mpv_initialize() now blocks SIGPIPE (details see client.h)
--- mpv 0.9.0 ---
1.16 - add mpv_opengl_cb_report_flip()
- introduce mpv_opengl_cb_draw() and deprecate mpv_opengl_cb_render()
- add MPV_FORMAT_BYTE_ARRAY
1.15 - mpv_initialize() will now load config files. This requires setting
the "config" and "config-dir" options. In particular, it will load
mpv.conf.
- minor backwards-compatible change to the "seek" and "screenshot"
commands (new flag syntax, old additional args deprecated)
--- mpv 0.8.0 ---
1.14 - add mpv_wait_async_requests()
- the --msg-level option changes its native type from a flat string to
a key-value list (setting/reading the option as string still works)
1.13 - add MPV_EVENT_QUEUE_OVERFLOW
1.12 - add class Handle to qthelper.hpp
- improve opengl_cb.h API uninitialization behavior, and fix the qml
example
- add mpv_create_client() function
1.11 - add OpenGL rendering interop API - allows an application to combine
its own and mpv's OpenGL rendering
Warning: this API is not stable yet - anything in opengl_cb.h might
be changed in completely incompatible ways in minor API bumps
--- mpv 0.7.0 ---
1.10 - deprecate/disable everything directly related to script_dispatch
(most likely affects nobody)
1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason
- add MPV_END_FILE_REASON_ERROR and the mpv_event_end_file.error field
for slightly better error reporting on playback failure
- add --stop-playback-on-init-failure option, and make it the default
behavior for libmpv only
- add qthelper.hpp set_option_variant()
- mark the following events as deprecated:
MPV_EVENT_TRACKS_CHANGED
MPV_EVENT_TRACK_SWITCHED
MPV_EVENT_PAUSE
MPV_EVENT_UNPAUSE
MPV_EVENT_METADATA_UPDATE
MPV_EVENT_CHAPTER_CHANGE
They are handled better with mpv_observe_property() as mentioned in
the documentation comments. They are not removed and still work.
1.8 - add qthelper.hpp
1.7 - add mpv_command_node(), mpv_command_node_async()
1.6 - modify "core-idle" property behavior
- MPV_EVENT_LOG_MESSAGE now always sends complete lines
- introduce numeric log levels (mpv_log_level)
--- mpv 0.6.0 ---
1.5 - change in X11 and "--wid" behavior again. The previous change didn't
work as expected, and now the behavior can be explicitly controlled
with the "input-x11-keyboard" option. This is only a temporary
measure until XEmbed is implemented and confirmed working.
Note: in 1.6, "input-x11-keyboard" was renamed to "input-vo-keyboard",
although the old option name still works.
--- mpv 0.5.2 is released ---
1.4 - subtle change in X11 and "--wid" behavior
(this change was added to 0.5.2, and broke some things, see #1090)
--- mpv 0.5.0 ---
--- 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://
@@ -106,6 +39,6 @@ API changes
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 ---
--- mpv 0.4.0 is released ---
1.0 - the API is declared stable
-14
View File
@@ -1,14 +0,0 @@
All examples in this directory and its sub-directories are licensed
under one of the following licenses:
WTFPL, ISC, Ms-PL, AGPLv3, BSD (any)
Pick any license of your liking, and disregard the others.
(The full text of each license is available on this website:
http://opensource.org/licenses/alphabetical )
Additionally, you may consider the example code to be public domain.
You are free to use any of the example code without further
requirements or need for attribution.
-35
View File
@@ -1,35 +0,0 @@
Client API examples
===================
All these examples use the mpv client API through libmpv.
cocoa
-----
Shows how to embed the mpv video window in Objective-C/Cocoa.
qt
--
Shows how to embed the mpv video window in Qt (using normal desktop widgets).
qml
---
Shows how to use mpv's OpenGL video renderer in QtQuick2 with QML.
qml_direct
----------
Alternative example, which typically avoids a FBO indirection. Might be
slightly faster, but is less flexible and harder to use.
sdl
---
Show how to embed the mpv OpenGL renderer in SDL.
simple
------
Very primitive terminal-only example. Shows some most basic API usage.
@@ -1,304 +0,0 @@
// Plays a video from the command line in an opengl view in its own window.
// Build with: clang -o cocoa-openglcb cocoa-openglcb.m `pkg-config --libs --cflags mpv` -framework Cocoa -framework OpenGL
#import <mpv/client.h>
#import <mpv/opengl_cb.h>
#import <stdio.h>
#import <stdlib.h>
#import <OpenGL/gl.h>
#import <Cocoa/Cocoa.h>
static inline void check_error(int status)
{
if (status < 0) {
printf("mpv API error: %s\n", mpv_error_string(status));
exit(1);
}
}
static void *get_proc_address(void *ctx, const char *name)
{
CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
void *addr = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), symbolName);
CFRelease(symbolName);
return addr;
}
static void glupdate(void *ctx);
@interface MpvClientOGLView : NSOpenGLView
@property mpv_opengl_cb_context *mpvGL;
- (instancetype)initWithFrame:(NSRect)frame;
- (void)drawRect;
- (void)fillBlack;
@end
@implementation MpvClientOGLView
- (instancetype)initWithFrame:(NSRect)frame
{
// make sure the pixel format is double buffered so we can use
// [[self openGLContext] flushBuffer].
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFADoubleBuffer,
0
};
self = [super initWithFrame:frame
pixelFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]];
if (self) {
[self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
// swap on vsyncs
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
[[self openGLContext] makeCurrentContext];
self.mpvGL = nil;
}
return self;
}
- (void)fillBlack
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
- (void)drawRect
{
if (self.mpvGL)
mpv_opengl_cb_draw(self.mpvGL, 0, self.bounds.size.width, -self.bounds.size.height);
else
[self fillBlack];
[[self openGLContext] flushBuffer];
}
- (void)drawRect:(NSRect)dirtyRect
{
[self drawRect];
}
@end
@interface CocoaWindow : NSWindow
@property(retain, readonly) MpvClientOGLView *glView;
@property(retain, readonly) NSButton *pauseButton;
@end
@implementation CocoaWindow
- (BOOL)canBecomeMainWindow { return YES; }
- (BOOL)canBecomeKeyWindow { return YES; }
- (void)initOGLView {
NSRect bounds = [[self contentView] bounds];
// window coordinate origin is bottom left
NSRect glFrame = NSMakeRect(bounds.origin.x, bounds.origin.y + 30, bounds.size.width, bounds.size.height - 30);
_glView = [[MpvClientOGLView alloc] initWithFrame:glFrame];
[self.contentView addSubview:_glView];
NSRect buttonFrame = NSMakeRect(bounds.origin.x, bounds.origin.y, 60, 30);
_pauseButton = [[NSButton alloc] initWithFrame:buttonFrame];
_pauseButton.buttonType = NSToggleButton;
// button target has to be the delegate (it holds the mpv context
// pointer), so that's set later.
_pauseButton.action = @selector(togglePause:);
_pauseButton.title = @"Pause";
_pauseButton.alternateTitle = @"Play";
[self.contentView addSubview:_pauseButton];
}
@end
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
mpv_handle *mpv;
dispatch_queue_t queue;
CocoaWindow *window;
}
@end
static void wakeup(void *);
@implementation AppDelegate
- (void)createWindow {
int mask = NSTitledWindowMask|NSClosableWindowMask|
NSMiniaturizableWindowMask|NSResizableWindowMask;
window = [[CocoaWindow alloc]
initWithContentRect:NSMakeRect(0, 0, 1280, 720)
styleMask:mask
backing:NSBackingStoreBuffered
defer:NO];
// force a minimum size to stop opengl from exploding.
[window setMinSize:NSMakeSize(200, 200)];
[window initOGLView];
[window setTitle:@"cocoa-openglcb example"];
[window makeMainWindow];
[window makeKeyAndOrderFront:nil];
NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"];
NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""];
NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"];
[m setSubmenu:sm forItem:item];
[sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"];
[NSApp setMenu:m];
[NSApp activateIgnoringOtherApps:YES];
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
atexit_b(^{
// Because activation policy has just been set to behave like a real
// application, that policy must be reset on exit to prevent, among
// other things, the menubar created here from remaining on screen.
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
});
// Read filename
NSArray *args = [NSProcessInfo processInfo].arguments;
if (args.count < 2) {
NSLog(@"Expected filename on command line");
exit(1);
}
NSString *filename = args[1];
[self createWindow];
window.pauseButton.target = self;
mpv = mpv_create();
if (!mpv) {
printf("failed creating context\n");
exit(1);
}
check_error(mpv_set_option_string(mpv, "input-media-keys", "yes"));
// request important errors
check_error(mpv_request_log_messages(mpv, "warn"));
check_error(mpv_initialize(mpv));
check_error(mpv_set_option_string(mpv, "vo", "opengl-cb"));
mpv_opengl_cb_context *mpvGL = mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
if (!mpvGL) {
puts("libmpv does not have the opengl-cb sub-API.");
exit(1);
}
// pass the mpvGL context to our view
window.glView.mpvGL = mpvGL;
int r = mpv_opengl_cb_init_gl(mpvGL, NULL, get_proc_address, NULL);
if (r < 0) {
puts("gl init has failed.");
exit(1);
}
mpv_opengl_cb_set_update_callback(mpvGL, glupdate, (__bridge void *)window.glView);
// Deal with MPV in the background.
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// Register to be woken up whenever mpv generates new events.
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *)self);
// Load the indicated file
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
check_error(mpv_command(mpv, cmd));
});
}
static void glupdate(void *ctx)
{
MpvClientOGLView *glView = (__bridge MpvClientOGLView *)ctx;
// I'm still not sure what the best way to handle this is, but this
// works.
dispatch_async(dispatch_get_main_queue(), ^{
[glView drawRect];
});
}
- (void) handleEvent:(mpv_event *)event
{
switch (event->event_id) {
case MPV_EVENT_SHUTDOWN: {
mpv_detach_destroy(mpv);
mpv_opengl_cb_uninit_gl(window.glView.mpvGL);
mpv = NULL;
printf("event: shutdown\n");
break;
}
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
}
default:
printf("event: %s\n", mpv_event_name(event->event_id));
}
}
- (void)togglePause:(NSButton *)button
{
if (mpv) {
switch (button.state) {
case NSOffState:
{
int pause = 0;
mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause);
}
break;
case NSOnState:
{
int pause = 1;
mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause);
}
break;
default:
NSLog(@"This should never happen.");
}
}
}
- (void) readEvents
{
dispatch_async(queue, ^{
while (mpv) {
mpv_event *event = mpv_wait_event(mpv, 0);
if (event->event_id == MPV_EVENT_NONE)
break;
[self handleEvent:event];
}
});
}
static void wakeup(void *context)
{
AppDelegate *a = (__bridge AppDelegate *) context;
[a readEvents];
}
// quit when the window is closed.
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
return YES;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
NSLog(@"Terminating.");
const char *args[] = {"quit", NULL};
mpv_command(mpv, args);
[window.glView clearGLContext];
return NSTerminateNow;
}
@end
// Delete this if you already have a main.m.
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *delegate = [AppDelegate new];
app.delegate = delegate;
[app run];
}
return 0;
}
-209
View File
@@ -1,209 +0,0 @@
// Plays a video from the command line in a view provided by the client
// application.
// Build with: clang -o cocoabasic cocoabasic.m `pkg-config --libs --cflags mpv` -framework cocoa
#include <mpv/client.h>
#include <stdio.h>
#include <stdlib.h>
static inline void check_error(int status)
{
if (status < 0) {
printf("mpv API error: %s\n", mpv_error_string(status));
exit(1);
}
}
#import <Cocoa/Cocoa.h>
@interface CocoaWindow : NSWindow
@end
@implementation CocoaWindow
- (BOOL)canBecomeMainWindow { return YES; }
- (BOOL)canBecomeKeyWindow { return YES; }
@end
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
mpv_handle *mpv;
dispatch_queue_t queue;
NSWindow *w;
NSView *wrapper;
}
@end
static void wakeup(void *);
@implementation AppDelegate
- (void)createWindow {
int mask = NSTitledWindowMask|NSClosableWindowMask|
NSMiniaturizableWindowMask|NSResizableWindowMask;
self->w = [[CocoaWindow alloc]
initWithContentRect:NSMakeRect(0,0, 1280, 720)
styleMask:mask
backing:NSBackingStoreBuffered
defer:NO];
[self->w setTitle:@"cocoabasic example"];
[self->w makeMainWindow];
[self->w makeKeyAndOrderFront:nil];
NSRect frame = [[self->w contentView] bounds];
self->wrapper = [[NSView alloc] initWithFrame:frame];
[self->wrapper setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[[self->w contentView] addSubview:self->wrapper];
[self->wrapper release];
NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"];
NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""];
NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"];
[m setSubmenu:sm forItem:item];
[sm addItemWithTitle: @"mpv_command('stop')" action:@selector(mpv_stop) keyEquivalent:@""];
[sm addItemWithTitle: @"mpv_command('quit')" action:@selector(mpv_quit) keyEquivalent:@""];
[sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"];
[NSApp setMenu:m];
[NSApp activateIgnoringOtherApps:YES];
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
atexit_b(^{
// Because activation policy has just been set to behave like a real
// application, that policy must be reset on exit to prevent, among
// other things, the menubar created here from remaining on screen.
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
});
// Read filename
NSArray *args = [NSProcessInfo processInfo].arguments;
if (args.count < 2) {
NSLog(@"Expected filename on command line");
exit(1);
}
NSString *filename = args[1];
[self createWindow];
// Deal with MPV in the background.
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
mpv = mpv_create();
if (!mpv) {
printf("failed creating context\n");
exit(1);
}
int64_t wid = (intptr_t) self->wrapper;
check_error(mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid));
// Maybe set some options here, like default key bindings.
// NOTE: Interaction with the window seems to be broken for now.
check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes"));
// for testing!
check_error(mpv_set_option_string(mpv, "input-media-keys", "yes"));
check_error(mpv_set_option_string(mpv, "input-cursor", "no"));
check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes"));
// request important errors
check_error(mpv_request_log_messages(mpv, "warn"));
check_error(mpv_initialize(mpv));
// Register to be woken up whenever mpv generates new events.
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self);
// Load the indicated file
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
check_error(mpv_command(mpv, cmd));
});
}
- (void) handleEvent:(mpv_event *)event
{
switch (event->event_id) {
case MPV_EVENT_SHUTDOWN: {
mpv_detach_destroy(mpv);
mpv = NULL;
printf("event: shutdown\n");
break;
}
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
}
case MPV_EVENT_VIDEO_RECONFIG: {
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *subviews = [self->wrapper subviews];
if ([subviews count] > 0) {
// mpv's events view
NSView *eview = [self->wrapper subviews][0];
[self->w makeFirstResponder:eview];
}
});
}
default:
printf("event: %s\n", mpv_event_name(event->event_id));
}
}
- (void) readEvents
{
dispatch_async(queue, ^{
while (mpv) {
mpv_event *event = mpv_wait_event(mpv, 0);
if (event->event_id == MPV_EVENT_NONE)
break;
[self handleEvent:event];
}
});
}
static void wakeup(void *context) {
AppDelegate *a = (__bridge AppDelegate *) context;
[a readEvents];
}
// Ostensibly, mpv's window would be hooked up to this.
- (BOOL) windowShouldClose:(id)sender
{
return NO;
}
- (void) mpv_stop
{
if (mpv) {
const char *args[] = {"stop", NULL};
mpv_command(mpv, args);
}
}
- (void) mpv_quit
{
if (mpv) {
const char *args[] = {"quit", NULL};
mpv_command(mpv, args);
}
}
@end
// Delete this if you already have a main.m.
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *delegate = [AppDelegate new];
app.delegate = delegate;
[app run];
}
return 0;
}
-136
View File
@@ -1,136 +0,0 @@
#include "main.h"
#include <stdexcept>
#include <clocale>
#include <QObject>
#include <QtGlobal>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
class MpvRenderer : public QQuickFramebufferObject::Renderer
{
static void *get_proc_address(void *ctx, const char *name) {
(void)ctx;
QOpenGLContext *glctx = QOpenGLContext::currentContext();
if (!glctx)
return NULL;
return (void *)glctx->getProcAddress(QByteArray(name));
}
mpv::qt::Handle mpv;
QQuickWindow *window;
mpv_opengl_cb_context *mpv_gl;
public:
MpvRenderer(const MpvObject *obj)
: mpv(obj->mpv), window(obj->window()), mpv_gl(obj->mpv_gl)
{
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
if (r < 0)
throw std::runtime_error("could not initialize OpenGL");
}
virtual ~MpvRenderer()
{
// Until this call is done, we need to make sure the player remains
// alive. This is done implicitly with the mpv::qt::Handle instance
// in this class.
mpv_opengl_cb_uninit_gl(mpv_gl);
}
void render()
{
QOpenGLFramebufferObject *fbo = framebufferObject();
window->resetOpenGLState();
mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height());
window->resetOpenGLState();
}
};
MpvObject::MpvObject(QQuickItem * parent)
: QQuickFramebufferObject(parent), mpv_gl(0)
{
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
if (!mpv)
throw std::runtime_error("could not create mpv context");
mpv_set_option_string(mpv, "terminal", "yes");
mpv_set_option_string(mpv, "msg-level", "all=v");
if (mpv_initialize(mpv) < 0)
throw std::runtime_error("could not initialize mpv context");
// Make use of the MPV_SUB_API_OPENGL_CB API.
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
// Request hw decoding, just for testing.
mpv::qt::set_option_variant(mpv, "hwdec", "auto");
// Setup the callback that will make QtQuick update and redraw if there
// is a new video frame. Use a queued connection: this makes sure the
// doUpdate() function is run on the GUI thread.
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
if (!mpv_gl)
throw std::runtime_error("OpenGL not compiled in");
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
Qt::QueuedConnection);
}
MpvObject::~MpvObject()
{
if (mpv_gl)
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
}
void MpvObject::on_update(void *ctx)
{
MpvObject *self = (MpvObject *)ctx;
emit self->onUpdate();
}
// connected to onUpdate(); signal makes sure it runs on the GUI thread
void MpvObject::doUpdate()
{
update();
}
void MpvObject::command(const QVariant& params)
{
mpv::qt::command_variant(mpv, params);
}
void MpvObject::setProperty(const QString& name, const QVariant& value)
{
mpv::qt::set_property_variant(mpv, name, value);
}
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
{
window()->setPersistentOpenGLContext(true);
window()->setPersistentSceneGraph(true);
return new MpvRenderer(this);
}
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
// Qt sets the locale in the QGuiApplication constructor, but libmpv
// requires the LC_NUMERIC category to be set to "C", so change it back.
std::setlocale(LC_NUMERIC, "C");
qmlRegisterType<MpvObject>("mpvtest", 1, 0, "MpvObject");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:///mpvtest/main.qml"));
view.show();
return app.exec();
}
-36
View File
@@ -1,36 +0,0 @@
#ifndef MPVRENDERER_H_
#define MPVRENDERER_H_
#include <QtQuick/QQuickFramebufferObject>
#include <mpv/client.h>
#include <mpv/opengl_cb.h>
#include <mpv/qthelper.hpp>
class MpvRenderer;
class MpvObject : public QQuickFramebufferObject
{
Q_OBJECT
mpv::qt::Handle mpv;
mpv_opengl_cb_context *mpv_gl;
friend class MpvRenderer;
public:
MpvObject(QQuickItem * parent = 0);
virtual ~MpvObject();
virtual Renderer *createRenderer() const;
public slots:
void command(const QVariant& params);
void setProperty(const QString& name, const QVariant& value);
signals:
void onUpdate();
private slots:
void doUpdate();
private:
static void on_update(void *ctx);
};
#endif
-71
View File
@@ -1,71 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.0
import mpvtest 1.0
Item {
width: 1280
height: 720
MpvObject {
id: renderer
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: renderer.command(["loadfile", "../../../test.mkv"])
}
}
Rectangle {
id: labelFrame
anchors.margins: -50
radius: 5
color: "white"
border.color: "black"
opacity: 0.8
anchors.fill: box
}
Row {
id: box
anchors.bottom: renderer.bottom
anchors.left: renderer.left
anchors.right: renderer.right
anchors.margins: 100
Text {
anchors.margins: 10
wrapMode: Text.WordWrap
text: "QtQuick and mpv are both rendering stuff.\n
Click to load ../../../test.mkv"
}
// Don't take these controls too seriously. They're for testing.
Column {
CheckBox {
id: checkbox
anchors.margins: 10
// Heavily filtered means good, right?
text: "Make video look like on a Smart TV"
onClicked: {
if (checkbox.checked) {
renderer.command(["vo_cmdline", "scale=sharpen3:scale-param1=5.0"])
} else {
renderer.command(["vo_cmdline", ""])
}
}
}
Slider {
id: slider
anchors.margins: 10
anchors.left: checkbox.left
anchors.right: checkbox.right
minimumValue: -100
maximumValue: 100
value: 0
onValueChanged: renderer.setProperty("gamma", slider.value | 0)
}
}
}
}
-12
View File
@@ -1,12 +0,0 @@
QT += qml quick
HEADERS += main.h
SOURCES += main.cpp
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig debug
PKGCONFIG += mpv
RESOURCES += mpvtest.qrc
OTHER_FILES += main.qml
-5
View File
@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/mpvtest">
<file>main.qml</file>
</qresource>
</RCC>
@@ -1,159 +0,0 @@
#include "main.h"
#include <stdexcept>
#include <clocale>
#include <QObject>
#include <QtGlobal>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
static void *get_proc_address(void *ctx, const char *name) {
(void)ctx;
QOpenGLContext *glctx = QOpenGLContext::currentContext();
if (!glctx)
return NULL;
return (void *)glctx->getProcAddress(QByteArray(name));
}
MpvRenderer::MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl)
: mpv(a_mpv), mpv_gl(a_mpv_gl), window(0), size()
{
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
if (r < 0)
throw std::runtime_error("could not initialize OpenGL");
}
MpvRenderer::~MpvRenderer()
{
// Until this call is done, we need to make sure the player remains
// alive. This is done implicitly with the mpv::qt::Handle instance
// in this class.
mpv_opengl_cb_uninit_gl(mpv_gl);
}
void MpvRenderer::paint()
{
window->resetOpenGLState();
// This uses 0 as framebuffer, which indicates that mpv will render directly
// to the frontbuffer. Note that mpv will always switch framebuffers
// explicitly. Some QWindow setups (such as using QQuickWidget) actually
// want you to render into a FBO in the beforeRendering() signal, and this
// code won't work there.
// The negation is used for rendering with OpenGL's flipped coordinates.
mpv_opengl_cb_draw(mpv_gl, 0, size.width(), -size.height());
window->resetOpenGLState();
}
MpvObject::MpvObject(QQuickItem * parent)
: QQuickItem(parent), mpv_gl(0), renderer(0)
{
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
if (!mpv)
throw std::runtime_error("could not create mpv context");
mpv_set_option_string(mpv, "terminal", "yes");
mpv_set_option_string(mpv, "msg-level", "all=v");
if (mpv_initialize(mpv) < 0)
throw std::runtime_error("could not initialize mpv context");
// Make use of the MPV_SUB_API_OPENGL_CB API.
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
// Setup the callback that will make QtQuick update and redraw if there
// is a new video frame. Use a queued connection: this makes sure the
// doUpdate() function is run on the GUI thread.
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
if (!mpv_gl)
throw std::runtime_error("OpenGL not compiled in");
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
Qt::QueuedConnection);
connect(this, &QQuickItem::windowChanged,
this, &MpvObject::handleWindowChanged);
}
MpvObject::~MpvObject()
{
if (mpv_gl)
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
}
void MpvObject::handleWindowChanged(QQuickWindow *win)
{
if (!win)
return;
connect(win, &QQuickWindow::beforeSynchronizing,
this, &MpvObject::sync, Qt::DirectConnection);
connect(win, &QQuickWindow::sceneGraphInvalidated,
this, &MpvObject::cleanup, Qt::DirectConnection);
connect(win, &QQuickWindow::frameSwapped,
this, &MpvObject::swapped, Qt::DirectConnection);
win->setClearBeforeRendering(false);
}
void MpvObject::sync()
{
if (!renderer) {
renderer = new MpvRenderer(mpv, mpv_gl);
connect(window(), &QQuickWindow::beforeRendering,
renderer, &MpvRenderer::paint, Qt::DirectConnection);
}
renderer->window = window();
renderer->size = window()->size() * window()->devicePixelRatio();
}
void MpvObject::swapped()
{
mpv_opengl_cb_report_flip(mpv_gl, 0);
}
void MpvObject::cleanup()
{
if (renderer) {
delete renderer;
renderer = 0;
}
}
void MpvObject::on_update(void *ctx)
{
MpvObject *self = (MpvObject *)ctx;
emit self->onUpdate();
}
// connected to onUpdate(); signal makes sure it runs on the GUI thread
void MpvObject::doUpdate()
{
window()->update();
}
void MpvObject::command(const QVariant& params)
{
mpv::qt::command_variant(mpv, params);
}
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
// Qt sets the locale in the QGuiApplication constructor, but libmpv
// requires the LC_NUMERIC category to be set to "C", so change it back.
std::setlocale(LC_NUMERIC, "C");
qmlRegisterType<MpvObject>("mpvtest", 1, 0, "MpvObject");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:///mpvtest/main.qml"));
view.show();
return app.exec();
}
@@ -1,51 +0,0 @@
#ifndef MPVRENDERER_H_
#define MPVRENDERER_H_
#include <QtQuick/QQuickItem>
#include <mpv/client.h>
#include <mpv/opengl_cb.h>
#include <mpv/qthelper.hpp>
class MpvRenderer : public QObject
{
Q_OBJECT
mpv::qt::Handle mpv;
mpv_opengl_cb_context *mpv_gl;
QQuickWindow *window;
QSize size;
friend class MpvObject;
public:
MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl);
virtual ~MpvRenderer();
public slots:
void paint();
};
class MpvObject : public QQuickItem
{
Q_OBJECT
mpv::qt::Handle mpv;
mpv_opengl_cb_context *mpv_gl;
MpvRenderer *renderer;
public:
MpvObject(QQuickItem * parent = 0);
virtual ~MpvObject();
public slots:
void command(const QVariant& params);
void sync();
void swapped();
void cleanup();
signals:
void onUpdate();
private slots:
void doUpdate();
void handleWindowChanged(QQuickWindow *win);
private:
static void on_update(void *ctx);
};
#endif
@@ -1,49 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 1.0
import mpvtest 1.0
Item {
width: 1280
height: 720
MpvObject {
id: renderer
// This object isn't real and not visible; it just renders into the
// background of the containing Window.
width: 0
height: 0
}
MouseArea {
anchors.fill: parent
onClicked: renderer.command(["loadfile", "../../../test.mkv"])
}
Rectangle {
id: labelFrame
anchors.margins: -50
radius: 5
color: "white"
border.color: "black"
opacity: 0.8
anchors.fill: box
}
Row {
id: box
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 100
Text {
anchors.margins: 10
wrapMode: Text.WordWrap
text: "QtQuick and mpv are both rendering stuff.\n
In this example, mpv is always in the background.\n
Click to load ../../../test.mkv"
}
}
}
@@ -1,12 +0,0 @@
QT += qml quick
HEADERS += main.h
SOURCES += main.cpp
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig debug
PKGCONFIG += mpv
RESOURCES += mpvtest.qrc
OTHER_FILES += main.qml
@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/mpvtest">
<file>main.qml</file>
</qresource>
</RCC>
-225
View File
@@ -1,225 +0,0 @@
// This example can be built with: qmake && make
#include <clocale>
#include <sstream>
#include <stdexcept>
#include <QtGlobal>
#include <QFileDialog>
#include <QStatusBar>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QApplication>
#include <QTextEdit>
#if QT_VERSION >= 0x050000
#include <QJsonDocument>
#endif
#include <mpv/qthelper.hpp>
#include "qtexample.h"
static void wakeup(void *ctx)
{
// This callback is invoked from any mpv thread (but possibly also
// recursively from a thread that is calling the mpv API). Just notify
// the Qt GUI thread to wake up (so that it can process events with
// mpv_wait_event()), and return as quickly as possible.
MainWindow *mainwindow = (MainWindow *)ctx;
emit mainwindow->mpv_events();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle("Qt embedding demo");
setMinimumSize(640, 480);
QMenu *menu = menuBar()->addMenu(tr("&File"));
QAction *on_open = new QAction(tr("&Open"), this);
on_open->setShortcuts(QKeySequence::Open);
on_open->setStatusTip(tr("Open a file"));
connect(on_open, &QAction::triggered, this, &MainWindow::on_file_open);
menu->addAction(on_open);
QAction *on_new = new QAction(tr("&New window"), this);
connect(on_new, &QAction::triggered, this, &MainWindow::on_new_window);
menu->addAction(on_new);
statusBar();
QMainWindow *log_window = new QMainWindow(this);
log = new QTextEdit(log_window);
log->setReadOnly(true);
log_window->setCentralWidget(log);
log_window->setWindowTitle("mpv log window");
log_window->setMinimumSize(500, 50);
log_window->show();
mpv = mpv_create();
if (!mpv)
throw std::runtime_error("can't create mpv instance");
// Create a video child window. Force Qt to create a native window, and
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa
mpv_container = new QWidget(this);
setCentralWidget(mpv_container);
mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors);
mpv_container->setAttribute(Qt::WA_NativeWindow);
// If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
int64_t wid = mpv_container->winId();
mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
// Enable default bindings, because we're lazy. Normally, a player using
// mpv as backend would implement its own key bindings.
mpv_set_option_string(mpv, "input-default-bindings", "yes");
// Enable keyboard input on the X11 window. For the messy details, see
// --input-vo-keyboard on the manpage.
mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
// Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
// this property changes.
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE);
mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE);
// Request log messages with level "info" or higher.
// They are received as MPV_EVENT_LOG_MESSAGE.
mpv_request_log_messages(mpv, "info");
// From this point on, the wakeup function will be called. The callback
// can come from any thread, so we use the QueuedConnection mechanism to
// relay the wakeup in a thread-safe way.
connect(this, &MainWindow::mpv_events, this, &MainWindow::on_mpv_events,
Qt::QueuedConnection);
mpv_set_wakeup_callback(mpv, wakeup, this);
if (mpv_initialize(mpv) < 0)
throw std::runtime_error("mpv failed to initialize");
}
void MainWindow::handle_mpv_event(mpv_event *event)
{
switch (event->event_id) {
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property *prop = (mpv_event_property *)event->data;
if (strcmp(prop->name, "time-pos") == 0) {
if (prop->format == MPV_FORMAT_DOUBLE) {
double time = *(double *)prop->data;
std::stringstream ss;
ss << "At: " << time;
statusBar()->showMessage(QString::fromStdString(ss.str()));
} else if (prop->format == MPV_FORMAT_NONE) {
// The property is unavailable, which probably means playback
// was stopped.
statusBar()->showMessage("");
}
} else if (strcmp(prop->name, "chapter-list") == 0 ||
strcmp(prop->name, "track-list") == 0)
{
// Dump the properties as JSON for demo purposes.
#if QT_VERSION >= 0x050000
if (prop->format == MPV_FORMAT_NODE) {
QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data);
// Abuse JSON support for easily printing the mpv_node contents.
QJsonDocument d = QJsonDocument::fromVariant(v);
append_log("Change property " + QString(prop->name) + ":\n");
append_log(d.toJson().data());
}
#endif
}
break;
}
case MPV_EVENT_VIDEO_RECONFIG: {
// Retrieve the new video size.
int64_t w, h;
if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 &&
w > 0 && h > 0)
{
// Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily
// imply a resize, and you should check yourself if the video
// dimensions really changed.
// mpv itself will scale/letter box the video to the container size
// if the video doesn't fit.
std::stringstream ss;
ss << "Reconfig: " << w << " " << h;
statusBar()->showMessage(QString::fromStdString(ss.str()));
}
break;
}
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
std::stringstream ss;
ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
append_log(QString::fromStdString(ss.str()));
break;
}
case MPV_EVENT_SHUTDOWN: {
mpv_terminate_destroy(mpv);
mpv = NULL;
break;
}
default: ;
// Ignore uninteresting or unknown events.
}
}
// This slot is invoked by wakeup() (through the mpv_events signal).
void MainWindow::on_mpv_events()
{
// 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);
}
}
void MainWindow::on_file_open()
{
QString filename = QFileDialog::getOpenFileName(this, "Open file");
if (mpv) {
const QByteArray c_filename = filename.toUtf8();
const char *args[] = {"loadfile", c_filename.data(), NULL};
mpv_command_async(mpv, 0, args);
}
}
void MainWindow::on_new_window()
{
(new MainWindow())->show();
}
void MainWindow::append_log(const QString &text)
{
QTextCursor cursor = log->textCursor();
cursor.movePosition(QTextCursor::End);
cursor.insertText(text);
log->setTextCursor(cursor);
}
MainWindow::~MainWindow()
{
if (mpv)
mpv_terminate_destroy(mpv);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Qt sets the locale in the QApplication constructor, but libmpv requires
// the LC_NUMERIC category to be set to "C", so change it back.
std::setlocale(LC_NUMERIC, "C");
MainWindow w;
w.show();
return a.exec();
}
+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();
}
@@ -5,8 +5,6 @@
#include <mpv/client.h>
class QTextEdit;
class MainWindow : public QMainWindow
{
Q_OBJECT
@@ -15,20 +13,15 @@ public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
virtual bool event(QEvent *event);
private slots:
void on_file_open();
void on_new_window();
void on_mpv_events();
signals:
void mpv_events();
private:
QWidget *mpv_container;
mpv_handle *mpv;
QTextEdit *log;
void append_log(const QString &text);
void create_player();
void handle_mpv_event(mpv_event *event);
@@ -5,9 +5,8 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = qtexample
TEMPLATE = app
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig debug
PKGCONFIG += mpv
CONFIG += link_pkgconfig
PKGCONFIG = mpv
SOURCES += qtexample.cpp
HEADERS += qtexample.h
-158
View File
@@ -1,158 +0,0 @@
// Build with: gcc -o main main.c `pkg-config --libs --cflags mpv sdl2`
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <mpv/client.h>
#include <mpv/opengl_cb.h>
static Uint32 wakeup_on_mpv_redraw, wakeup_on_mpv_events;
static void die(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
static void *get_proc_address_mpv(void *fn_ctx, const char *name)
{
return SDL_GL_GetProcAddress(name);
}
static void on_mpv_events(void *ctx)
{
SDL_Event event = {.type = wakeup_on_mpv_events};
SDL_PushEvent(&event);
}
static void on_mpv_redraw(void *ctx)
{
SDL_Event event = {.type = wakeup_on_mpv_redraw};
SDL_PushEvent(&event);
}
int main(int argc, char *argv[])
{
if (argc != 2)
die("pass a single media file as argument");
mpv_handle *mpv = mpv_create();
if (!mpv)
die("context init failed");
// Some minor options can only be set before mpv_initialize().
if (mpv_initialize(mpv) < 0)
die("mpv init failed");
if (SDL_Init(SDL_INIT_VIDEO) < 0)
die("SDL init failed");
SDL_Window *window =
SDL_CreateWindow("hi", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
SDL_WINDOW_RESIZABLE);
if (!window)
die("failed to create SDL window");
// The OpenGL API is somewhat separate from the normal mpv API. This only
// returns NULL if no OpenGL support is compiled.
mpv_opengl_cb_context *mpv_gl = mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
if (!mpv_gl)
die("failed to create mpv GL API handle");
SDL_GLContext glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
die("failed to create SDL GL context");
// This makes mpv use the currently set GL context. It will use the callback
// to resolve GL builtin functions, as well as extensions.
if (mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL) < 0)
die("failed to initialize mpv GL context");
// Actually using the opengl_cb state has to be explicitly requested.
// Otherwise, mpv will create a separate platform window.
if (mpv_set_option_string(mpv, "vo", "opengl-cb") < 0)
die("failed to set VO");
// We use events for thread-safe notification of the SDL main loop.
// Generally, the wakeup callbacks (set further below) should do as least
// work as possible, and merely wake up another thread to do actual work.
// On SDL, waking up the mainloop is the ideal course of action. SDL's
// SDL_PushEvent() is thread-safe, so we use that.
wakeup_on_mpv_redraw = SDL_RegisterEvents(1);
wakeup_on_mpv_events = SDL_RegisterEvents(1);
if (wakeup_on_mpv_redraw == (Uint32)-1 || wakeup_on_mpv_events == (Uint32)-1)
die("could not register events");
// When normal mpv events are available.
mpv_set_wakeup_callback(mpv, on_mpv_events, NULL);
// When a new frame should be drawn with mpv_opengl_cb_draw().
// (Separate from the normal event handling mechanism for the sake of
// users which run OpenGL on a different thread.)
mpv_opengl_cb_set_update_callback(mpv_gl, on_mpv_redraw, NULL);
// Play this file. Note that this starts playback asynchronously.
const char *cmd[] = {"loadfile", argv[1], NULL};
mpv_command(mpv, cmd);
while (1) {
SDL_Event event;
if (SDL_WaitEvent(&event) != 1)
die("event loop error");
int redraw = 0;
switch (event.type) {
case SDL_QUIT:
goto done;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
redraw = 1;
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_SPACE)
mpv_command_string(mpv, "cycle pause");
break;
default:
// Happens when a new video frame should be rendered, or if the
// current frame has to be redrawn e.g. due to OSD changes.
if (event.type == wakeup_on_mpv_redraw)
redraw = 1;
// Happens when at least 1 new event is in the mpv event queue.
if (event.type == wakeup_on_mpv_events) {
// Handle all remaining mpv events.
while (1) {
mpv_event *mp_event = mpv_wait_event(mpv, 0);
if (mp_event->event_id == MPV_EVENT_NONE)
break;
printf("event: %s\n", mpv_event_name(mp_event->event_id));
}
}
}
if (redraw) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
// Note:
// - The 0 is the FBO to use; 0 is the default framebuffer (i.e.
// render to the window directly.
// - The negative height tells mpv to flip the coordinate system.
// - If you do not want the video to cover the whole screen, or want
// to apply any form of fancy transformation, you will have to
// render to a FBO.
// - See opengl_cb.h on what OpenGL environment mpv expects, and
// other API details.
mpv_opengl_cb_draw(mpv_gl, 0, w, -h);
SDL_GL_SwapWindow(window);
}
}
done:
// Destroy the GL renderer and all of the GL objects it allocated. If video
// is still running, the video track will be deselected.
mpv_opengl_cb_uninit_gl(mpv_gl);
mpv_terminate_destroy(mpv);
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
static inline void check_error(int status)
{
if (status < 0) {
printf("mpv API error: %s\n", mpv_error_string(status));
exit(1);
}
}
@@ -1,18 +1,9 @@
// Build with: gcc -o simple simple.c `pkg-config --libs --cflags mpv`
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <mpv/client.h>
static inline void check_error(int status)
{
if (status < 0) {
printf("mpv API error: %s\n", mpv_error_string(status));
exit(1);
}
}
#include "libmpv/client.h"
#include "shared.h"
int main(int argc, char *argv[])
{
@@ -30,7 +21,6 @@ int main(int argc, char *argv[])
// Enable default key bindings, so the user can actually interact with
// the player (and e.g. close the window).
check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));
mpv_set_option_string(ctx, "input-vo-keyboard", "yes");
int val = 1;
check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));
-212
View File
@@ -1,212 +0,0 @@
Compiling for Windows
=====================
Compiling for Windows is supported with MinGW-w64. This can be used to produce
both 32-bit and 64-bit executables, and it works for building on Windows and
cross-compiling from Linux and Cygwin. MinGW-w64 is available from:
http://mingw-w64.sourceforge.net.
While building a complete MinGW-w64 toolchain yourself is possible, there are a
few build environments and scripts to help ease the process, such as MSYS2 and
MXE. Note that MinGW environments included in Linux distributions are often
broken, outdated and useless, and usually don't use MinGW-w64.
**Warning**: the original MinGW (http://www.mingw.org) is unsupported.
Cross-compilation
=================
When cross-compiling, you have to run mpv's configure with these arguments:
```bash
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
```
[MXE](http://mxe.cc) makes it very easy to bootstrap a complete MingGW-w64
environment from a Linux machine. See a working example below.
Alternatively, you can try [mingw-w64-cmake](https://github.com/lachs0r/mingw-w64-cmake),
which bootstraps a MinGW-w64 environment and builds mpv and dependencies.
Example with MXE
----------------
```bash
# Before starting, make sure you install MXE prerequisites. MXE will download
# and build all target dependencies, but no host dependencies. For example,
# you need a working compiler, or MXE can't build the crosscompiler.
#
# Refer to
#
# http://mxe.cc/#requirements
#
# Scroll down for disto/OS-specific instructions to install them.
# Download MXE. Note that compiling the required packages requires about 1.4 GB
# or more!
cd /opt
git clone https://github.com/mxe/mxe mxe
cd mxe
# Set build options.
# The JOBS environment variable controls threads to use when building. DO NOT
# use the regular `make -j4` option with MXE as it will slow down the build.
# Alternatively, you can set this in the make command by appending "JOBS=4"
# to the end of command:
echo "JOBS := 4" >> settings.mk
# The MXE_TARGET environment variable builds MinGW-w64 for 32 bit targets.
# Alternatively, you can specify this in the make command by appending
# "MXE_TARGETS=i686-w64-mingw32" to the end of command:
echo "MXE_TARGETS := i686-w64-mingw32.static" >> settings.mk
# If you want to build 64 bit version, use this:
# echo "MXE_TARGETS := x86_64-w64-mingw32.static" >> settings.mk
# Build required packages. The following provide a minimum required to build
# a reasonable mpv binary (though not an absolute minimum).
make gcc ffmpeg libass jpeg pthreads lua
# Add MXE binaries to $PATH
export PATH=/opt/mxe/usr/bin/:$PATH
# Build mpv. The target will be used to automatically select the name of the
# build tools involved (e.g. it will use i686-w64-mingw32.static-gcc).
cd ..
git clone https://github.com/mpv-player/mpv.git
cd mpv
python ./bootstrap.py
DEST_OS=win32 TARGET=i686-w64-mingw32.static ./waf configure
# Or, if 64 bit version,
# DEST_OS=win32 TARGET=x86_64-w64-mingw32.static ./waf configure
./waf build
```
Native compilation with MSYS2
=============================
For Windows developers looking to get started quickly, MSYS2 can be used to
compile mpv natively on a Windows machine. The MSYS2 repositories have binary
packages for most of mpv's dependencies, so the process should only involve
building mpv itself.
To build 64-bit mpv on Windows:
Installing MSYS2
----------------
1. Download an installer from https://msys2.github.io/
It doesn't matter whether the i686 or the x86_64 version is used. Both can
build 32-bit and 64-bit binaries when running on a 64-bit version of Windows.
2. Add ``C:\msys64\mingw64\bin`` to your ``%PATH%``. mpv will depend on several
DLLs in this folder.
3. Start a MinGW-w64 shell (``mingw64_shell.bat``). For a 32-bit build, use
``mingw32_shell.bat``.
Updating MSYS2
--------------
To prevent errors during post-install, ``msys2-runtime`` and ``pacman`` must be
updated first.
```bash
# Check for updates to msys2-runtime and pacman. If there were updates, restart
# MSYS2 before continuing.
pacman -Sy --needed msys2-runtime pacman
# Update everything else
pacman -Su
```
Installing mpv dependencies
---------------------------
```bash
# Install MSYS2 build dependencies and a MinGW-w64 compiler
pacman -S git pkg-config python3 mingw-w64-x86_64-gcc
# Install the most important MinGW-w64 dependencies. libass, libbluray and
# lcms2 are also pulled in as dependencies of ffmpeg.
pacman -S mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-lua51
# Install additional (optional) dependencies
pacman -S mingw-w64-x86_64-libdvdnav mingw-w64-x86_64-libguess
```
For a 32-bit build, install ``mingw-w64-i686-*`` packages instead.
Building mpv
------------
Clone the latest mpv from git and install waf:
```bash
git clone https://github.com/mpv-player/mpv.git && cd mpv
./bootstrap.py
```
Finally, compile and install mpv. Binaries will be installed to
``/mingw64/bin``.
```bash
# For a 32-bit build, use --prefix=/mingw32 instead
./waf configure CC=gcc --prefix=/mingw64
./waf install
```
Or, compile and install both libmpv and mpv:
```bash
./waf configure CC=gcc --enable-libmpv-shared --prefix=/mingw64
./waf install
# waf installs libmpv to the wrong directory, so fix it up
mv -f /mingw64/bin/pkgconfig/mpv.pc /mingw64/lib/pkgconfig/
mv -f /mingw64/bin/libmpv.dll.a /mingw64/lib/
sed -i 's_/mingw64/bin_/mingw64/lib_' /mingw64/lib/pkgconfig/mpv.pc
rmdir /mingw64/bin/pkgconfig
```
Additional dependencies
=======================
pthreads
--------
mpv will use a pthreads wrapper by default. Either pthreads-win32 or
winpthreads should work. The latter is packaged with most MinGW-w64
environments, including MSYS2, so it shouldn't be a problem. If you don't have
a pthreads wrapper or you want to build mpv without one, configure with:
```bash
./waf configure --enable-win32-internal-pthreads
```
libwaio
-------
If you want to use ``--input-file``, you need libwaio. It's available from
git://midipix.org/waio
To compile libwaio in MSYS2, run:
```bash
git clone git://midipix.org/waio && cd waio
# 32-bit build, run from mingw32_shell.bat
./build-mingw-nt32 lib-static CC=gcc AR=ar
cp -r include/waio /mingw32/include
cp lib32/libwaio.a /mingw32/lib
# 64-bit build, run from mingw64_shell.bat
./build-mingw-nt64 lib-static CC=gcc AR=ar
cp -r include/waio /mingw64/include
cp lib64/libwaio.a /mingw64/lib
```
+2 -10
View File
@@ -4,8 +4,8 @@ How to contribute
General
-------
The main contact for mpv development is IRC, specifically #mpv
and #mpv-devel on Freenode.
The main contact for mpv development is IRC, specifically #mpv-player and
#mpv-player-dev on Freenode.
Sending patches
---------------
@@ -14,9 +14,6 @@ Sending patches
``git format-patch``. diffs posted as pastebins (especially if the http link
returns HTML) just cause extra work for everyone, because they lack commit
message and authorship information.
- When creating pull requests, be sure to test your changes. If you didn't, please
say so in the pull request message.
- Write informative commit messages. Use present tense to describe the
situation with the patch applied, and past tense for the situation before
the change.
@@ -54,9 +51,6 @@ Sending patches
change in the same commit.
- If you add a new command line option, document it in options.rst. If you
add a new input property, document it in input.rst.
- All new code must be LGPLv2.1+ licensed, or come with the implicit agreement
that it will be relicensed to LGPLv2.1+ later (see ``Copyright`` in the
repository root directory).
Code formatting
---------------
@@ -124,8 +118,6 @@ mpv uses C99 with K&R formatting, with some exceptions.
very_long_condition_b)
{
code();
} else {
...
}
```
- Remove any trailing whitespace.
+74
View File
@@ -0,0 +1,74 @@
Cross Compiling to Windows
==========================
Cross compiling mpv to Windows is supported with MinGW-w64. This can be used to
produce both 32 bit and 64 bit executables. MinGW-w64 is available from
http://mingw-w64.sourceforge.net.
You have to run mpv's configure with these arguments:
```bash
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
```
While building a complete MinGW-w64 toolchain yourself is possible, people have
created scripts to help ease the process. These are the two recommended methods:
- Using [mingw-w64-cmake](https://github.com/lachs0r/mingw-w64-cmake) to setup
a MinGW-w64 environment. We recommend you to try this first before MXE.
mingw-w64-cmake will also build mpv and its dependencies.
- Alternatively, you can use MXE: http://mxe.cc. With MXE, you have to manually
set the target to MinGW-w64 (even if you compile to 32 bit). A working example
below.
**Warning**: the original MinGW (http://www.mingw.org) is unsupported.
Note that MinGW environments included in Linux distributions are often broken,
outdated and useless, and usually don't use MinGW-w64.
Example with MXE
----------------
```bash
# Download MXE. Note that compiling the required packages requires about 1.4 GB
# or more!
cd /opt
git clone https://github.com/mxe/mxe mingw
cd mingw
# Set build options.
# The JOBS environment variable controls threads to use when building. DO NOT
# use the regular `make -j4` option with MXE as it will slow down the build.
# Alternatively, you can set this in the make command by appending "JOBS=4"
# to the end of command:
echo "JOBS := 4" >> settings.mk
# The MXE_TARGET environment variable builds MinGW-w64 for 32 bit targets.
# Alternatively, you can specify this in the make command by appending
# "MXE_TARGETS=i686-w64-mingw32" to the end of command:
echo "MXE_TARGETS := i686-w64-mingw32" >> settings.mk
# If you want to build 64 bit version, use this:
# echo "MXE_TARGETS := x86_64-w64-mingw32" >> settings.mk
# Build required packages. The following provide a minimum required to build
# mpv.
make gcc ffmpeg libass jpeg pthreads
# Add MXE binaries to $PATH
export PATH=/opt/mingw/usr/bin/:$PATH
# Build mpv. The target will be used to automatically select the name of the
# build tools involved (e.g. it will use i686-w64-mingw32-gcc).
cd ..
git clone https://github.com/mpv-player/mpv.git
cd mpv
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
# Or, if 64 bit version,
# DEST_OS=win32 TARGET=x86_64-w64-mingw32 ./waf configure
./waf build
```
+4 -4
View File
@@ -27,13 +27,13 @@ The general simplified syntax is::
<filename>,<start in seconds>,<length in seconds>
If the start time is omitted, 0 is used. If the length is omitted, the
estimated remaining duration of the source file is used.
estimated duration of the source file is used.
Note::
mpv can't use ordered chapter files in EDL entries. Usage of relative or
absolute paths as well as any protocol prefixes is prevented for security
reasons.
mpv can't use ordered chapter files or libquvi-resolved URLs in EDL
entries. Usage of relative or absolute paths as well as any protocol
prefixes is prevented for security reasons.
Syntax of mpv EDL files
-90
View File
@@ -1,90 +0,0 @@
Introduction
============
mpv provides access to its internal via the following means:
- options
- commands
- properties
- events
All of these are important for interfacing both with end users and API users
(which include Lua scripts, libmpv, and the JSON IPC). As such, they constitute
a large part of the user interface and APIs.
This document lists changes to them. New changes are added to the top.
Interface changes
=================
::
--- mpv 0.11.0 ---
- add "af-metadata" property
--- mpv 0.10.0 ---
- add --video-aspect-method option
- add --playlist-pos option
- add --video-sync* options
"display-sync-active" property
"vo-missed-frame-count" property
"audio-speed-correction" and "video-speed-correction" properties
- remove --demuxer-readahead-packets and --demuxer-readahead-bytes
add --demuxer-max-packets and --demuxer-max-bytes
(the new options are not replacement and have very different semantics)
- change "video-aspect" property: always settable, even if no video is
running; always return the override - if no override is set, return
the video's aspect ratio
- remove disc-nav (DVD, BD) related properties and commands
- add "option-info/<name>/set-locally" property
- add --cache-backbuffer; change --cache-default default to 75MB
the new total cache size is the sum of backbuffer and the cache size
specified by --cache-default or --cache
- add ``track-list/N/audio-channels`` property
- change --screenshot-tag-colorspace default value
- add --stretch-image-subs-to-screen
- add "playlist/N/title" property
- add --video-stereo-mode=no to disable auto-conversions
- add --force-seekable, and change default seekability in some cases
- add vf yadif/vavpp/vdpaupp interlaced-only suboptions
Also, the option is enabled by default (Except vf_yadif, which has
it enabled only if it's inserted by the deinterlace property.)
- add --hwdec-preload
- add ao coreaudio exclusive suboption
- add ``track-list/N/forced`` property
- add audio-params/channel-count and ``audio-params-out/channel-count props.
- add af volume replaygain-fallback suboption
- add video-params/stereo-in property
- add "keypress", "keydown", and "keyup" commands
- deprecate --ad-spdif-dtshd and enabling passthrough via --ad
add --audio-spdif as replacement
- remove "get_property" command
- remove --slave-broken
- add vo opengl custom shader suboptions (source-shader, scale-shader,
pre-shaders, post-shaders)
- completely change how the hwdec properties work:
- "hwdec" now reflects the --hwdec option
- "hwdec-detected" does partially what the old "hwdec" property did
(and also, "detected-hwdec" is removed)
- "hwdec-active" is added
- add protocol-list property
- deprecate audio-samplerate and audio-channels properties
(audio-params sub-properties are the replacement)
- add audio-params and audio-out-params properties
- deprecate "audio-format" property, replaced with "audio-codec-name"
- deprecate --media-title, replaced with --force-media-title
- deprecate "length" property, replaced with "duration"
- change volume property:
- the value 100 is now always "unchanged volume" - with softvol, the
range is 0 to --softvol-max, without it is 0-100
- the minimum value of --softvol-max is raised to 100
- remove vo opengl npot suboption
- add relative seeking by percentage to "seek" command
- add playlist_shuffle command
- add --force-window=immediate
- add ao coreaudio change-physical-format suboption
- remove vo opengl icc-cache suboption, add icc-cache-dir suboption
- add --screenshot-directory
- add --screenshot-high-bit-depth
- add --screenshot-jpeg-source-chroma
- default action for "rescan_external_files" command changes
--- mpv 0.9.0 ---
+211 -56
View File
@@ -11,10 +11,6 @@ syntax is:
To get a full list of available audio filters, see ``--af=help``.
Also, keep in mind that most actual filters are available via the ``lavfi``
wrapper, which gives you access to most of libavfilter's filters. This
includes all filters that have been ported from MPlayer to libavfilter.
You can also set defaults for each filter. The defaults are applied before the
normal filter parameters.
@@ -63,18 +59,11 @@ Available filters are:
(If you just want to set defaults for this filter that will be used
even by automatically inserted lavrresample instances, you should
prefer setting them with ``--af-defaults=lavrresample:...``.)
``normalize=<yes|no>``
Whether to normalize when remixing channel layouts (default: yes). This
is e.g. applied when downmixing surround audio to stereo. The advantage
is that this guarantees that no clipping can happen. Unfortunately,
this can also lead to too low volume levels. Whether you enable or
disable this is essentially a matter of taste, but the default uses
the safer choice.
``o=<string>``
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
be documented by FFmpeg or Libav.
``lavcac3enc[=tospdif[:bitrate[:minch]]]``
``lavcac3enc[=tospdif[:bitrate[:minchn]]]``
Encode multi-channel audio to AC-3 at runtime using libavcodec. Supports
16-bit native-endian input format, maximum 6 channels. The output is
big-endian when outputting a raw AC-3 stream, native-endian when
@@ -88,12 +77,10 @@ Available filters are:
``bitrate=<rate>``
The bitrate use for the AC-3 stream. Set it to 384 to get 384 kbps.
The default is 640. Some receivers might not be able to handle this.
Valid values: 32, 40, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, 384, 448, 512, 576, 640.
The special value ``auto`` selects a default bitrate based on the
The special value ``default`` selects a default bitrate based on the
input channel number:
:1ch: 96
@@ -103,9 +90,62 @@ Available filters are:
:5ch: 448
:6ch: 448
``minch=<n>``
If the input channel number is less than ``<minch>``, the filter will
detach itself (default: 3).
``minchn=<n>``
If the input channel number is less than ``<minchn>``, the filter will
detach itself (default: 5).
``sweep[=speed]``
Produces a sine sweep.
``<0.0-1.0>``
Sine function delta, use very low values to hear the sweep.
``sinesuppress[=freq:decay]``
Remove a sine at the specified frequency. Useful to get rid of the 50/60 Hz
noise on low quality audio equipment. It only works on mono input.
``<freq>``
The frequency of the sine which should be removed (in Hz) (default:
50)
``<decay>``
Controls the adaptivity (a larger value will make the filter adapt to
amplitude and phase changes quicker, a smaller value will make the
adaptation slower) (default: 0.0001). Reasonable values are around
0.001.
``bs2b[=option1:option2:...]``
Bauer stereophonic to binaural transformation using libbs2b. Improves the
headphone listening experience by making the sound similar to that from
loudspeakers, allowing each ear to hear both channels and taking into
account the distance difference and the head shadowing effect. It is
applicable only to 2-channel audio.
``fcut=<300-1000>``
Set cut frequency in Hz.
``feed=<10-150>``
Set feed level for low frequencies in 0.1*dB.
``profile=<value>``
Several profiles are available for convenience:
:default: will be used if nothing else was specified (fcut=700,
feed=45)
:cmoy: Chu Moy circuit implementation (fcut=700, feed=60)
:jmeier: Jan Meier circuit implementation (fcut=650, feed=95)
If ``fcut`` or ``feed`` options are specified together with a profile, they
will be applied on top of the selected profile.
``hrtf[=flag]``
Head-related transfer function: Converts multichannel audio to 2-channel
output for headphones, preserving the spatiality of the sound.
==== ===================================
Flag Meaning
==== ===================================
m matrix decoding of the rear channel
s 2-channel matrix decoding
0 no matrix decoding (default)
==== ===================================
``equalizer=g1:g2:g3:...:g10``
10 octave band graphic equalizer, implemented using 10 IIR band-pass
@@ -183,21 +223,14 @@ Available filters are:
automatically up- and downmix standard channel layouts.
``format=format:srate:channels:out-format:out-srate:out-channels``
Does not do any format conversion itself. Rather, it may cause the
filter system to insert necessary conversion filters before or after this
filter if needed. It is primarily useful for controlling the audio format
going into other filters. To specify the format for audio output, see
``--audio-format``, ``--audio-samplerate``, and ``--audio-channels``. This
filter is able to force a particular format, whereas ``--audio-*``
may be overridden by the ao based on output compatibility.
Force a specific audio format/configuration without actually changing the
audio data. Keep in mind that the filter system might auto-insert actual
conversion filters before or after this filter if needed.
All parameters are optional. The first 3 parameters restrict what the filter
accepts as input. They will therefore cause conversion filters to be
inserted before this one. The ``out-`` parameters tell the filters or audio
outputs following this filter how to interpret the data without actually
doing a conversion. Setting these will probably just break things unless you
really know you want this for some reason, such as testing or dealing with
broken media.
accepts as input. The ``out-`` parameters change the audio format, without
actually doing a conversion. The data will be 'reinterpreted' by the
filters or audio outputs following this filter.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
@@ -217,9 +250,22 @@ Available filters are:
``<out-channels>``
*NOTE*: this filter used to be named ``force``. The old ``format`` filter
used to do conversion itself, unlike this one which lets the filter system
handle the conversion.
See also ``--audio-format``, ``--audio-samplerate``, and
``--audio-channels`` for related options. Keep in mind that
``--audio-channels`` does not actually force the number of
channels in most cases, while this filter can do this.
*NOTE*: this filter used to be named ``force``. Also, unlike the old
``format`` filter, this does not do any actual conversion anymore.
Conversion is done by other, automatically inserted filters.
``convert24``
Filter for internal use only. Converts between 24-bit and 32-bit sample
formats.
``convertsignendian``
Filter for internal use only. Converts between signed/unsigned formats
and formats with different endian.
``volume[=<volumedb>[:...]]``
Implements software volume control. Use this filter with caution since it
@@ -245,10 +291,6 @@ Available filters are:
``replaygain-clip=yes|no``
Prevent clipping caused by replaygain by automatically lowering the
gain (default). Use ``replaygain-clip=no`` to disable this.
``replaygain-fallback``
Gain in dB to apply if the file has no replay gain tags. This option
is always applied if the replaygain logic is somehow inactive. If this
is applied, no other replaygain options are applied.
``softclip``
Turns soft clipping on. Soft-clipping can make the
sound more smooth if very high volume levels are used. Enable this
@@ -312,6 +354,64 @@ Available filters are:
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
remixing audio to 5.1 and output it like this.
``sub[=fc:ch]``
Adds a subwoofer channel to the audio stream. The audio data used for
creating the subwoofer channel is an average of the sound in channel 0 and
channel 1. The resulting sound is then low-pass filtered by a 4th order
Butterworth filter with a default cutoff frequency of 60Hz and added to a
separate channel in the audio stream.
.. warning::
Disable this filter when you are playing media with an LFE channel
(e.g. 5.1 surround sound), otherwise this filter will disrupt the sound
to the subwoofer.
``<fc>``
cutoff frequency in Hz for the low-pass filter (20 Hz to 300 Hz)
(default: 60 Hz) For the best result try setting the cutoff frequency
as low as possible. This will improve the stereo or surround sound
experience.
``<ch>``
Determines the channel number in which to insert the sub-channel
audio. Channel number can be between 0 and 7 (default: 5). Observe
that the number of channels will automatically be increased to <ch> if
necessary.
.. admonition:: Example
``mpv --af=sub=100:4 --audio-channels=5 media.avi``
Would add a subwoofer channel with a cutoff frequency of 100 Hz to
output channel 4.
``center``
Creates a center channel from the front channels. May currently be low
quality as it does not implement a high-pass filter for proper extraction
yet, but averages and halves the channels instead.
``<ch>``
Determines the channel number in which to insert the center channel.
Channel number can be between 0 and 7 (default: 5). Observe that the
number of channels will automatically be increased to ``<ch>`` if
necessary.
``surround[=delay]``
Decoder for matrix encoded surround sound like Dolby Surround. Some files
with 2-channel audio actually contain matrix encoded surround sound.
``<delay>``
delay time in ms for the rear speakers (0 to 1000) (default: 20) This
delay should be set as follows: If d1 is the distance from the
listening position to the front speakers and d2 is the distance from
the listening position to the rear speakers, then the delay should be
set to 15ms if d1 <= d2 and to 15 + 5*(d1-d2) if d1 > d2.
.. admonition:: Example
``mpv --af=surround=15 --audio-channels=4 media.avi``
Would add surround sound decoding with 15 ms delay for the sound to
the rear speakers.
``delay[=[ch1,ch2,...]]``
Delays the sound to the loudspeakers such that the sound from the
different channels arrives at the listening position simultaneously. It is
@@ -340,6 +440,36 @@ Available filters are:
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
(``mmap()``). Memory mapped areas contain a header::
int nch /* number of channels */
int size /* buffer size */
unsigned long long counter /* Used to keep sync, updated every time
new data is exported. */
The rest is payload (non-interleaved) 16-bit data.
``<mmapped_file>``
File to map data to (required)
``<nsamples>``
number of samples per channel (default: 512).
.. admonition:: Example
``mpv --af=export=/tmp/mpv-af_export:1024 media.avi``
Would export 1024 samples per channel to ``/tmp/mpv-af_export``.
``extrastereo[=mul]``
(Linearly) increases the difference between left and right channels which
adds some sort of "live" effect to playback.
``<mul>``
Sets the difference coefficient (default: 2.5). 0.0 means mono sound
(average of both channels), with 1.0 sound will be unchanged, with
-1.0 left and right channels will be swapped.
``drc[=method:target]``
Applies dynamic range compression. This maximizes the volume by compressing
the audio signal's dynamic range. (Formerly called ``volnorm``.)
@@ -363,6 +493,45 @@ Available filters are:
This filter can cause distortion with audio signals that have a very
large dynamic range.
``ladspa=file:label:[<control0>,<control1>,...]``
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
filter is reentrant, so multiple LADSPA plugins can be used at once.
``<file>``
Specifies the LADSPA plugin library file.
.. note::
See also the note about the ``LADSPA_PATH`` variable in the
`ENVIRONMENT VARIABLES`_ section.
``<label>``
Specifies the filter within the library. Some libraries contain only
one filter, but others contain many of them. Entering 'help' here
will list all available filters within the specified library, which
eliminates the use of 'listplugins' from the LADSPA SDK.
``[<control0>,<control1>,...]``
Controls are zero or more ``,`` separated floating point values that
determine the behavior of the loaded plugin (for example delay,
threshold or gain).
In verbose mode (add ``-v`` to the mpv command line), all
available controls and their valid ranges are printed. This eliminates
the use of 'analyseplugin' from the LADSPA SDK.
Note that ``,`` is already used by the option parser to separate
filters, so you must quote the list of values with ``[...]`` or
similar.
.. admonition:: Example
``mpv --af=ladspa='/usr/lib/ladspa/delay.so':delay_5s:[0.5,0.2] media.avi``
Does something.
``karaoke``
Simple voice removal filter exploiting the fact that voice is usually
recorded with mono gear and later 'center' mixed onto the final audio
stream. Beware that this filter will turn your signal into mono. Works
well for 2 channel tracks; do not bother trying it on anything but 2
channel stereo.
``scaletempo[=option1:option2:...]``
Scales audio tempo without altering pitch, optionally synced to playback
speed (default).
@@ -395,10 +564,9 @@ Available filters are:
Scale tempo in sync with speed (default).
pitch
Reverses effect of filter. Scales pitch without altering tempo.
Add this to your ``input.conf`` to step by musical semi-tones::
[ multiply speed 0.9438743126816935
] multiply speed 1.059463094352953
Add ``[ speed_mult 0.9438743126816935`` and ``] speed_mult
1.059463094352953`` to your ``input.conf`` to step by musical
semi-tones.
.. warning::
@@ -431,20 +599,6 @@ Available filters are:
Changing playback speed would change pitch, leaving audio tempo at
1.2x.
``rubberband``
High quality pitch correction with librubberband. This can be used in place
of ``scaletempo``, and will be used to adjust audio pitch when playing
at speed different from normal.
This filter has a number of sub-options. You can list them with
``mpv --af=rubberband=help``. This will also show the default values
for each option. The options are not documented here, because they are
merely passed to librubberband. Look at the librubberband documentation
to learn what each option does:
http://breakfastquay.com/rubberband/code-doc/classRubberBand_1_1RubberBandStretcher.html
(The mapping of the mpv rubberband filter sub-option names and values to
those of librubberband follows a simple pattern: ``"Option" + Name + Value``.)
``lavfi=graph``
Filter audio using FFmpeg's libavfilter.
@@ -459,3 +613,4 @@ Available filters are:
``o=<string>``
AVOptions.
+44 -96
View File
@@ -28,8 +28,8 @@ normal driver parameters.
.. admonition:: Examples
- ``--ao=alsa,oss,`` Try the ALSA driver, then the OSS driver, then others.
- ``--ao=alsa:resample=yes:device=[plughw:0,3]`` Lets ALSA resample and
sets the device-name as first card, fourth device.
- ``--ao=alsa:no-block:device=[hw:0,3]`` Sets noblock-mode and the
device-name as first card, fourth device.
Available audio output drivers are:
@@ -39,6 +39,8 @@ Available audio output drivers are:
``device=<device>``
Sets the device name. For ac3 output via S/PDIF, use an "iec958" or
"spdif" device, unless you really know how to set it correctly.
``no-block``
Sets noblock-mode.
``resample=yes``
Enable ALSA resampling plugin. (This is disabled by default, because
some drivers report incorrect audio delay in some cases.)
@@ -55,14 +57,6 @@ Available audio output drivers are:
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.
``ignore-chmap``
Don't read or set the channel map of the ALSA device - only request the
required number of channels, and then pass the audio as-is to it. This
option most likely should not be used. It can be useful for debugging,
or for static setups with a specially engineered ALSA configuration (in
this case you should always force the same layout with ``--audio-channels``,
or it will work only for files which use the layout implicit to your
ALSA device).
.. note::
@@ -76,42 +70,6 @@ Available audio output drivers are:
shells (like zsh), you have to quote the option string to prevent the
shell from interpreting the brackets instead of passing them to mpv.
Actually, you should use the ``--audio-device`` option, instead of
setting the device directly.
.. warning::
Handling of multichannel/surround audio changed in mpv 0.8.0 from the
behavior in MPlayer/mplayer2 and older versions of mpv.
The old behavior is that the player always downmixed to stereo by
default. The ``--audio-channels`` (or ``--channels`` before that) option
had to be set to get multichannel audio. Then playing stereo would
use the ``default`` device (which typically allows multiple programs
to play audio at the same time via dmix), while playing anything with
more channels would open one of the hardware devices, e.g. via the
``surround51`` alias (typically with exclusive access). Whether the
player would use exclusive access or not would depend on the file
being played.
The new behavior since mpv 0.8.0 always enables multichannel audio,
i.e. ``--audio-channels=auto`` is the default. However, since ALSA
provides no good way to play multichannel audio in a non-exclusive
way (without blocking other applications from using audio), the player
is restricted to the capabilities of the ``default`` device by default,
which means it supports only stereo and mono (at least with current
typical ALSA configurations). But if a hardware device is selected,
then multichannel audio will typically work.
The short story is: if you want multichannel audio with ALSA, use
``--audio-device`` to select the device (use ``--audio-device=help``
to get a list of all devices and their mpv name).
You can also try `using the upmix plugin <http://git.io/vfuAy>`_.
This setup enables multichannel audio on the ``default`` device
with automatic upmixing with shared access, so playing stereo
and multichannel audio at the same time will work as expected.
``oss``
OSS audio output driver
@@ -156,29 +114,25 @@ Available audio output drivers are:
with fixed setups.
``coreaudio`` (Mac OS X only)
Native Mac OS X audio output driver using AudioUnits and the CoreAudio
Native Mac OS X audio output driver using the AudioUnits and the CoreAudio
sound server.
Automatically redirects to ``coreaudio_exclusive`` when playing compressed
formats.
``device_id=<id>``
ID of output device to use (0 = default device)
``help``
List all available output devices with their IDs.
``change-physical-format=<yes|no>``
Change the physical format to one similar to the requested audio format
(default: no). This has the advantage that multichannel audio output
will actually work. The disadvantage is that it will change the
system-wide audio settings. This is equivalent to changing the ``Format``
setting in the ``Audio Devices`` dialog in the ``Audio MIDI Setup``
utility. Note that this does not effect the selected speaker setup.
``exclusive``
Use exclusive mode access. This merely redirects to
``coreaudio_exclusive``, but should be preferred over using that AO
directly.
``coreaudio_exclusive`` (Mac OS X only)
``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)
``help``
List all available output devices with their IDs.
``openal``
Experimental OpenAL audio output driver
@@ -200,15 +154,23 @@ Available audio output drivers are:
changes. Default: 250.
``latency-hacks=<yes|no>``
Enable hacks to workaround PulseAudio timing bugs (default: no). If
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 or
some plugins, while enabling it might help in some unknown situations
(it used to be required to get good behavior on old PulseAudio versions).
information. Disabling this might help with e.g. networked audio.
If you have stuttering video when using pulse, try to enable this
option. (Or alternatively, try to update PulseAudio.)
``portaudio``
PortAudio audio output driver. This works on all platforms, and has
extensive MS Windows support.
.. note:: This driver is not very useful. It was added in the hope of
providing portable audio API across all platforms, but turned
out semi-broken and underfeatured.
``device``
Specify the subdevice to use. Giving ``help`` as device name lists all
devices found by PortAudio. Devices can be given as numeric values,
starting from ``1``.
``dsound`` (Windows only)
DirectX DirectSound audio output driver
@@ -219,9 +181,6 @@ Available audio output drivers are:
Sets the device number to use. Playing a file with ``-v`` will show a
list of available devices.
``buffersize=<ms>``
DirectSound buffer size in milliseconds (default: 200).
``sdl``
SDL 1.2+ audio output driver. Should work on any platform supported by SDL
1.2, but may require the ``SDL_AUDIODRIVER`` environment variable to be set
@@ -268,13 +227,6 @@ Available audio output drivers are:
Simulate broken audio drivers, which always add the fixed device
latency to the reported audio playback position.
``broken-delay``
Simulate broken audio drivers, which don't report latency correctly.
``channel-layouts``
If not empty, this is a ``,`` separated list of channel layouts the
AO allows. This can be used to test channel layout selection.
``pcm``
Raw PCM/WAVE file writer audio output
@@ -285,10 +237,6 @@ Available audio output drivers are:
Write the sound to ``<filename>`` instead of the default
``audiodump.wav``. If ``no-waveheader`` is specified, the default is
``audiodump.pcm``.
``(no-)append``
Append to the file, instead of overwriting it. Always use this with the
``no-waveheader`` option - with ``waveheader`` it's broken, because
it will write a WAVE header every time the file is opened.
``rsound``
Audio output to an RSound daemon
@@ -319,19 +267,19 @@ Available audio output drivers are:
``wasapi``
Audio output to the Windows Audio Session API.
``device=<id>``
Uses the requested endpoint instead of the system's default audio
endpoint. Both the number and the ID String are valid; the ID String
is guaranteed to not change unless the driver is uninstalled.
Also supports searching active devices by name. If more than one
device matches the name, refuses loading it.
To get a list of the valid devices, give ``help`` as the id. The
list is the same as the ``list`` suboption, but stops the player
initialization.
``exclusive``
Requests exclusive, direct hardware access. By definition prevents
sound playback of any other program until mpv exits.
``device=<id>``
Uses the requested endpoint instead of the system's default audio
endpoint. Both an ordinal number (0,1,2,...) and the GUID
String are valid; the GUID string is guaranteed to not change
unless the driver is uninstalled.
Also supports searching active devices by human readable name. If more
than one device matches the name, refuses loading it.
This option is mostly deprecated in favour of the more general
``--audio-device`` option. That said, ``--audio-device=help`` will give
a list of valid device GUIDs (prefixed with ``wasapi/``), as well as
their human readable names, which should work here.
``list``
Lists all audio endpoints (output devices) present in the system.
+380 -10
View File
@@ -1,13 +1,383 @@
CHANGELOG
=========
CHANGES FROM OTHER VERSIONS OF MPLAYER
======================================
There is no real changelog, but you can look at the following things:
**mpv** is based on mplayer2, which in turn is based on the original MPlayer
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
large part of which is incompatible or completely changes how the player
behaves. Although there are still many similarities to its ancestors, **mpv**
should generally be treated as a completely different program.
* The release changelog, which should contain most user-visible changes,
including new features and bug fixes:
.. note::
These lists are incomplete.
https://github.com/mpv-player/mpv/releases
* The git log, which is the "real" changelog
* The file ``mplayer-changes.rst`` in the ``DOCS`` sub directory on the git
repository, which used to be in place of this section. It documents some
changes that happened since mplayer2 forked off MPlayer.
General Changes for MPlayer to mpv
----------------------------------
* Switch to GPLv2+. (Technically speaking, MPlayer as a whole seems to be
GPLv2-only, while mplayer2 is GPLv3+ - see ``Copyright`` file for details.)
* Removal of the internal GUI, MEncoder, OSD menu, video kernel drivers for
Linux 2.4 (including VIDIX)
* Large internal cleanups
* Removal of support for dead platforms
* 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 OS X
* General OS X improvements
* Improvements in audio/video sync handling
* Cleaned up terminal output
* Gapless audio support (``--gapless-audio``)
* Improved responsiveness on user input
* Support for modifier keys (alt, shift, ctrl) in input.conf
* OSS4 volume control
* More correct color reproduction (color matrix generation)
* Use libass for subtitle rendering by default (better quality)
* Generally preferring FFmpeg/Libav over internal demuxers and decoders
* Improvements when playing multiple files (``--fixed-vo``)
* Screenshot improvements (instant screenshots without 1-frame delay, allow
taking screenshots even with hardware decoding)
* Improved support for PulseAudio
* 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
languages like Arabic should be better supported.
* Cleaned up terminal output (nicer status line, less useless noise)
* Support for playing URLs of popular streaming sites directly
(e.g. ``mpv https://www.youtube.com/watch?v=...``)
* Improved OpenGL output (``--vo=opengl-hq``)
* Make ``--softvol`` default (**mpv** is not a mixer control panel)
* Improved support for .cue files
* Screenshot improvements (can save screenshots as 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 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``)
* Remove ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding can
handle these use cases. For yuv4mpeg, for example, use:
``mpv input.mkv -o output.y4m --no-audio --oautofps --oneverdrop``.
* Image subtitles (DVDs etc.) are rendered in color and use more correct
positioning (color can be disabled with ``--sub-gray``)
* Wayland support
* Support for precise scrolling which scales the parameter of commands. If the
input doesn't support precise scrolling the scale factor stays 1.
* 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.
* 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).
* Allow customizing whether a key binding for seeking shows the video time, the
OSD bar, or nothing (see section `Input Command Prefixes`_).
* Display list of chapters and audio/subtitle tracks on OSD (see section
`Properties`_).
* ``--keep-open`` option to stop the player from closing the window and
exiting after playback ends.
* Re-enable screensaver while the player is paused.
* Matroska edition switching at runtime.
* Support for libavfilter (for video->video and audio->audio). This allows
using most of FFmpeg's filters, which improve greatly on the old MPlayer
filters in features, performance, and correctness.
* Improved downmixing and output of surround audio. Instead of using hardcoded
pan filters to do remixing, use libavresample. Channel maps are used to
identify the channel layout, so e.g. ``3.0`` and ``2.1`` audio can be
distinguished.
* Allow resuming playback at a later point with ``Shift+q``, also see
``quit_watch_later`` input command.
* Support mapping multiple commands to one key.
* Allow changing/adjusting video filters at runtime. (This is also used to make
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise.)
* Native VAAPI support
* 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`_)
* A client API, that allows embedding **mpv** into applications
(see ``libmpv/client.h`` in the sources)
* General bug fixes and removal of long-standing issues
* General code cleanups (including refactoring or rewrites of many parts)
* Many more changes
Detailed Listing of User-visible Changes
----------------------------------------
This listing is about changed command line switches, slave commands, and similar
things. Completely removed features are not listed.
Command Line Switches
~~~~~~~~~~~~~~~~~~~~~
* There is a new command line syntax, which is generally preferred over the old
syntax. ``-optname optvalue`` becomes ``--optname=optvalue``.
The old syntax will not be removed. However, the new syntax is mentioned in
all documentation and so on, and unlike the old syntax is not ambiguous,
so it is a good thing to know about this change.
* In general, negating switches like ``-noopt`` now have to be written as
``-no-opt`` or ``--no-opt``.
* Per-file options are not the default anymore. You can explicitly specify
file-local options. See ``Usage`` section.
* Many options have been renamed, removed or changed semantics. Some options
that are required for a good playback experience with MPlayer are now
superfluous or even worse than the defaults, so make sure to read the manual
before trying to use your existing configuration with **mpv**.
* Table of renamed/replaced switches:
=========================== ========================================
Old New
=========================== ========================================
``-no<opt>`` ``--no-<opt>`` (add a dash)
``-a52drc level`` ``--ad-lavc-ac3drc=level``
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
``-af volnorm`` ``--af=drc`` (renamed)
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
``-aspect`` ``--video-aspect``
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
``-ass`` ``--sub-ass``
``-audiofile-cache`` (removed; the main cache settings are used)
``-audiofile`` ``--audio-file``
``-benchmark`` ``--untimed`` (no stats)
``-capture`` ``--stream-capture=<filename>``
``-channels`` ``--audio-channels`` (changed semantics)
``-cursor-autohide-delay`` ``--cursor-autohide``
``-delay`` ``--audio-delay``
``-dumpstream`` ``--stream-dump=<filename>``
``-dvdangle`` ``--dvd-angle``
``-endpos`` ``--length``
``-font`` ``--osd-font``
``-forcedsubsonly`` ``--sub-forced-only``
``-forceidx`` ``--index``
``-format`` ``--audio-format``
``-fsmode-dontuse`` (removed)
``-fstype`` ``--x11-netwm`` (changed semantics)
``-hardframedrop`` ``--framedrop=hard``
``-identify`` (removed; use TOOLS/mpv_identify.sh)
``-idx`` ``--index``
``-lavdopts ...`` ``--vd-lavc-...``
``-lavfdopts`` ``--demuxer-lavf-...``
``-lircconf`` ``--input-lirc-conf``
``-loop 0`` ``--loop=inf``
``-mixer-channel`` AO suboptions (``alsa``, ``oss``)
``-mixer`` AO suboptions (``alsa``, ``oss``)
``-mouse-movements`` ``--input-cursor``
``-msgcolor`` ``--msg-color``
``-msglevel`` ``--msg-level`` (changed semantics)
``-msgmodule`` ``--msg-module``
``-name`` ``--x11-name``
``-noar`` ``--no-input-appleremote``
``-noautosub`` ``--no-sub-auto``
``-noconsolecontrols`` ``--no-input-terminal``
``-nojoystick`` ``--no-input-joystick``
``-nosound`` ``--no-audio``
``-osdlevel`` ``--osd-level``
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
``-playing-msg`` ``--term-playing-msg``
``-pp ...`` ``'--vf=pp=[...]'``
``-pphelp`` ``--vf=pp:help``
``-rawaudio ...`` ``--demuxer-rawaudio-...``
``-rawvideo ...`` ``--demuxer-rawvideo-...``
``-spugauss`` ``--sub-gauss``
``-srate`` ``--audio-samplerate``
``-ss`` ``--start``
``-ssf <sub>`` ``--sws-...``
``-stop-xscreensaver`` ``--stop-screensaver``
``-sub-fuzziness`` ``--sub-auto``
``-sub`` ``--sub-file``
``-subcp`` ``--sub-codepage``
``-subdelay`` ``--sub-delay``
``-subfile`` ``--sub-file``
``-subfont-*`` ``--sub-text-*``, ``--osd-*``
``-subfont-text-scale`` ``--sub-scale``
``-subfont`` ``--sub-text-font``
``-subfps`` ``--sub-fps``
``-subpos`` ``--sub-pos``
``-sws`` ``--sws-scaler``
``-tvscan`` ``--tv-scan``
``-use-filename-title`` ``--title='${filename}'``
``-vc ffh264vdpau`` (etc.) ``--hwdec=vdpau``
``-vobsub`` ``--sub-file`` (pass the .idx file)
``-x W``, ``-y H`` ``--geometry=WxH`` + ``--no-keepaspect``
``-xineramascreen`` ``--screen`` (different values)
``-xy W`` ``--autofit=W``
``-zoom`` Inverse available as ``--video-unscaled``
``dvdnav://`` ``dvdnav://menu``
``dvd://1`` ``dvd://0`` (0-based offset)
=========================== ========================================
.. note::
``-opt val`` becomes ``--opt=val``.
.. note::
Quite some video filters, video outputs, audio filters, audio outputs, had
changes in their option parsing. These aren't mentioned in the table above.
Also, some video and audio filters have been removed, and you have to use
libavfilter (using ``--vf=lavfi=[...]`` or ``--af=lavfi=[...]``) to get
them back.
input.conf and Slave Commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Table of renamed input commands:
This lists only commands that are not always gracefully handled by the
internal legacy translation layer. If an input.conf contains any legacy
commands, a warning will be printed when starting the player. The warnings
also show the replacement commands.
Properties containing ``_`` to separate words use ``-`` instead.
+--------------------------------+----------------------------------------+
| Old | New |
+================================+========================================+
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
| | (translation layer cannot deal with |
| | whitespace) |
+--------------------------------+----------------------------------------+
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
+--------------------------------+----------------------------------------+
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
| | |
| | ``set video-aspect 0`` (reset aspect) |
+--------------------------------+----------------------------------------+
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
| | ``<dir>`` parameter unsupported. Use |
| | a negative ``<step>`` instead. |
+--------------------------------+----------------------------------------+
| ``step_property <prop>`` | Prefix ``cycle`` or ``add`` with |
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
| | ``<step>`` |
+--------------------------------+----------------------------------------+
| ``osd_show_property_text`` | ``show_text <text>`` |
| ``<text>`` | The property expansion format string |
| | syntax slightly changed. |
+--------------------------------+----------------------------------------+
| ``osd_show_text`` | Now does the same as |
| | ``osd_show_property_text``. Use the |
| | ``raw`` prefix to disable property |
| | expansion. |
+--------------------------------+----------------------------------------+
| ``show_tracks`` | ``show_text ${track-list}`` |
+--------------------------------+----------------------------------------+
| ``show_chapters`` | ``show_text ${chapter-list}`` |
+--------------------------------+----------------------------------------+
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
+--------------------------------+----------------------------------------+
| ``tv_start_scan`` | ``set tv-scan yes`` |
+--------------------------------+----------------------------------------+
| ``tv_set_channel <val>`` | ``set tv-channel <val>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_channel`` | ``cycle tv-channel`` |
+--------------------------------+----------------------------------------+
| ``dvb_set_channel <v1> <v2>`` | ``set dvb-channel <v1>-<v2>`` |
+--------------------------------+----------------------------------------+
| ``dvb_step_channel`` | ``cycle dvb-channel`` |
+--------------------------------+----------------------------------------+
| ``tv_set_freq <val>`` | ``set tv-freq <val>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_freq`` | ``cycle tv-freq`` |
+--------------------------------+----------------------------------------+
| ``tv_set_norm <norm>`` | ``set tv-norm <norm>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_norm`` | ``cycle tv-norm`` |
+--------------------------------+----------------------------------------+
.. note::
Due to lack of hardware and users using the TV/DVB/PVR features, and
due to the need to cleanup the related command code, it's possible
that the new commands are buggy or behave worse. This can be improved
if testers are available. Otherwise, some of the TV code will be
removed at some point.
Slave mode
~~~~~~~~~~
* Slave mode was removed. A proper slave mode application needed tons of code
and hacks to get
it right. The main problem is that slave mode is a bad and incomplete
interface, and to get around that, applications parsed output messages
intended for users. It is hard to know which messages exactly are parsed by
slave mode applications. This makes it virtually impossible to improve
terminal output intended for users without possibly breaking something.
This is absolutely insane, and since initial improvements to **mpv** quickly
made slave mode incompatible to most applications, it was removed as useless
cruft. The client API (see below) is provided instead.
``--identify`` was replaced by the ``TOOLS/mpv_identify.sh`` wrapper script.
* For some time (until including release 0.4.x), mpv supported a
``--slave-broken`` option. The following options are equivalent:
::
--input-file=/dev/stdin --input-terminal=no
Assuming the system supports ``/dev/stdin``.
(The option was readded in 0.5.1 and sets exactly these options.)
* A JSON RPC protocol giving access to the client API is 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.
Policy for Removed Features
---------------------------
**mpv** is in active development. If something is in the way of more important
development (such as fixing bugs or implementing new features), we sometimes
remove features. Usually this happens only with old features that either seem
to be useless, or are not used by anyone. Often these are obscure, or
"inherited", or were marked experimental, but never received any particular
praise by any users.
Sometimes, features are replaced by something new. The new code will be either
simpler or more powerful, but doesn't necessarily provide everything the old
feature did.
We can not exclude that we accidentally remove features that are actually
popular. Generally, we do not know how much a specific functionality is used.
If you miss a feature and think it should be re-added, please open an issue
on the mpv bug tracker. Hopefully, a solution can be found. Often, it turns
out that re-adding something is not much of a problem, or that there are
better alternatives.
Why this Fork?
--------------
mplayer2 is practically dead, and mpv started out as a branch containing
new/experimental development. (Some of it was merged right *after* the fork
was made public, seemingly as an acknowledgment that development, or at
least merging, should have been more active.)
MPlayer is focused on not breaking anything, but is stuck with a horrible
codebase resistant to cleanup. (Unless you do what mpv did - merciless and
consequent pruning of bad, old code.) Cleanup and keeping broken things
conflict, so the kind of development mpv strives for can't be done within
MPlayer due to clashing development policies.
Additionally, mplayer2 already had lots of changes over MPlayer, which would
have needed to be backported to the MPlayer codebase. This would not only
have been hard (several years of diverging development), but also would have
been impossible due to the aforementioned MPlayer development policy.
+1 -1
View File
@@ -111,7 +111,7 @@ You can encode files from one format/codec to another using this facility.
.. admonition:: Examples
``"--ovc=mpeg4 --ovcopts=qscale=5"``
``"--ovc=mpeg4 --oacopts=qscale=5"``
selects constant quantizer scale 5 for MPEG-4 encoding.
``"--ovc=libx264 --ovcopts=crf=23"``
+168 -820
View File
File diff suppressed because it is too large. Load diff
-243
View File
@@ -1,243 +0,0 @@
JSON IPC
========
mpv can be controlled by external programs using the JSON-based IPC protocol. It
can be enabled by specifying the path to a unix socket using the option
``--input-unix-socket``. Clients can connect to this socket and send commands to
the player or receive events from it.
.. warning::
This is not intended to be a secure network protocol. It is explicitly
insecure: there is no authentication, no encryption, and the commands
themselves are insecure too. For example, the ``run`` command is exposed,
which can run arbitrary system commands. The use-case is controlling the
player locally. This is not different from the MPlayer slave protocol.
Socat example
-------------
You can use the ``socat`` tool to send commands (and receive reply) from the
shell. Assuming mpv was started with:
::
mpv file.mkv --input-unix-socket=/tmp/mpvsocket
Then you can control it using socat:
::
> echo '{ "command": ["get_property", "playback-time"] }' | socat - /tmp/mpvsocket
{"data":190.482000,"error":"success"}
In this case, socat copies data between stdin/stdout and the mpv socket
connection.
See the ``--idle`` option how to make mpv start without exiting immediately or
playing a file.
It's also possible to send input.conf style text-only commands:
::
> echo 'show_text ${playback-time}' | socat - /tmp/mpvsocket
But you won't get a reply over the socket. (This particular command shows the
playback time on the player's OSD.)
Protocol
--------
Clients can execute commands on the player by sending JSON messages of the
following form:
::
{ "command": ["command_name", "param1", "param2", ...] }
where ``command_name`` is the name of the command to be executed, followed by a
list of parameters. Parameters must be formatted as native JSON values
(integers, strings, booleans, ...). Every message **must** be terminated with
``\n``. Additionally, ``\n`` must not appear anywhere inside the message. In
practice this means that messages should be minified before being sent to mpv.
mpv will then send back a reply indicating whether the command was run
correctly, and an additional field holding the command-specific return data (it
can also be null).
::
{ "error": "success", "data": null }
mpv will also send events to clients with JSON messages of the following form:
::
{ "event": "event_name" }
where ``event_name`` is the name of the event. Additional event-specific fields
can also be present. See `List of events`_ for a list of all supported events.
Because events can occur at any time, it may be difficult at times to determine
which response goes with which command. Commands may optionally include a
``request_id`` which, if provided in the command request, will be copied
verbatim into the response. mpv does not intrepret the ``request_id`` in any
way; it is solely for the use of the requester.
For example, this request:
::
{ "command": ["get_property", "time-pos"], "request_id": 100 }
Would generate this response:
::
{ "error": "success", "data": 1.468135, "request_id": 100 }
All commands, replies, and events are separated from each other with a line
break character (``\n``).
If the first character (after skipping whitespace) is not ``{``, the command
will be interpreted as non-JSON text command, as they are used in input.conf
(or ``mpv_command_string()`` in the client API). Additionally, line starting
with ``#`` and empty lines are ignored.
Currently, embedded 0 bytes terminate the current line, but you should not
rely on this.
Commands
--------
Additionally to the commands described in `List of Input Commands`_, a few
extra commands can also be used as part of the protocol:
``client_name``
Return the name of the client as string. This is the string ``ipc-N`` with
N being an integer number.
``get_time_us``
Return the current mpv internal time in microseconds as a number. This is
basically the system time, with an arbitrary offset.
``get_property``
Return the value of the given property. The value will be sent in the data
field of the replay message.
Example:
::
{ "command": ["get_property", "volume"] }
{ "data": 50.0, "error": "success" }
``get_property_string``
Like ``get_property``, but the resulting data will always be a string.
Example:
::
{ "command": ["get_property_string", "volume"] }
{ "data": "50.000000", "error": "success" }
``set_property``
Set the given property to the given value. See `Properties`_ for more
information about properties.
Example:
::
{ "command": ["set_property", "pause", true] }
{ "error": "success" }
``set_property_string``
Like ``set_property``, but the argument value must be passed as string.
Example:
::
{ "command": ["set_property_string", "pause", "yes"] }
{ "error": "success" }
``observe_property``
Watch a property for changes. If the given property is changed, then an
event of type ``property-change`` will be generated
Example:
::
{ "command": ["observe_property", 1, "volume"] }
{ "error": "success" }
{ "event": "property-change", "id": 1, "data": 52.0, "name": "volume" }
``observe_property_string``
Like ``observe_property``, but the resulting data will always be a string.
Example:
::
{ "command": ["observe_property_string", 1, "volume"] }
{ "error": "success" }
{ "event": "property-change", "id": 1, "data": "52.000000", "name": "volume" }
``unobserve_property``
Undo ``observe_property`` or ``observe_property_string``. This requires the
numeric id passed to the observe command as argument.
Example:
::
{ "command": ["unobserve_property", 1] }
{ "error": "success" }
``request_log_messages``
Enable output of mpv log messages. They will be received as events. The
parameter to this command is the log-level (see ``mpv_request_log_messages``
C API function).
Log message output is meant for humans only (mostly for debugging).
Attempting to retrieve information by parsing these messages will just
lead to breakages with future mpv releases. Instead, make a feature request,
and ask for a proper event that returns the information you need.
``enable_event``, ``disable_event``
Enables or disables the named event. Mirrors the ``mpv_request_event`` C
API function. If the string ``all`` is used instead of an event name, all
events are enabled or disabled.
By default, most events are enabled, and there is not much use for this
command.
``suspend``
Suspend the mpv main loop. There is a long-winded explanation of this in
the C API function ``mpv_suspend()``. In short, this prevents the player
from displaying the next video frame, so that you don't get blocked when
trying to access the player.
``resume``
Undo one ``suspend`` call. ``suspend`` increments an internal counter, and
``resume`` decrements it. When 0 is reached, the player is actually resumed.
``get_version``
Returns the client API version the C API of the remote mpv instance
provides. (Also see ``DOCS/client-api-changes.rst``.)
UTF-8
-----
Normally, all strings are in UTF-8. Sometimes it can happen that strings are
in some broken encoding (often happens with file tags and such, and filenames
on many Unixes are not required to be in UTF-8 either). This means that mpv
sometimes sends invalid JSON. If that is a problem for the client application's
parser, it should filter the raw data for invalid UTF-8 sequences and perform
the desired replacement, before feeding the data to its JSON parser.
mpv will not attempt to construct invalid UTF-8 with broken escape sequences.
+62 -223
View File
@@ -1,16 +1,10 @@
LUA SCRIPTING
=============
mpv can load Lua scripts. Scripts passed to the ``--script`` option, or found in
the ``scripts`` subdirectory of the mpv configuration directory (usually
``~/.config/mpv/scripts/``) will be loaded on program start. mpv also appends the
``scripts`` subdirectory to the end of Lua's path so you can import scripts from
there too. Since it's added to the end, don't name scripts you want to import
the same as Lua libraries because they will be overshadowed by them.
mpv provides the built-in module ``mp``, which contains functions to send
commands to the mpv core and to retrieve information about playback state, user
settings, file information, and so on.
mpv can load Lua scripts. Scripts in ``~/.mpv/lua/`` will be loaded on program
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.
These scripts can be used to control mpv in a similar way to slave mode.
Technically, the Lua code uses the client API internally.
@@ -22,24 +16,23 @@ A script which leaves fullscreen mode when the player is paused:
::
function on_pause_change(name, value)
if value == true then
mp.set_property("fullscreen", "no")
end
function on_pause()
mp.set_property("fullscreen", "no")
end
mp.observe_property("pause", "bool", on_pause_change)
mp.register_event("pause", on_pause)
This script provides a pretty weird feature, but Lua scripting was made to
allow users implement features which are not going to be added to the mpv core.
Details on the script initialization and lifecycle
--------------------------------------------------
Mode of operation
-----------------
Your script will be loaded by the player at program start from the ``scripts``
configuration subdirectory, or from a path specified with the ``--script``
option. Some scripts are loaded internally (like ``--osc``). Each script runs in
its own thread. Your script is first run "as is", and once that is done, the event loop
is entered. This event loop will dispatch events received by mpv and call your
own event handlers which you have registered with ``mp.register_event``, or
timers added with ``mp.add_timeout`` or similar.
Your script will be loaded by the player at program start from ``~/.mpv/lua/``,
or ``--lua``, or in some cases, internally (like ``--osc``). Each script runs
in its own thread. Your script is first run "as is", and once that is done,
the event loop is entered. This event loop will dispatch events received by mpv
and call your own event handlers which you have registered with
``mp.register_event``, or timers added with ``mp.add_timeout`` or similar.
When the player quits, all scripts will be asked to terminate. This happens via
a ``shutdown`` event, which by default will make the event loop return. If your
@@ -54,15 +47,6 @@ The event loop will wait for events and dispatch events registered with
``mp.register_event``. It will also handle timers added with ``mp.add_timeout``
and similar (by waiting with a timeout).
Since mpv 0.6.0, the player will wait until the script is fully loaded before
continuing normal operation. The player considers a script as fully loaded as
soon as it starts waiting for mpv events (or it exits). In practice this means
the player will more or less hang until the script returns from the main chunk
(and ``mp_event_loop`` is called), or the script calls ``mp_event_loop`` or
``mp.dispatch_events`` directly. This is done to make it possible for a script
to fully setup event handlers etc. before playback actually starts. In older
mpv versions, this happened asynchronously.
mp functions
------------
@@ -73,10 +57,6 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Run the given command. This is similar to the commands used in input.conf.
See `List of Input Commands`_.
By default, this will show something on the OSD (depending on the command),
as if it was used in ``input.conf``. See `Input Command Prefixes`_ how
to influence OSD usage per command.
Returns ``true`` on success, or ``nil, error`` on error.
``mp.commandv(arg1, arg2, ...)``
@@ -98,18 +78,6 @@ The ``mp`` module is preloaded, although it can be loaded manually with
the ``expand-properties`` prefix, or the ``mp.get_property`` family of
functions.
Unlike ``mp.command``, this will not use OSD by default either (except
for some OSD-specific commands).
``mp.command_native(table [,def])``
Similar to ``mp.commandv``, but pass the argument list as table. This has
the advantage that in at least some cases, arguments can be passed as
native types.
Returns a result table on success (usually empty), or ``def, error`` on
error. ``def`` is the second parameter provided to the function, and is
nil if it's missing.
``mp.get_property(name [,def])``
Return the value of the given property as string. These are the same
properties as used in input.conf. See `Properties`_ for a list of
@@ -188,7 +156,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Return the current mpv internal time in seconds as a number. This is
basically the system time, with an arbitrary offset.
``mp.add_key_binding(key, name|fn [,fn [,flags]])``
``mp.add_key_binding(key, name|fn [,fn])``
Register callback to be run on a key binding. The binding will be mapped to
the given ``key``, which is a string describing the physical key. This uses
the same key names as in input.conf, and also allows combinations
@@ -205,23 +173,8 @@ The ``mp`` module is preloaded, although it can be loaded manually with
overwritten. You can omit the name, in which case a random name is generated
internally.
The last argument is used for optional flags. This is a table, which can
have the following entries:
``repeatable``
If set to ``true``, enables key repeat for this specific binding.
``complex``
If set to ``true``, then ``fn`` is called on both key up and down
events (as well as key repeat, if enabled), with the first
argument being a table. This table has an ``event`` entry, which
is set to one of the strings ``down``, ``repeat``, ``up`` or
``press`` (the latter if key up/down can't be tracked). It further
has an ``is_mouse`` entry, which tells whether the event was caused
by a mouse button.
Internally, key bindings are dispatched via the ``script_message_to`` or
``script_binding`` input commands and ``mp.register_script_message``.
Internally, key bindings are dispatched via the ``script_message_to`` input
command and ``mp.register_script_message``.
Trying to map multiple commands to a key will essentially prefer a random
binding, while the other bindings are not called. It is guaranteed that
@@ -244,7 +197,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
y script_binding something
y script_message something
This will print the message when the key ``y`` is pressed. (``x`` will
@@ -255,7 +208,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
y script_binding fooscript.something
y script_message_to fooscript something
``mp.add_forced_key_binding(...)``
This works almost the same as ``mp.add_key_binding``, but registers the
@@ -333,6 +286,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
the timer is re-added after the function fn is run.
Returns a timer object. The timer object provides the following methods:
``stop()``
Disable the timer. Does nothing if the timer is already disabled.
This will remember the current elapsed time when stopping, so that
@@ -363,26 +317,9 @@ The ``mp`` module is preloaded, although it can be loaded manually with
(``true``). This value is used when the timer expires (but before
the timer callback function fn is run).
Note that these are method, and you have to call them using ``:`` instead
of ``.`` (Refer to http://www.lua.org/manual/5.2/manual.html#3.4.9 .)
Example:
::
seconds = 0
timer = mp.add_periodic_timer(1, function()
print("called every second")
# stop it after 10 seconds
seconds = seconds + 1
if seconds >= 10 then
timer:kill()
end
end)
``mp.get_opt(key)``
Return a setting from the ``--script-opts`` option. It's up to the user and
Return a setting from the ``--lua-opts`` option. It's up to the user and
the script how this mechanism is used. Currently, all scripts can access
this equally, so you should be careful about collisions.
@@ -518,8 +455,8 @@ Example implementation::
optionB = -0.5,
optionC = true,
}
read_options(options, "myscript")
print(options.optionA)
options.read_options(options, "myscript")
print(option.optionA)
The config file will be stored in ``lua-settings/identifier.conf`` in mpv's user
@@ -534,12 +471,12 @@ Example config::
optionC=no
Command-line options are read from the ``--script-opts`` parameter. To avoid
Command-line options are read from the ``--lua-opts`` parameter. To avoid
collisions, all keys have to be prefixed with ``identifier-``.
Example command-line::
--script-opts=myscript-optionA=TEST,myscript-optionB=0,myscript-optionC=yes
--lua-opts=myscript-optionA=TEST:myscript-optionB=0:myscript-optionC=yes
mp.utils options
@@ -549,13 +486,6 @@ This built-in module provides generic helper functions for Lua, and have
strictly speaking nothing to do with mpv or video/audio playback. They are
provided for convenience. Most compensate for Lua's scarce standard library.
Be warned that any of these functions might disappear any time. They are not
strictly part of the guaranteed API.
``utils.getcwd()``
Returns the directory that mpv was launched from. On error, ``nil, error``
is returned.
``utils.readdir(path [, filter])``
Enumerate all entries at the given path on the filesystem, and return them
as array. Each entry is a directory entry (without the path).
@@ -592,76 +522,6 @@ strictly part of the guaranteed API.
Return the concatenation of the 2 paths. Tries to be clever. For example,
if ```p2`` is an absolute path, p2 is returned without change.
``utils.subprocess(t)``
Runs an external process and waits until it exits. Returns process status
and the captured output.
The parameter ``t`` is a table. The function reads the following entries:
``args``
Array of strings. The first array entry is the executable. This
can be either an absolute path, or a filename with no path
components, in which case the ``PATH`` environment variable is
used to resolve the executable. The other array elements are
passed as command line arguments.
``cancellable``
Optional. If set to ``true`` (default), then if the user stops
playback or goes to the next file while the process is running,
the process will be killed.
``max_size``
Optional. The maximum size in bytes of the data that can be captured
from stdout. (Default: 16 MB.)
The function returns a table as result with the following entries:
``status``
The raw exit status of the process. It will be negative on error.
``stdout``
Captured output stream as string, limited to ``max_size``.
``error``
``nil`` on success. The string ``killed`` if the process was
terminated in an unusual way. The string ``init`` if the process
could not be started.
On Windows, ``killed`` is only returned when the process has been
killed by mpv as a result of ``cancellable`` being set to ``true``.
``killed_by_us``
Set to ``true`` if the process has been killed by mpv as a result
of ``cancellable`` being set to ``true``.
In all cases, ``mp.resume_all()`` is implicitly called.
``utils.parse_json(str [, trail])``
Parses the given string argument as JSON, and returns it as a Lua table. On
error, returns ``nil, error``. (Currently, ``error`` is just a string
reading ``error``, because there is no fine-grained error reporting of any
kind.)
The returned value uses similar conventions as ``mp.get_property_native()``
to distinguish empty objects and arrays.
If the ``trail`` parameter is ``true`` (or any value equal to ``true``),
then trailing non-whitespace text is tolerated by the function, and the
trailing text is returned as 3rd return value. (The 3rd return value is
always there, but with ``trail`` set, no error is raised.)
``utils.format_json(v)``
Format the given Lua table (or value) as a JSON string and return it. On
error, returns ``nil, error``. (Errors usually only happen on value types
incompatible with JSON.)
The argument value uses similar conventions as ``mp.set_property_native()``
to distinguish empty objects and arrays.
``utils.to_string(v)``
Turn the given value into a string. Formats tables and their contents. This
doesn't do anything special; it is only needed because Lua is terrible.
Events
------
@@ -679,7 +539,7 @@ Example:
print("start of playback!")
end
mp.register_event("file-loaded", my_fn)
mp.register_event("playback-start", my_fn)
@@ -694,49 +554,37 @@ List of events
Happens after a file was unloaded. Typically, the player will load the
next file right away, or quit if this was the last file.
The event has the ``reason`` field, which takes one of these values:
``eof``
The file has ended. This can (but doesn't have to) include
incomplete files or broken network connections under
circumstances.
``stop``
Playback was ended by a command.
``quit``
Playback was ended by sending the quit command.
``error``
An error happened. In this case, an ``error`` field is present with
the error string.
``redirect``
Happens with playlists and similar. Details see
``MPV_END_FILE_REASON_REDIRECT`` in the C API.
``unknown``
Unknown. Normally doesn't happen, unless the Lua API is out of sync
with the C API. (Likewise, it could happen that your script gets
reason strings that did not exist yet at the time your script was
written.)
``file-loaded``
Happens after a file was loaded and begins playback.
``seek``
Happens on seeking. (This might include cases when the player seeks
internally, even without user interaction. This includes e.g. segment
changes when playing ordered chapters Matroska files.)
Happens on seeking (including ordered chapter segment changes).
``playback-restart``
Start of playback after seek or after file was loaded.
``tracks-changed``
The list of video/audio/sub tracks was updated. (This happens on playback
start, and very rarely during playback.)
``track-switched``
A video/audio/subtitle track was switched on or off. This usually happens
when the user (or a script) changes the subtitle track and so on.
``idle``
Idle mode is entered. This happens when playback ended, and the player was
started with ``--idle`` or ``--force-window``. This mode is implicitly ended
when the ``start-file`` or ``shutdown`` events happen.
``pause``
Playback was paused. This also happens when for example the player is
paused on low network cache. Then the event type indicates the pause state
(like the property "pause" as opposed to the "core-idle" property), and you
might receive multiple ``pause`` events in a row.
``unpause``
Playback was unpaused. See above for details.
``tick``
Called after a video frame was displayed. This is a hack, and you should
avoid using it. Use timers instead and maybe watch pausing/unpausing events
@@ -744,7 +592,7 @@ List of events
``shutdown``
Sent when the player quits, and the script should terminate. Normally
handled automatically. See `Details on the script initialization and lifecycle`_.
handled automatically. See `Mode of operation`_.
``log-message``
Receives messages enabled with ``mp.enable_messages``. The message data
@@ -763,8 +611,13 @@ List of events
(undocumented) existing ones.
``text``
The log message. The text will end with a newline character. Sometimes
it can contain multiple lines.
The log message. Note that this is the direct output of a printf()
style output API. The text will contain embedded newlines, and it's
possible that a single message contains multiple lines, or that a
message contains a partial line.
It's safe to display messages only if they end with a newline character,
and to buffer them otherwise.
Keep in mind that these messages are meant to be hints for humans. You
should not parse them, and prefix/level/text of messages might change
@@ -779,6 +632,9 @@ List of events
``command-reply``
Undocumented (not useful for Lua scripts).
``script-input-dispatch``
Undocumented (used internally).
``client-message``
Undocumented (used internally).
@@ -788,25 +644,8 @@ List of events
``audio-reconfig``
Happens on audio output or filter reconfig.
The following events also happen, but are deprecated: ``tracks-changed``,
``track-switched``, ``pause``, ``unpause``, ``metadata-update``,
``chapter-change``. Use ``mp.observe_property()`` instead.
``metadata-update``
Metadata (like file tags) was updated.
Extras
------
This documents experimental features, or features that are "too special" to
guarantee a stable interface.
``mp.add_hook(type, priority, fn)``
Add a hook callback for ``type`` (a string identifying a certain kind of
hook). These hooks allow the player to call script functions and wait for
their result (normally, the Lua scripting interface is asynchronous from
the point of view of the player core). ``priority`` is an arbitrary integer
that allows ordering among hooks of the same kind. Using the value 50 is
recommended as neutral default value. ``fn`` is the function that will be
called during execution of the hook.
See `Hooks`_ for currently existing hooks and what they do - only the hook
list is interesting; handling hook execution is done by the Lua script
function automatically.
``chapter-change``
The current chapter possibly changed.
+43 -293
View File
@@ -12,7 +12,8 @@ a media player
SYNOPSIS
========
| **mpv** [options] [file|URL|PLAYLIST|-]
| **mpv** [options] [file|URL|-]
| **mpv** [options] --playlist=PLAYLIST
| **mpv** [options] files
DESCRIPTION
@@ -32,10 +33,8 @@ INTERACTIVE CONTROL
===================
mpv has a fully configurable, command-driven control layer which allows you
to control mpv using keyboard, mouse, or remote control (there is no
LIRC support - configure remotes as input devices instead).
See the ``--input-`` options for ways to customize it.
to control mpv using keyboard, mouse, joystick or remote control (with
LIRC). See the ``--input-`` options for ways to customize it.
Keyboard Control
----------------
@@ -48,10 +47,6 @@ UP and DOWN
Seek forward/backward 1 minute. Shift+arrow does a 5 second exact seek (see
``--hr-seek``).
Ctrl+LEFT and Ctrl+RIGHT
Seek to the previous/next subtitle. Subject to some restrictions and
might not work always; see ``sub_seek`` command.
[ and ]
Decrease/increase current playback speed by 10%.
@@ -65,7 +60,7 @@ BACKSPACE
Go backward/forward in the playlist.
ENTER
Go forward in the playlist.
Go forward in the playlist, even over the end.
p / SPACE
Pause (pressing again unpauses).
@@ -78,7 +73,7 @@ p / SPACE
Step backward. Pressing once will pause, every consecutive press will
play one frame in reverse and then go into pause mode again.
q
q / ESC
Stop playing and quit.
Q
@@ -103,9 +98,6 @@ m
f
Toggle fullscreen (see also ``--fs``).
ESC
Exit fullscreen mode.
T
Toggle stay-on-top (see also ``--ontop``).
@@ -116,7 +108,11 @@ o (also P)
Show progression bar, elapsed time and total duration on the OSD.
O
Toggle OSD states between normal and playback time/duration.
Toggle OSD states: none / seek / seek + timer / seek + timer + total time.
d
Toggle frame dropping states: none / skip display / skip decoding (see
``--framedrop``).
v
Toggle subtitle visibility.
@@ -127,20 +123,6 @@ j and J
x and z
Adjust subtitle delay by +/- 0.1 seconds.
l
Set/clear A-B loop points. See ``ab_loop`` command for details.
L
Toggle infinite looping.
Ctrl + and Ctrl -
Adjust audio delay by +/- 0.1 seconds.
u
Switch between applying no style overrides to SSA/ASS subtitles, and
overriding them almost completely with the normal subtitle style. See
``--ass-style-override`` for more info.
V
Toggle subtitle VSFilter aspect compatibility mode. See
``--ass-vsfilter-aspect-compat`` for more info.
@@ -155,10 +137,6 @@ S
Take a screenshot, without subtitles. (Whether this works depends on VO
driver support.)
Ctrl s
Take a screenshot, as the window shows it (with subtitles, OSD, and scaled
video).
I
Show filename on the OSD.
@@ -167,16 +145,9 @@ PGUP and PGDWN
"previous" will actually go to the beginning of the current chapter; see
``--chapter-seek-threshold``.
Shift+PGUP and Shift+PGDWN
Seek backward or forward by 10 minutes. (This used to be mapped to
PGUP/PGDWN without Shift.)
d
D
Activate/deactivate deinterlacer.
A
Cycle aspect ratio override.
(The following keys are valid only when using a video output that supports the
corresponding adjustment, or the software equalizer (``--vf=eq``).)
@@ -248,47 +219,13 @@ is the same as ``--no-fs``.
If an option is marked as *(XXX only)*, it will only work in combination with
the *XXX* option or if *XXX* is compiled in.
Escaping spaces and other special characters
--------------------------------------------
.. note::
Keep in mind that the shell will partially parse and mangle the arguments you
pass to mpv. For example, you might need to quote or escape options and
filenames:
The suboption parser (used for example for ``--ao=pcm`` suboptions)
supports a special kind of string-escaping intended for use with external
GUIs.
``mpv "filename with spaces.mkv" --title="window title"``
It gets more complicated if the suboption parser is involved. The suboption
parser puts several options into a single string, and passes them to a
component at once, instead of using multiple options on the level of the
command line.
The suboption parser can quote strings with ``"``, ``'``, and ``[...]``.
Additionally, there is a special form of quoting with ``%n%`` described below.
For example, the ``opengl`` VO can take multiple options:
``mpv test.mkv --vo=opengl:scale=lanczos:icc-profile=file.icc,xv``
This passes ``scale=lanczos`` and ``icc-profile=file.icc`` to ``opengl``,
and also specifies ``xv`` as fallback VO. If the icc-profile path contains
spaces or characters like ``,`` or ``:``, you need to quote them:
``mpv '--vo=opengl:icc-profile="file with spaces.icc",xv'``
Shells may actually strip some quotes from the string passed to the commandline,
so the example quotes the string twice, ensuring that mpv receives the ``"``
quotes.
The ``[...]`` form of quotes wraps everything between ``[`` and ``]``. It's
useful with shells that don't interpret these characters in the middle of
an argument (like bash). These quotes are balanced (since mpv 0.9.0): the ``[``
and ``]`` nest, and the quote terminates on the last ``]`` that has no matching
``[`` within the string. (For example, ``[a[b]c]`` results in ``a[b]c``.)
The fixed-length quoting syntax is intended for use with external
scripts and programs.
It is started with ``%`` and has the following format::
It has the following format::
%n%string_of_length_n
@@ -300,15 +237,6 @@ It is started with ``%`` and has the following format::
``mpv --ao=pcm:file=%`expr length "$NAME"`%"$NAME" test.avi``
Suboptions passed to the client API are also subject to escaping. Using
``mpv_set_option_string()`` is exactly like passing ``--name=data`` to the
command line (but without shell processing of the string). Some options
support passing values in a more structured way instead of flat strings, and
can avoid the suboption parsing mess. For example, ``--vf`` supports
``MPV_FORMAT_NODE``, which lets you pass suboptions as a nested data structure
of maps and arrays. (``--vo`` supports this in the same way, although this
fact is undocumented.)
Paths
-----
@@ -324,23 +252,12 @@ console controls. (Which makes it suitable for playing data piped to stdin.)
For paths passed to suboptions, the situation is further complicated by the
need to escape special characters. To work this around, the path can be
additionally wrapped in the fixed-length syntax, e.g. ``%n%string_of_length_n``
(see above).
additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
Some mpv options interpret paths starting with ``~``. Currently, the prefix
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
``~~/`` expands to the mpv configuration directory (usually ``~/.mpv/``).
``~/`` expands to the user's home directory. (The trailing ``/`` is always
required.) There are the following paths as well:
================ ===============================================================
Name Meaning
================ ===============================================================
``~~home/`` same as ``~~/``
``~~global/`` the global config path, if available (not on win32)
``~~osxbundle/`` the OSX bundle resource path (OSX only)
``~~desktop/`` the path to the desktop (win32, OSX)
================ ===============================================================
required.)
Per-File Options
----------------
@@ -391,8 +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 ``~/.config/mpv/mpv.conf``. For details and platform
specifics see the `FILES`_ section.
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
@@ -408,17 +324,6 @@ setting them to *no*. Even suboptions can be specified in this way.
# Use quotes for text that can contain spaces:
status-msg="Time: ${time-pos}"
Escaping spaces and special characters
--------------------------------------
This is done like with command line options. The shell is not involved here,
but option values still need to be quoted as a whole if it contains certain
characters like spaces. A config entry can be quoted with ``"`` and ``'``,
as well as with the fixed-length syntax (``%n%``) mentioned before. This is like
passing the exact contents of the quoted string as command line option. C-style
escapes are currently _not_ interpreted on this level, although some options do
this manually. (This is a mess and should probably be changed at some point.)
Putting Command Line Options into the Configuration File
--------------------------------------------------------
@@ -440,11 +345,11 @@ File-specific Configuration Files
You can also write file-specific configuration files. If you wish to have a
configuration file for a file called 'video.avi', create a file named
'video.avi.conf' with the file-specific options in it and put it in
``~/.config/mpv/``. You can also put the configuration file in the same directory
as the file to be played. Both require you to set the ``--use-filedir-conf``
``~/.mpv/``. You can also put the configuration file in the same directory
as the file to be played, as long as you give the ``--use-filedir-conf``
option (either on the command line or in your global config file). If a
file-specific configuration file is found in the same directory, no
file-specific configuration is loaded from ``~/.config/mpv``. In addition, the
file-specific configuration is loaded from ``~/.mpv``. In addition, the
``--use-filedir-conf`` option enables directory-specific configuration files.
For this, mpv first tries to load a mpv.conf from the same directory
as the file played and then tries to load any file-specific configuration.
@@ -465,11 +370,12 @@ profile name ``default`` to continue with normal options.
::
[vo.vdpau]
# Use hardware decoding
# Use hardware decoding (might break playback of some h264 files)
hwdec=vdpau
[protocol.dvd]
profile-desc="profile for dvd:// streams"
vf=pp=hb/vb/dr/al/fd
alang=en
[extension.flv]
@@ -492,61 +398,11 @@ A screenshot will usually contain the unscaled video contents at the end of the
video filter chain and subtitles. By default, ``S`` takes screenshots without
subtitles, while ``s`` includes subtitles.
Unlike with MPlayer, the ``screenshot`` video filter is not required. This
filter was never required in mpv, and has been removed.
TERMINAL STATUS LINE
====================
During playback, mpv shows the playback status on the terminal. It looks like
something like this:
``AV: 00:03:12 / 00:24:25 (13%) A-V: -0.000``
The status line can be overridden with the ``--term-status-msg`` option.
The following is a list of things that can show up in the status line. Input
properties, that can be used to get the same information manually, are also
listed.
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
- The total file duration (absent if unknown) (``length`` property)
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
is the user-requested speed, and not the actual speed (usually they should
be the same, unless playback is too slow). (``speed`` property.)
- Playback percentage, e.g. ``(13%)``. How much of the file has been played.
Normally calculated out of playback position and duration, but can fallback
to other methods (like byte position) if these are not available.
(``percent-pos`` property.)
- The audio/video sync as ``A-V: 0.000``. This is the difference between
audio and video time. Normally it should be 0 or close to 0. If it's growing,
it might indicate a playback problem. (``avsync`` property.)
- Total A/V sync change, e.g. ``ct: -0.417``. Normally invisible. Can show up
if there is audio "missing", or not enough frames can be dropped. Usually
this will indicate a problem. (``total-avsync-change`` property.)
- Encoding state in ``{...}``, only shown in encoding mode.
- Display sync state. If display sync is active (``display-sync-active``
property), this shows ``DS: +0.02598%``, where the number is the speed change
factor applied to audio to achieve sync to display, expressed in percent
deviation from 1.0 (``audio-speed-correction`` property). In sync modes which
don't resample, this will always be ``+0.00000%``.
- Missed frames, e.g. ``Missed: 4``. (``vo-missed-frame-count`` property.) Shows
up in display sync mode only. This is incremented each time a frame took
longer to display than intended.
- Dropped frames, e.g. ``Dropped: 4``. Shows up only if the count is not 0. Can
grow if the video framerate is higher than that of the display, or if video
rendering is too slow. Also can be incremented on "hiccups" and when the video
frame couldn't be displayed on time. (``vo-drop-frame-count`` property.)
If the decoder drops frames, the number of decoder-dropped frames is appended
to the display as well, e.g.: ``Dropped: 4/34``. This happens only if
decoder frame dropping is enabled with the ``--framedrop`` options.
(``drop-frame-count`` property.)
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
The first value shows the amount of video buffered in the demuxer in seconds,
the second value shows *additional* data buffered in the stream cache in
kilobytes. (``demuxer-cache-duration`` and ``cache-used`` properties.)
The ``screenshot`` video filter is not required when using a recommended GUI
video output driver. It should normally not be added to the config file, as
taking screenshots is handled by the VOs, and adding the screenshot filter will
break hardware decoding. (The filter may still be useful for taking screenshots
at a certain point within the video chain when using multiple video filters.)
PROTOCOLS
=========
@@ -556,10 +412,6 @@ PROTOCOLS
be specified. mpv will never attempt to guess whether a filename is
actually a network address. A protocol prefix is always required.
Note that not all prefixes are documented here. Undocumented prefixes are
either aliases to documented protocols, or are just redirections to
protocols implemented and documented in FFmpeg.
``-``
Play data from stdin.
@@ -569,11 +421,15 @@ PROTOCOLS
``bd://[title][/device]`` ``--bluray-device=PATH``
Play a Blu-Ray disc. Currently, this does not accept ISO files. Instead,
you must mount the ISO file as filesystem, and point ``--bluray-device``
to the mounted directory directly.
to the mounted directly.
``bdnav://[title][/device]``
Play a Blu-Ray disc, with navigation features enabled. This feature is
permanently experimental.
``dvd://[title|[starttitle]-endtitle][/device]`` ``--dvd-device=PATH``
Play a DVD. DVD menus are not supported. If no title is given, the longest
title is auto-selected.
Play a DVD. If you want dvdnav menus, use ``dvd://menu``. If no title
is given, the longest title is auto-selected.
``dvdnav://`` is an old alias for ``dvd://`` and does exactly the same
thing.
@@ -618,11 +474,6 @@ PROTOCOLS
``PATH`` itself should start with a third ``/`` to make the path an
absolute path.
``fd://123``
Read data from the given UNIX FD (for example 123). This is similar to
piping data to stdin via ``-``, but can use an arbitrary file descriptor.
Will not work correctly on MS Windows.
``edl://[edl specification as in edl-mpv.rst]``
Stitch together parts of multiple files and play them.
@@ -632,40 +483,6 @@ PROTOCOLS
``memory://data``
Use the ``data`` part as source data.
PSEUDO GUI MODE
===============
mpv has no official GUI, other than the OSC (`ON SCREEN CONTROLLER`_), which
is not a full GUI and is not meant to be. However, to compensate for the lack
of expected GUI behavior, mpv will in some cases start with some settings
changed to behave slightly more like a GUI mode.
Currently this happens only in the following cases:
- if started using the ``mpv.desktop`` file on Linux (e.g. started from menus
or file associations provided by desktop environments)
- if started from explorer.exe on Windows (technically, if it was started on
Windows, and all of the stdout/stderr/stdin handles are unset)
- manually adding ``--profile=pseudo-gui`` to the command line
This mode implicitly adds ``--profile=pseudo-gui`` to the command line, with
the ``pseudo-gui`` profile being predefined with the following contents:
::
[pseudo-gui]
terminal=no
force-window=yes
idle=once
screenshot-directory=~~desktop/
This follows the mpv config file format. To customize pseudo-GUI mode, you can
put your own ``pseudo-gui`` profile into your ``mpv.conf``. This profile will
enhance the default profile, rather than overwrite it.
The profile always overrides other settings in ``mpv.conf``.
.. include:: options.rst
.. include:: ao.rst
@@ -684,8 +501,6 @@ The profile always overrides other settings in ``mpv.conf``.
.. include:: lua.rst
.. include:: ipc.rst
.. include:: changes.rst
ENVIRONMENT VARIABLES
@@ -812,8 +627,6 @@ If errors happen, the following exit codes can be returned:
immediately after initialization.
:3: There were some files that could be played, and some files which
couldn't (using the definition of success from above).
:4: Quit due to a signal, Ctrl+c in a VO window (by default), or from the
default quit key bindings in encoding mode.
Note that quitting the player manually will always lead to exit code 0,
overriding the exit code that would be returned normally. Also, the ``quit``
@@ -822,90 +635,27 @@ input command can take an exit code: in this case, that exit code is returned.
FILES
=====
For Windows-specifics, see `FILES ON WINDOWS`_ section.
``/usr/local/etc/mpv/mpv.conf``
mpv system-wide settings (depends on ``--prefix`` passed to configure - mpv
in default configuration will use ``/usr/local/etc/mpv/`` as config
directory, while most Linux distributions will set it to ``/etc/mpv/``).
mpv system-wide settings (depends on ``--prefix`` passed to configure)
``~/.config/mpv/mpv.conf``
mpv user settings (see `CONFIGURATION FILES`_ section)
mpv user settings
``~/.config/mpv/input.conf``
key bindings (see `INPUT.CONF`_ section)
``~/.config/mpv/scripts/``
All files in this directory are loaded as if they were passed to the
``--script`` option. They are loaded in alphabetical order, and sub-directories
``~/.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.
``~/.config/mpv/watch_later/``
Contains temporary config files needed for resuming playback of files with
the watch later feature. See for example the ``Q`` key binding, or the
``quit_watch_later`` input command.
Each file is a small config file which is loaded if the corresponding media
file is loaded. It contains the playback position and some (not necessarily
all) settings that were changed during playback. The filenames are hashed
from the full paths of the media files. It's in general not possible to
extract the media filename from this hash. However, you can set the
``--write-filename-in-watch-later-config`` option, and the player will
add the media filename to the contents of the resume config file.
``~/.config/mpv/lua-settings/osc.conf``
This is loaded by the OSC script. See the `ON SCREEN CONTROLLER`_ docs
for details.
Other files in this directory are specific to the corresponding scripts
as well, and the mpv core doesn't touch them.
Note that the environment variables ``$XDG_CONFIG_HOME`` and ``$MPV_HOME`` can
override the standard directory ``~/.config/mpv/``.
Also, the old config location at ``~/.mpv/`` is still read, and if the XDG
variant does not exist, will still be preferred.
FILES ON WINDOWS
================
On win32 (if compiled with MinGW, but not Cygwin), the default config file
locations are different. They are generally located under ``%APPDATA%/mpv/``.
For example, the path to mpv.conf is ``%APPDATA%/mpv/mpv.conf``, which maps to
a system and user-specific path, for example
``C:\users\USERNAME\Application Data\mpv\mpv.conf``
You can find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.exe.
Other config files (such as ``input.conf``) are in the same directory. See the
`FILES`_ section above.
The environment variable ``$MPV_HOME`` completely overrides these, like on
UNIX.
If a directory named ``portable_config`` next to the mpv.exe exists, all
config will be loaded from this directory only. Watch later config files are
written to this directory as well. (This exists on Windows only and is redundant
with ``$MPV_HOME``. However, since Windows is very scripting unfriendly, a
wrapper script just setting ``$MPV_HOME``, like you could do it on other
systems, won't work. ``portable_config`` is provided for convenience to get
around this restriction.)
Config files located in the same directory as ``mpv.exe`` are loaded with
lower priority. Some config files are loaded only once, which means that
e.g. of 2 ``input.conf`` files located in two config directories, only the
one from the directory with higher priority will be loaded.
A third config directory with lowest priority is the directory named ``mpv``
in the same directory as ``mpv.exe``. This used to be the directory with
highest priority, but is now discouraged to use and might be removed in the
future.
Note that mpv likes to mix ``/`` and ``\`` path separators for simplicity.
kernel32.dll accepts this, but cmd.exe does not.
EXAMPLES OF MPV USAGE
=====================
+400 -955
View File
File diff suppressed because it is too large. Load diff
+17 -35
View File
@@ -5,7 +5,7 @@ The On Screen Controller (short: OSC) is a minimal GUI integrated with mpv to
offer basic mouse-controllability. It is intended to make interaction easier
for new users and to enable precise and direct seeking.
The OSC is enabled by default if mpv was compiled with Lua support. It can be
The OSC is enabled by default if mpv was compiled with lua support. It can be
disabled entirely using the ``--osc=no`` option.
Using the OSC
@@ -127,7 +127,7 @@ Key Bindings
These key bindings are active by default if nothing else is already bound to
these keys. In case of collision, the function needs to be bound to a
different key. See the `Script Commands`_ section.
different key. See `Script Commands`_ section.
============= ================================================
del Hide the OSC permanently until mpv is restarted.
@@ -138,7 +138,7 @@ Configuration
The OSC offers limited configuration through a config file
``lua-settings/osc.conf`` placed in mpv's user dir and through the
``--script-opts`` command-line option. Options provided through the command-line
``--lua-opts`` command-line option. Options provided through the command-line
will override those from the config file.
Config Syntax
@@ -161,7 +161,7 @@ To avoid collisions with other scripts, all options need to be prefixed with
Example::
--script-opts=osc-optionA=value1,osc-optionB=value2
--lua-opts=osc-optionA=value1:osc-optionB=value2
Configurable Options
@@ -169,35 +169,34 @@ Configurable Options
``showwindowed``
| Default: yes
| Enable the OSC when windowed
| Show OSC when windowed?
``showfullscreen``
| Default: yes
| Enable the OSC when fullscreen
| Show OSC when fullscreen?
``scalewindowed``
| Default: 1.0
| Scale factor of the OSC when windowed
| Default: 1
| Scaling of the controller when windowed
``scalefullscreen``
| Default: 1.0
| Scale factor of the OSC when fullscreen
| Default: 1
| Scaling of the controller when fullscreen
``scaleforcedwindow``
| Default: 2.0
| Scale factor of the OSC when rendered on a forced (dummy) window
| Default: 2
| Scaling of the controller when rendered on a forced (dummy) window
``vidscale``
| Default: yes
| Scale the OSC with the video
| ``no`` tries to keep the OSC size constant as much as the window size allows
| Scale the controller with the video?
``valign``
| Default: 0.8
| Vertical alignment, -1 (top) to 1 (bottom)
``halign``
| Default: 0.0
| Default: 0
| Horizontal alignment, -1 (left) to 1 (right)
``boxalpha``
@@ -226,22 +225,9 @@ Configurable Options
| Minimum amount of pixels the mouse has to move between ticks to make
the OSC show up
``layout``
| Default: box
| The layout for the OSC. Currently available are: box, slimbox,
bottombar and topbar.
``seekbarstyle``
| Default: slider
| Sets the style of the seekbar, slider (diamond marker) or bar (fill)
``timetotal``
| Default: no
| Show total time instead of time remaining
``timems``
| Default: no
| Display timecodes with milliseconds
``seektooltip``
| Default: yes
| Display a tooltip over the seekbar indicating time at mouse position.
Script Commands
~~~~~~~~~~~~~~~
@@ -255,10 +241,6 @@ in ``input.conf``, or sent by other scripts.
``disable-osc``
Hide the OSC permanently. This is also what the ``del`` key does.
``osc-message``
Show a message on screen using the OSC. First argument is the message,
second the duration in seconds.
Example
+375 -226
View File
@@ -17,10 +17,6 @@ normal filter parameters.
To get a full list of available video filters, see ``--vf=help``.
Also, keep in mind that most actual filters are available via the ``lavfi``
wrapper, which gives you access to most of libavfilter's filters. This
includes all filters that have been ported from MPlayer to libavfilter.
Video filters are managed in lists. There are a few commands to manage the
filter list.
@@ -101,7 +97,7 @@ Available filters are:
All parameters are optional.
``<w>:<h>``
``<w>,<h>``
scaled width/height (default: original width/height)
:0: scaled d_width/d_height
@@ -147,8 +143,8 @@ Available filters are:
Accurate rounding for the vertical scaler, which may be faster or
slower than the default rounding.
:no: Disable accurate rounding (default).
:yes: Enable accurate rounding.
:0: Disable accurate rounding (default).
:1: Enable accurate rounding.
``dsize[=w:h:aspect-method:r:aspect]``
Changes the intended display size/aspect at an arbitrary point in the
@@ -156,7 +152,7 @@ Available filters are:
number (1.33). Alternatively, you may specify the exact display width and
height desired. Note that this filter does *not* do any scaling itself; it
just affects what later scalers (software or hardware) will do when
auto-scaling to the correct aspect.
auto-scaling to correct aspect.
``<w>,<h>``
New display width and height.
@@ -202,7 +198,7 @@ Available filters are:
``<aspect>``
Force an aspect ratio.
``format=fmt=<value>:colormatrix=<value>:...``
``format[=fmt[:outfmt]]``
Restricts the color space for the next filter without doing any conversion.
Use together with the scale filter for a real conversion.
@@ -211,148 +207,19 @@ Available filters are:
For a list of available formats, see ``format=fmt=help``.
``<fmt>``
Format name, e.g. rgb15, bgr24, 420p, etc. (default: don't change).
Format name, e.g. rgb15, bgr24, 420p, etc. (default: yuyv).
``<outfmt>``
Format name that should be substituted for the output. If they do not
have the same bytes per pixel and chroma subsamplimg, it will fail.
``<colormatrix>``
Controls the YUV to RGB color space conversion when playing video. There
are various standards. Normally, BT.601 should be used for SD video, and
BT.709 for HD video. (This is done by default.) Using incorrect color space
results in slightly under or over saturated and shifted colors.
Format name that should be substituted for the output. If this is not
100% compatible with the ``<fmt>`` value, it will crash.
These options are not always supported. Different video outputs provide
varying degrees of support. The ``opengl`` and ``vdpau`` video output
drivers usually offer full support. The ``xv`` output can set the color
space if the system video driver supports it, but not input and output
levels. The ``scale`` video filter can configure color space and input
levels, but only if the output format is RGB (if the video output driver
supports RGB output, you can force this with ``-vf scale,format=rgba``).
If this option is set to ``auto`` (which is the default), the video's
color space flag will be used. If that flag is unset, the color space
will be selected automatically. This is done using a simple heuristic that
attempts to distinguish SD and HD video. If the video is larger than
1279x576 pixels, BT.709 (HD) will be used; otherwise BT.601 (SD) is
selected.
Available color spaces are:
:auto: automatic selection (default)
:bt.601: ITU-R BT.601 (SD)
:bt.709: ITU-R BT.709 (HD)
:bt.2020-ncl: ITU-R BT.2020 non-constant luminance system
:bt.2020-cl: ITU-R BT.2020 constant luminance system
:smpte-240m: SMPTE-240M
``<colorlevels>``
YUV color levels used with YUV to RGB conversion. This option is only
necessary when playing broken files which do not follow standard color
levels or which are flagged wrong. If the video does not specify its
color range, it is assumed to be limited range.
The same limitations as with ``<colormatrix>`` apply.
Available color ranges are:
:auto: automatic selection (normally limited range) (default)
:limited: limited range (16-235 for luma, 16-240 for chroma)
:full: full range (0-255 for both luma and chroma)
``<outputlevels>``
RGB color levels used with YUV to RGB conversion. Normally, output devices
such as PC monitors use full range color levels. However, some TVs and
video monitors expect studio RGB levels. Providing full range output to a
device expecting studio level input results in crushed blacks and whites,
the reverse in dim gray blacks and dim whites.
The same limitations as with ``<colormatrix>`` apply.
Available color ranges are:
:auto: automatic selection (equals to full range) (default)
:limited: limited range (16-235 per component), studio levels
:full: full range (0-255 per component), PC levels
.. note::
It is advisable to use your graphics driver's color range option
instead, if available.
``<primaries>``
RGB primaries the source file was encoded with. Normally this should be set
in the file header, but when playing broken or mistagged files this can be
used to override the setting.
This option only affects video output drivers that perform color
management, for example ``opengl`` with the ``target-prim`` or
``icc-profile`` suboptions set.
If this option is set to ``auto`` (which is the default), the video's
primaries flag will be used. If that flag is unset, the color space will
be selected automatically, using the following heuristics: If the
``<colormatrix>`` is set or determined as BT.2020 or BT.709, the
corresponding primaries are used. Otherwise, if the video height is
exactly 576 (PAL), BT.601-625 is used. If it's exactly 480 or 486 (NTSC),
BT.601-525 is used. If the video resolution is anything else, BT.709 is
used.
Available primaries are:
:auto: automatic selection (default)
:bt.601-525: ITU-R BT.601 (SD) 525-line systems (NTSC, SMPTE-C)
:bt.601-625: ITU-R BT.601 (SD) 625-line systems (PAL, SECAM)
:bt.709: ITU-R BT.709 (HD) (same primaries as sRGB)
:bt.2020: ITU-R BT.2020 (UHD)
:apple: Apple RGB
:adobe: Adobe RGB (1998)
:prophoto: ProPhoto RGB (ROMM)
:cie1931: CIE 1931 RGB
``<gamma>``
Gamma function the source file was encoded with. Normally this should be set
in the file header, but when playing broken or mistagged files this can be
used to override the setting.
This option only affects video output drivers that perform color management.
If this option is set to ``auto`` (which is the default), the gamma will
be set to BT.1886 for YCbCr content, sRGB for RGB content and Linear for
XYZ content.
Available gamma functions are:
:auto: automatic selection (default)
:bt.1886: ITU-R BT.1886 (approximation of BT.601/BT.709/BT.2020 curve)
:srgb: IEC 61966-2-4 (sRGB)
:linear: Linear light
:gamma1.8: Pure power curve (gamma 1.8)
:gamma2.2: Pure power curve (gamma 2.2)
:gamma2.8: Pure power curve (gamma 2.8)
:prophoto: ProPhoto RGB (ROMM) curve
``<stereo-in>``
Set the stereo mode the video is assumed to be encoded in. Takes the
same values as the ``--video-stereo-mode`` option.
``<stereo-out>``
Set the stereo mode the video should be displayed as. Takes the
same values as the ``--video-stereo-mode`` option.
``<rotate>``
Set the rotation the video is assumed to be encoded with in degrees.
The special value ``-1`` uses the input format.
``<dw>``, ``<dh>``
Set the display size. Note that setting the display size such that
the video is scaled in both directions instead of just changing the
aspect ratio is an implementation detail, and might change later.
``<dar>``
Set the display aspect ratio of the video frame. This is a float,
but values such as ``[16:9]`` can be passed too (``[...]`` for quoting
to prevent the option parser from interpreting the ``:`` character).
.. admonition:: Examples
====================== =====================
Valid Invalid (will crash)
====================== =====================
``format=rgb24:bgr24`` ``format=rgb24:420p``
``format=yuyv:uyvy``
====================== =====================
``noformat[=fmt]``
Restricts the color space for the next filter without doing any conversion.
@@ -364,6 +231,150 @@ Available filters are:
``<fmt>``
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
``pp[=[filter1[:option1[:option2...]]/[-]filter2...]]``
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
common options to determine their scope:
``a/autoq``
Automatically switch the subfilter off if the CPU is too slow.
``c/chrom``
Do chrominance filtering, too (default).
``y/nochrom``
Do luminance filtering only (no chrominance).
``n/noluma``
Do chrominance filtering only (no luminance).
.. note::
``--vf=pp:help`` shows a list of available subfilters.
.. note::
Unlike in MPlayer or in earlier versions, you must quote the pp string
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
Available subfilters are:
``hb/hdeblock[:difference[:flatness]]``
horizontal deblocking filter
:<difference>: Difference factor where higher values mean more
deblocking (default: 32).
:<flatness>: Flatness threshold where lower values mean more
deblocking (default: 39).
``vb/vdeblock[:difference[:flatness]]``
vertical deblocking filter
:<difference>: Difference factor where higher values mean more
deblocking (default: 32).
:<flatness>: Flatness threshold where lower values mean more
deblocking (default: 39).
``ha/hadeblock[:difference[:flatness]]``
accurate horizontal deblocking filter
:<difference>: Difference factor where higher values mean more
deblocking (default: 32).
:<flatness>: Flatness threshold where lower values mean more
deblocking (default: 39).
``va/vadeblock[:difference[:flatness]]``
accurate vertical deblocking filter
:<difference>: Difference factor where higher values mean more
deblocking (default: 32).
:<flatness>: Flatness threshold where lower values mean more
deblocking (default: 39).
The horizontal and vertical deblocking filters share the difference and
flatness values so you cannot set different horizontal and vertical
thresholds.
``h1/x1hdeblock``
experimental horizontal deblocking filter
``v1/x1vdeblock``
experimental vertical deblocking filter
``dr/dering``
deringing filter
``tn/tmpnoise[:threshold1[:threshold2[:threshold3]]]``
temporal noise reducer
:<threshold1>: larger -> stronger filtering
:<threshold2>: larger -> stronger filtering
:<threshold3>: larger -> stronger filtering
``al/autolevels[:f/fullyrange]``
automatic brightness / contrast correction
:f/fullyrange: Stretch luminance to (0-255).
``lb/linblenddeint``
Linear blend deinterlacing filter that deinterlaces the given block by
filtering all lines with a (1 2 1) filter.
``li/linipoldeint``
Linear interpolating deinterlacing filter that deinterlaces the given
block by linearly interpolating every second line.
``ci/cubicipoldeint``
Cubic interpolating deinterlacing filter deinterlaces the given block
by cubically interpolating every second line.
``md/mediandeint``
Median deinterlacing filter that deinterlaces the given block by
applying a median filter to every second line.
``fd/ffmpegdeint``
FFmpeg deinterlacing filter that deinterlaces the given block by
filtering every second line with a (-1 4 2 4 -1) filter.
``l5/lowpass5``
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]``
Overrides the quantizer table from the input with the constant
quantizer you specify.
:<quantizer>: quantizer to use
``de/default``
default pp filter combination (hb:a,vb:a,dr:a)
``fa/fast``
fast pp filter combination (h1:a,v1:a,dr:a)
``ac``
high quality pp filter combination (ha:a:128:7,va:a,dr:a)
.. note::
This filter is only available if FFmpeg/Libav has been compiled
with libpostproc enabled.
.. admonition:: Examples
``--vf=pp=hb/vb/dr/al``
horizontal and vertical deblocking, deringing and automatic
brightness/contrast
``--vf=pp=de/-al``
default filters without brightness/contrast correction
``--vf=pp=[default/tmpnoise:1:2:3]``
Enable default filters & temporal denoiser.
``--vf=pp=[hb:y/vb:a]``
Horizontal deblocking on luminance only, and switch vertical
deblocking on or off automatically depending on available CPU time.
``lavfi=graph[:sws-flags[:o=opts]]``
Filter video using FFmpeg's libavfilter.
@@ -413,6 +424,45 @@ Available filters are:
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
forces a specific threading configuration.
``noise[=<strength>[:averaged][:pattern][:temporal][:uniform][:hq]``
Adds noise.
``strength``
Set the noise for all components. If you want different strength
values for luma and chroma, use libavfilter's noise filter directly
(using ``--vf=lavfi=[noise=...]``), or tell the libavfilter developers
to stop being stupid.
``averaged``
averaged temporal noise (smoother, but a lot slower)
``pattern``
mix random noise with a (semi)regular pattern
``temporal``
temporal noise (noise pattern changes between frames)
``uniform``
uniform noise (Gaussian otherwise)
``hq``
high quality (slightly better looking, slightly slower) - not available
when using libavfilter
``hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp]``
This filter aims to reduce image noise producing smooth images and making
still images really still (This should enhance compressibility.).
``<luma_spatial>``
spatial luma strength (default: 4)
``<chroma_spatial>``
spatial chroma strength (default: 3)
``<luma_tmp>``
luma temporal strength (default: 6)
``<chroma_tmp>``
chroma temporal strength (default:
``luma_tmp*chroma_spatial/luma_spatial``)
``eq[=gamma:contrast:brightness:saturation:rg:gg:bg:weight]``
Software equalizer that uses lookup tables (slow), allowing gamma correction
in addition to simple brightness and contrast adjustment. The parameters are
@@ -439,6 +489,39 @@ Available filters are:
and just plain white. A value of 0.0 turns the gamma correction all
the way down while 1.0 leaves it at its full strength (default: 1.0).
``ilpack[=mode]``
When interlaced video is stored in YUV 4:2:0 formats, chroma interlacing
does not line up properly due to vertical downsampling of the chroma
channels. This filter packs the planar 4:2:0 data into YUY2 (4:2:2) format
with the chroma lines in their proper locations, so that in any given
scanline, the luma and chroma data both come from the same field.
``<mode>``
Select the sampling mode.
:0: nearest-neighbor sampling, fast but incorrect
:1: linear interpolation (default)
``unsharp[=lx:ly:la:cx:cy:ca]``
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.
``lx``, ``ly``, ``cx``, ``cy``
width and height of the matrix, odd sized in both directions (min =
3:3, max = 13:11 or 11:13, usually something between 3:3 and 7:7)
``la``, ``ca``
Relative amount of sharpness/blur to add to the image (a sane range
should be -1.5-1.5).
:<0: blur
:>0: sharpen
``swapuv``
Swap U & V plane.
``pullup[=jl:jr:jt:jb:sb:mp]``
Pulldown reversal (inverse telecine) filter, capable of handling mixed
hard-telecine, 24000/1001 fps progressive, and 30000/1001 fps progressive
@@ -470,7 +553,121 @@ Available filters are:
video. The main purpose of setting ``mp`` to a chroma plane is to reduce
CPU load and make pullup usable in realtime on slow machines.
``yadif=[mode:interlaced-only]``
``divtc[=options]``
Inverse telecine for deinterlaced video. If 3:2-pulldown telecined video
has lost one of the fields or is deinterlaced using a method that keeps
one field and interpolates the other, the result is a juddering video that
has every fourth frame duplicated. This filter is intended to find and
drop those duplicates and restore the original film framerate. Two
different modes are available: One-pass mode is the default and is
straightforward to use, but has the disadvantage that any changes in the
telecine phase (lost frames or bad edits) cause momentary judder until the
filter can resync again. Two-pass mode avoids this by analyzing the entire
video beforehand so it will have forward knowledge about the phase changes
and can resync at the exact spot. These passes do *not* correspond to pass
one and two of the encoding process. You must run an extra pass using
``divtc`` pass one before the actual encoding throwing the resulting video
away. Then use ``divtc`` pass two for the actual encoding. If you use
multiple encoder passes, use ``divtc`` pass two for all of them.
The options are:
``pass=1|2``
Use two pass mode.
``file=<filename>``
Set the two pass log filename (default: ``framediff.log``).
``threshold=<value>``
Set the minimum strength the telecine pattern must have for the filter
to believe in it (default: 0.5). This is used to avoid recognizing
false pattern from the parts of the video that are very dark or very
still.
``window=<numframes>``
Set the number of past frames to look at when searching for pattern
(default: 30). Longer window improves the reliability of the pattern
search, but shorter window improves the reaction time to the changes
in the telecine phase. This only affects the one-pass mode. The
two-pass mode currently uses fixed window that extends to both future
and past.
``phase=0|1|2|3|4``
Sets the initial telecine phase for one pass mode (default: 0). The
two-pass mode can see the future, so it is able to use the correct
phase from the beginning, but one-pass mode can only guess. It catches
the correct phase when it finds it, but this option can be used to fix
the possible juddering at the beginning. The first pass of the two
pass mode also uses this, so if you save the output from the first
pass, you get constant phase result.
``deghost=<value>``
Set the deghosting threshold (0-255 for one-pass mode, -255-255 for
two-pass mode, default 0). If nonzero, deghosting mode is used. This
is for video that has been deinterlaced by blending the fields
together instead of dropping one of the fields. Deghosting amplifies
any compression artifacts in the blended frames, so the parameter
value is used as a threshold to exclude those pixels from deghosting
that differ from the previous frame less than specified value. If two
pass mode is used, then negative value can be used to make the filter
analyze the whole video in the beginning of pass-2 to determine
whether it needs deghosting or not and then select either zero or the
absolute value of the parameter. Specify this option for pass 2, it
makes no difference on pass 1.
``phase[=t|b|p|a|u|T|B|A|U][:v]``
Delay interlaced video by one field time so that the field order changes.
The intended use is to fix PAL videos that have been captured with the
opposite field order to the film-to-video transfer. The options are:
``t``
Capture field order top-first, transfer bottom-first. Filter will
delay the bottom field.
``b``
Capture bottom-first, transfer top-first. Filter will delay the top
field.
``p``
Capture and transfer with the same field order. This mode only exists
for the documentation of the other options to refer to, but if you
actually select it, the filter will faithfully do nothing ;-)
``a``
Capture field order determined automatically by field flags, transfer
opposite. Filter selects among ``t`` and ``b`` modes on a frame by frame
basis using field flags. If no field information is available, then this
works just like ``u``.
``u``
Capture unknown or varying, transfer opposite. Filter selects among
``t`` and ``b`` on a frame by frame basis by analyzing the images and
selecting the alternative that produces best match between the fields.
``T``
Capture top-first, transfer unknown or varying. Filter selects among
``t`` and ``p`` using image analysis.
``B``
Capture bottom-first, transfer unknown or varying. Filter selects
among ``b`` and ``p`` using image analysis.
``A``
Capture determined by field flags, transfer unknown or varying. Filter
selects among ``t``, ``b`` and ``p`` using field flags and image
analysis. If no field information is available, then this works just
like ``U``. This is the default mode.
``U``
Both capture and transfer unknown or varying. Filter selects among
``t``, ``b`` and ``p`` using image analysis only.
``v``
Verbose operation. Prints the selected mode for each frame and the
average squared difference between fields for ``t``, ``b``, and ``p``
alternatives. (Ignored when libavfilter is used.)
``yadif=[mode[:enabled=yes|no]]``
Yet another deinterlacing filter
``<mode>``
@@ -479,10 +676,10 @@ Available filters are:
:frame-nospatial: Like ``frame`` but skips spatial interlacing check.
:field-nospatial: Like ``field`` but skips spatial interlacing check.
``<interlaced-only>``
:no: Deinterlace all frames (default).
:yes: Only deinterlace frames marked as interlaced (default if this
filter is inserted via ``deinterlace`` property).
``<enabled>``
:yes: Filter is active (default).
:no: Filter is not active, but can be activated with the ``D`` key
(or any other key that toggles the ``deinterlace`` property).
This filter, is automatically inserted when using the ``D`` key (or any
other key that toggles the ``deinterlace`` property or when using the
@@ -497,6 +694,28 @@ Available filters are:
when inserting yadif with ``--vf``, so using the above methods is
recommended.
``delogo[=x:y:w:h:t:show]``
Suppresses a TV station logo by a simple interpolation of the surrounding
pixels. Just set a rectangle covering the logo and watch it disappear (and
sometimes something even uglier appear - your mileage may vary).
``<x>,<y>``
top left corner of the logo
``<w>,<h>``
width and height of the cleared rectangle
``<t>``
Thickness of the fuzzy edge of the rectangle (added to ``w`` and
``h``). When set to -1, a green rectangle is drawn on the screen to
simplify finding the right ``x``,``y``,``w``,``h`` parameters.
``show``
Draw a rectangle showing the area defined by x/y/w/h.
``screenshot``
Optional filter for screenshot support. This is only needed if the video
output does not provide working direct screenshot support. Note that it is
not always safe to insert this filter by default. See `TAKING SCREENSHOTS`_
for details.
``sub=[=bottom-margin:top-margin]``
Moves subtitle rendering to an arbitrary point in the filter chain, or force
subtitle rendering in the video filter as opposed to using video output OSD
@@ -504,9 +723,9 @@ Available filters are:
``<bottom-margin>``
Adds a black band at the bottom of the frame. The SSA/ASS renderer can
place subtitles there (with ``--sub-use-margins``).
place subtitles there (with ``--ass-use-margins``).
``<top-margin>``
Black band on the top for toptitles (with ``--sub-use-margins``).
Black band on the top for toptitles (with ``--ass-use-margins``).
.. admonition:: Examples
@@ -605,8 +824,6 @@ Available filters are:
Loads an external library to filter the image. The library interface
is the ``vf_dlopen`` interface specified using ``libmpcodecs/vf_dlopen.h``.
.. warning:: This filter is deprecated.
``dll=<library>``
Specify the library to load. This may require a full file system path
in some cases. This argument is required.
@@ -680,64 +897,13 @@ Available filters are:
filters work anyway.)
``concurrent-frames``
Number of frames that should be requested in parallel. The
Number of frames that should be requested in parallel (default: 2). The
level of concurrency depends on the filter and how quickly mpv can
decode video to feed the filter. This value should probably be
proportional to the number of cores on your machine. Most time,
making it higher than the number of cores can actually make it
slower.
By default, this uses the special value ``auto``, which sets the option
to the number of detected logical CPU cores.
The following variables are defined by mpv:
``video_in``
The mpv video source as vapoursynth clip. Note that this has no length
set, which confuses many filters. Using ``Trim`` on the clip with a
high dummy length can turn it into a finite clip.
``video_in_dw``, ``video_in_dh``
Display size of the video. Can be different from video size if the
video does not use square pixels (e.g. DVD).
``container_fps``
FPS value as reported by file headers. This value can be wrong or
completely broken (e.g. 0 or NaN). Even if the value is correct,
if another filter changes the real FPS (by dropping or inserting
frames), the value of this variable might not be useful. Note that
the ``--fps`` command line option overrides this value.
Useful for some filters which insist on having a FPS.
``display_fps``
Refresh rate of the current display. Note that this value can be 0.
``vapoursynth-lazy``
The same as ``vapoursynth``, but doesn't load Python scripts. Instead, a
custom backend using Lua and the raw VapourSynth API is used. The syntax
is completely different, and absolutely no convenience features are
provided. There's no type checking either, and you can trigger crashes.
.. admonition:: Example:
::
video_out = invoke("morpho", "Open", {clip = video_in})
The special variable ``video_in`` is the mpv video source, while the
special variable ``video_out`` is used to read video from. The 1st argument
is the plugin (queried with ``getPluginByNs``), the 2nd is the filter name,
and the 3rd argument is a table with the arguments. Positional arguments
are not supported. The types must match exactly. Since Lua is terrible and
can't distinguish integers and floats, integer arguments must be prefixed
with ``i_``, in which case the prefix is removed and the argument is cast
to an integer. Should the argument's name start with ``i_``, you're out of
luck.
Clips (VSNodeRef) are passed as light userdata, so trying to pass any
other userdata type will result in hard crashes.
``vavpp``
VA-AP-API video post processing. Works with ``--vo=vaapi`` and ``--vo=opengl``
only. Currently deinterlaces. This filter is automatically inserted if
@@ -753,14 +919,6 @@ Available filters are:
Show only first field (going by ``--field-dominance``).
bob
bob deinterlacing (default).
weave, motion-adaptive, motion-compensated
Advanced deinterlacing algorithms. Whether these actually work
depends on the GPU hardware, the GPU drivers, driver bugs, and
mpv bugs.
``<interlaced-only>``
:no: Deinterlace all frames.
:yes: Only deinterlace frames marked as interlaced (default).
``vdpaupp``
VDPAU video post processing. Works with ``--vo=vdpau`` and ``--vo=opengl``
@@ -806,21 +964,12 @@ Available filters are:
``pullup``
Try to apply inverse telecine, needs motion adaptive temporal
deinterlacing.
``interlaced-only=<yes|no>``
If ``yes`` (default), only deinterlace frames marked as interlaced.
``hqscaling=<0-9>``
0
Use default VDPAU scaling (default).
1-9
Apply high quality VDPAU scaling (needs capable hardware).
``vdpaurb``
VDPAU video read back. Works with ``--vo=vdpau`` and ``--vo=opengl`` only.
This filter will read back frames decoded by VDPAU so that other filters,
which are not normally compatible with VDPAU, can be used like normal.
This filter must be specified before ``vdpaupp`` in the filter chain if
``vdpaupp`` is used.
``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
+341 -519
View File
File diff suppressed because it is too large. Load diff
-451
View File
@@ -1,451 +0,0 @@
CHANGES FROM OTHER VERSIONS OF MPLAYER
======================================
**mpv** is based on mplayer2, which in turn is based on the original MPlayer
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
large part of which is incompatible or completely changes how the player
behaves. Although there are still many similarities to its ancestors, **mpv**
should generally be treated as a completely different program.
.. note::
These lists are incomplete.
General Changes from MPlayer to mpv
-----------------------------------
This listing is about changes introduced by mplayer2 and mpv relatively to
MPlayer.
Player
~~~~~~
* New name for the binary (``mpv``). New location for config files (either
``~/.config/mpv/mpv.conf``, or if you want, ``~/.mpv/config``).
* Encoding functionality (replacement for MEncoder, see the `ENCODING`_ section).
* Support for Lua scripting (see the `LUA SCRIPTING`_ section).
* Better pause handling (e.g. do not unpause on a command).
* Precise seeking support.
* Improvements in audio/video sync handling.
* Do not lose settings when playing a new file in the same player instance.
* Slave mode compatibility broken (see below).
* Re-enable screensaver while the player is paused.
* Allow resuming playback at a later point with ``Shift+q``, also see the
``quit_watch_later`` input command.
* ``--keep-open`` option to stop the player from closing the window and
exiting after playback ends.
* A client API, that allows embedding **mpv** into applications
(see ``libmpv/client.h`` in the sources).
Input
~~~~~
* Improved default keybindings. MPlayer bindings are also available (see
``etc/mplayer-input.conf`` in the source tree).
* Improved responsiveness on user input.
* Support for modifier keys (alt, shift, ctrl) in input.conf.
* Allow customizing whether a key binding for seeking shows the video time, the
OSD bar, or nothing (see the `Input Command Prefixes`_ section).
* Support mapping multiple commands to one key.
* Classic LIRC support was removed. Install remotes as input devices instead.
This way they will send X11 key events to the mpv window, which can be bound
using the normal ``input.conf``.
Also see: http://github.com/mpv-player/mpv/wiki/IR-remotes
* Joystick support was removed. It was considered useless and was the cause
of some problems (e.g. a laptop's accelerator being recognized as joystick).
* Support for relative seeking by percentage.
Audio
~~~~~
* Support for gapless audio (see the ``--gapless-audio`` option).
* Support for OSS4 volume control.
* Improved support for PulseAudio.
* Make ``--softvol`` default (**mpv** is not a mixer control panel).
* By default, do pitch correction if playback speed is increased.
* Improved downmixing and output of surround audio:
- Instead of using hardcoded pan filters to do remixing, libavresample is used
- Channel maps are used to identify the channel layout, so e.g. ``3.0`` and
``2.1`` audio can be distinguished.
Video
~~~~~
* Wayland support.
* Native support for VAAPI and VDA. Improved VDPAU video output.
* Improved OpenGL output (see the ``opengl-hq`` video output).
* Make hardware decoding work with the ``opengl`` video output.
* Support for libavfilter (for video->video and audio->audio). This allows
using most of FFmpeg's filters, which improve greatly on the old MPlayer
filters in features, performance, and correctness.
* More correct color reproduction (color matrix generation), including support
for BT.2020 (Ultra HD) and linear XYZ (Digital Cinema) inputs.
* Support for color managed displays, via ICC profiles.
* High-quality image resamplers (see the ``opengl`` ``scale`` suboption).
* Support for scaling in (sigmoidized) linear light.
* Better subtitle rendering using libass by default.
* Improvements when playing multiple files (``-fixed-vo`` is default, do not
reset settings by default when playing a new file).
* Replace image video outputs (``--vo=jpeg`` etc.) with ``--vo=image``.
* Removal of ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding
can handle these use cases. For yuv4mpeg, for example, use::
mpv input.mkv -o output.y4m --no-audio --oautofps --oneverdrop
* Image subtitles (DVDs etc.) are rendered in color and use more correct
positioning (color for image subs can be disabled with ``--sub-gray``).
* Support for the X11 video output is removed, since it was considered
deprecated. SDL video output can still be used as a fallback.
OSD and terminal
~~~~~~~~~~~~~~~~
* Cleaned up terminal output: nicer status line, less useless noise.
* Improved OSD rendering using libass, with full Unicode support.
* New OSD bar with chapter marks. Not positioned in the middle of the video
(this can be customized with the ``--osd-bar-align-y`` option).
* Display list of chapters and audio/subtitle tracks on OSD (see the
`Properties`_ section).
Screenshots
~~~~~~~~~~~
* Instant screenshots without 1-frame delay.
* Support for taking screenshots even with hardware decoding.
* Support for saving screenshots as JPEG or PNG.
* Support for configurable file names.
* Support for taking screenshots with or without subtitles.
Note that the ``screenshot`` video filter is not needed anymore, and should not
be put into the mpv config file.
Miscellaneous
~~~~~~~~~~~~~
* Better MKV support (e.g. ordered chapters, 3D metadata).
* Matroska edition switching at runtime.
* Support for playing URLs of popular streaming sites directly.
(e.g. ``mpv https://www.youtube.com/watch?v=...``).
Requires a recent version of ``youtube-dl`` to be installed. Can be
disabled with ``ytdl=no`` in the mpv config file.
* Support for precise scrolling which scales the parameter of commands. If the
input doesn't support precise scrolling the scale factor stays 1.
* Allow changing/adjusting video filters at runtime. (This is also used to make
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise).
* Improved support for .cue files.
Mac OS X
~~~~~~~~
* Native OpenGL backend.
* Cocoa event loop is independent from MPlayer's event loop, so user
actions like accessing menus and live resizing do not block the playback.
* Apple Remote support.
* Media Keys support.
* VDA support using libavcodec hwaccel API instead of FFmpeg's decoder with up
to 2-2.5x reduction in CPU usage.
Windows
~~~~~~~
* Improved support for Unicode file names.
* Improved window handling.
* Do not block playback when moving the window.
* Improved Direct3D video output.
* Added WASAPI audio output.
Internal changes
~~~~~~~~~~~~~~~~
* Switch to GPLv2+ (see ``Copyright`` file for details).
* Removal of lots of cruft:
- Internal GUI (replaced by the OSC, see the `ON SCREEN CONTROLLER`_ section).
- MEncoder (replaced by native encoding, see the `ENCODING`_ section).
- OSD menu.
- Kernel video drivers for Linux 2.4 (including VIDIX).
- Teletext support.
- Support for dead platforms.
- Most built-in demuxers have been replaced by their libavformat counterparts.
- Built-in network support has been replaced by libavformat's (which also
supports https URLs).
- Embedded copies of libraries (such as FFmpeg).
* General code cleanups (including refactoring or rewrites of many parts).
* New build system.
* Many bug fixes and removal of long-standing issues.
* Generally preferring FFmpeg/Libav over internal demuxers, decoders, and
filters.
Detailed Listing of User-visible Changes
----------------------------------------
This listing is about changed command line switches, slave commands, and similar
things. Completely removed features are not listed.
Command Line Switches
~~~~~~~~~~~~~~~~~~~~~
* There is a new command line syntax, which is generally preferred over the old
syntax. ``-optname optvalue`` becomes ``--optname=optvalue``.
The old syntax will not be removed. However, the new syntax is mentioned in
all documentation and so on, and unlike the old syntax is not ambiguous,
so it is a good thing to know about this change.
* In general, negating switches like ``-noopt`` now have to be written as
``-no-opt`` or ``--no-opt``.
* Per-file options are not the default anymore. You can explicitly specify
file-local options. See ``Usage`` section.
* Many options have been renamed, removed or changed semantics. Some options
that are required for a good playback experience with MPlayer are now
superfluous or even worse than the defaults, so make sure to read the manual
before trying to use your existing configuration with **mpv**.
* Table of renamed/replaced switches:
=========================== ========================================
Old New
=========================== ========================================
``-no<opt>`` ``--no-<opt>`` (add a dash)
``-a52drc level`` ``--ad-lavc-ac3drc=level``
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
``-af volnorm`` ``--af=drc`` (renamed)
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
``-aspect`` ``--video-aspect``
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
``-ass`` ``--sub-ass``
``-audiofile-cache`` (removed; the main cache settings are used)
``-audiofile`` ``--audio-file``
``-benchmark`` ``--untimed`` (no stats)
``-capture`` ``--stream-capture=<filename>``
``-channels`` ``--audio-channels`` (changed semantics)
``-cursor-autohide-delay`` ``--cursor-autohide``
``-delay`` ``--audio-delay``
``-dumpstream`` ``--stream-dump=<filename>``
``-dvdangle`` ``--dvd-angle``
``-endpos`` ``--length``
``-fixed-vo`` (removed; always the default)
``-font`` ``--osd-font``
``-forcedsubsonly`` ``--sub-forced-only``
``-forceidx`` ``--index``
``-format`` ``--audio-format``
``-fsmode-dontuse`` (removed)
``-fstype`` ``--x11-netwm`` (changed semantics)
``-hardframedrop`` ``--framedrop=hard``
``-identify`` (removed; use TOOLS/mpv_identify.sh)
``-idx`` ``--index``
``-lavdopts ...`` ``--vd-lavc-...``
``-lavfdopts`` ``--demuxer-lavf-...``
``-loop 0`` ``--loop=inf``
``-mixer-channel`` AO suboptions (``alsa``, ``oss``)
``-mixer`` AO suboptions (``alsa``, ``oss``)
``-mouse-movements`` ``--input-cursor``
``-msgcolor`` ``--msg-color``
``-msglevel`` ``--msg-level`` (changed semantics)
``-msgmodule`` ``--msg-module``
``-name`` ``--x11-name``
``-noar`` ``--no-input-appleremote``
``-noautosub`` ``--no-sub-auto``
``-noconsolecontrols`` ``--no-input-terminal``
``-nosound`` ``--no-audio``
``-osdlevel`` ``--osd-level``
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
``-playing-msg`` ``--term-playing-msg``
``-pp ...`` ``'--vf=lavfi=[pp=...]'``
``-pphelp`` (See FFmpeg libavfilter documentation.)
``-rawaudio ...`` ``--demuxer-rawaudio-...``
``-rawvideo ...`` ``--demuxer-rawvideo-...``
``-spugauss`` ``--sub-gauss``
``-srate`` ``--audio-samplerate``
``-ss`` ``--start``
``-ssf <sub>`` ``--sws-...``
``-stop-xscreensaver`` ``--stop-screensaver``
``-sub-fuzziness`` ``--sub-auto``
``-sub`` ``--sub-file``
``-subcp`` ``--sub-codepage``
``-subdelay`` ``--sub-delay``
``-subfile`` ``--sub-file``
``-subfont-*`` ``--sub-text-*``, ``--osd-*``
``-subfont-text-scale`` ``--sub-scale``
``-subfont`` ``--sub-text-font``
``-subfps`` ``--sub-fps``
``-subpos`` ``--sub-pos``
``-sws`` ``--sws-scaler``
``-tvscan`` ``--tv-scan``
``-use-filename-title`` ``--title='${filename}'``
``-vc ffh264vdpau`` (etc.) ``--hwdec=vdpau``
``-vobsub`` ``--sub-file`` (pass the .idx file)
``-x W``, ``-y H`` ``--geometry=WxH`` + ``--no-keepaspect``
``-xineramascreen`` ``--screen`` (different values)
``-xy W`` ``--autofit=W``
``-zoom`` Inverse available as ``--video-unscaled``
``dvdnav://`` Removed.
``dvd://1`` ``dvd://0`` (0-based offset)
=========================== ========================================
.. note::
``-opt val`` becomes ``--opt=val``.
.. note::
Quite some video filters, video outputs, audio filters, audio outputs, had
changes in their option parsing. These aren't mentioned in the table above.
Also, some video and audio filters have been removed, and you have to use
libavfilter (using ``--vf=lavfi=[...]`` or ``--af=lavfi=[...]``) to get
them back.
input.conf and Slave Commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Table of renamed input commands:
This lists only commands that are not always gracefully handled by the
internal legacy translation layer. If an input.conf contains any legacy
commands, a warning will be printed when starting the player. The warnings
also show the replacement commands.
Properties containing ``_`` to separate words use ``-`` instead.
+--------------------------------+----------------------------------------+
| Old | New |
+================================+========================================+
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
| | (translation layer cannot deal with |
| | whitespace) |
+--------------------------------+----------------------------------------+
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
+--------------------------------+----------------------------------------+
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
| | |
| | ``set video-aspect 0`` (reset aspect) |
+--------------------------------+----------------------------------------+
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
| | ``<dir>`` parameter unsupported. Use |
| | a negative ``<step>`` instead. |
+--------------------------------+----------------------------------------+
| ``step_property <prop>`` | Prefix ``cycle`` or ``add`` with |
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
| | ``<step>`` |
+--------------------------------+----------------------------------------+
| ``osd_show_property_text`` | ``show_text <text>`` |
| ``<text>`` | The property expansion format string |
| | syntax slightly changed. |
+--------------------------------+----------------------------------------+
| ``osd_show_text`` | Now does the same as |
| | ``osd_show_property_text``. Use the |
| | ``raw`` prefix to disable property |
| | expansion. |
+--------------------------------+----------------------------------------+
| ``show_tracks`` | ``show_text ${track-list}`` |
+--------------------------------+----------------------------------------+
| ``show_chapters`` | ``show_text ${chapter-list}`` |
+--------------------------------+----------------------------------------+
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
+--------------------------------+----------------------------------------+
| ``tv_start_scan`` | ``set tv-scan yes`` |
+--------------------------------+----------------------------------------+
| ``tv_set_channel <val>`` | ``set tv-channel <val>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_channel`` | ``cycle tv-channel`` |
+--------------------------------+----------------------------------------+
| ``dvb_set_channel <v1> <v2>`` | ``set dvb-channel <v1>-<v2>`` |
+--------------------------------+----------------------------------------+
| ``dvb_step_channel`` | ``cycle dvb-channel`` |
+--------------------------------+----------------------------------------+
| ``tv_set_freq <val>`` | ``set tv-freq <val>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_freq`` | ``cycle tv-freq`` |
+--------------------------------+----------------------------------------+
| ``tv_set_norm <norm>`` | ``set tv-norm <norm>`` |
+--------------------------------+----------------------------------------+
| ``tv_step_norm`` | ``cycle tv-norm`` |
+--------------------------------+----------------------------------------+
.. note::
Due to lack of hardware and users using the TV/DVB/PVR features, and
due to the need to cleanup the related command code, it's possible
that the new commands are buggy or behave worse. This can be improved
if testers are available. Otherwise, some of the TV code will be
removed at some point.
Slave mode
~~~~~~~~~~
* Slave mode was removed. A proper slave mode application needed tons of code
and hacks to get
it right. The main problem is that slave mode is a bad and incomplete
interface, and to get around that, applications parsed output messages
intended for users. It is hard to know which messages exactly are parsed by
slave mode applications. This makes it virtually impossible to improve
terminal output intended for users without possibly breaking something.
This is absolutely insane, and since initial improvements to **mpv** quickly
made slave mode incompatible to most applications, it was removed as useless
cruft. The client API (see below) is provided instead.
``--identify`` was replaced by the ``TOOLS/mpv_identify.sh`` wrapper script.
* For some time (until including release 0.4.x), mpv supported a
``--slave-broken`` option. The following options are equivalent:
::
--input-file=/dev/stdin --input-terminal=no
Assuming the system supports ``/dev/stdin``.
(The option was added back in 0.5.1 and sets exactly these options. It was
removed in 0.10.x again.)
* A JSON RPC protocol giving access to the client API is also supported. See
`JSON IPC`_ for more information.
* **mpv** also provides a client API, which can be used to embed the player
by loading it as shared library. (See ``libmpv/client.h`` in the sources.)
It might also be possible to implement a custom slave mode-like protocol
using Lua scripting.
Policy for Removed Features
---------------------------
**mpv** is in active development. If something is in the way of more important
development (such as fixing bugs or implementing new features), we sometimes
remove features. Usually this happens only with old features that either seem
to be useless, or are not used by anyone. Often these are obscure, or
"inherited", or were marked experimental, but never received any particular
praise by any users.
Sometimes, features are replaced by something new. The new code will be either
simpler or more powerful, but doesn't necessarily provide everything the old
feature did.
We can not exclude that we accidentally remove features that are actually
popular. Generally, we do not know how much a specific functionality is used.
If you miss a feature and think it should be re-added, please open an issue
on the mpv bug tracker. Hopefully, a solution can be found. Often, it turns
out that re-adding something is not much of a problem, or that there are
better alternatives.
Why this Fork?
--------------
mplayer2 is practically dead, and mpv started out as a branch containing
new/experimental development. (Some of it was merged right *after* the fork
was made public, seemingly as an acknowledgment that development, or at
least merging, should have been more active.)
MPlayer is focused on not breaking anything, but is stuck with a horrible
codebase resistant to cleanup. (Unless you do what mpv did - merciless and
consequent pruning of bad, old code.) Cleanup and keeping broken things
conflict, so the kind of development mpv strives for can't be done within
MPlayer due to clashing development policies.
Additionally, mplayer2 already had lots of changes over MPlayer, which would
have needed to be backported to the MPlayer codebase. This would not only
have been hard (several years of diverging development), but also would have
been impossible due to the aforementioned MPlayer development policy.
+27 -69
View File
@@ -1,106 +1,64 @@
Release Policy
==============
Once or twice a month, a new release is cut off of the master branch and is
assigned a 0.X.Y version number, where X is incremented each time a release
contains breaking changes, such as changed options or added/removed features,
and Y is incremented if a release contains only bugfixes and other minor
changes.
Every few months, a new release is cut off of the master branch and is assigned
a 0.X.0 version number.
There is only one release branch that keeps track of the latest version and
will not be maintained separately.
As part of the maintenance process, minor releases are made, which are assigned
0.X.Y version numbers. Minor releases contain bug fixes only. They never merge
the master branch, and no features are added to it. Only the latest release is
maintained.
The goal of releases is to provide Linux distributions with something to
package. If you want the newest features, just use the master branch.
We try our best to keep it deployable at all times.
The goal of releases is to provide stability and an unchanged base for the sake
of Linux distributions. If you want the newest features, just use the master
branch, which is stable most of the time, except sometimes, when it's not.
Releases other than the latest release are unsupported and unmaintained.
Release procedure
-----------------
- Merge master into branch release/current.
- Create branch release/0.X or cherry-pick commits to the relevant branch.
- Create and/or update the `RELEASE_NOTES` file.
- Create and/or update the ``RELEASE_NOTES`` file.
- Create and/or update the `VERSION` file.
- Create and/or update the ``VERSION`` file.
- Update `DOCS/client-api-changes.rst` (on major releases).
- Update ``DOCS/client-api-changes.rst`` (on major releases).
- Create tag v0.X.Y.
- Push branch and tag to GitHub.
- Create a new GitHub release using the content of `RELEASE_NOTES` related to
- Create a new GitHub release using the content of ``RELEASE_NOTES`` related to
the new version.
Release notes template
----------------------
Here is a template that can be used for writing the `RELEASE_NOTES` file.
Here is a template that can be used for writing the ``RELEASE_NOTES`` file.
```markdown
Release 0.X.Y
=============
Features
--------
Changes
-------
New
~~~
- List of changes.
- List of new features
Bug fixes
---------
Removed
~~~~~~~
- List of bug fixes.
- List of removed features
New features
------------
Deprecated
~~~~~~~~~~
- List of deprecated features
Behavior
--------
- List of user-visible changes in behavior
Options and Commands
--------------------
Added
~~~~~
- List of added options and commands
Changed
~~~~~~~
- List of changed options and commands
Renamed
~~~~~~~
- List of renamed options and commands
Deprecated
~~~~~~~~~~
- List of deprecated options and commands
Removed
~~~~~~~
- List of removed options and commands
Fixes and Minor Enhancements
----------------------------
- List of fixes and minor enhancements
- List of new features.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running `git log <start>..<end>` in
complete change log can be viewed by running ``git log <start>..<end>`` in
the git repository.
```
@@ -108,6 +66,6 @@ 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
``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.
+19 -36
View File
@@ -1,4 +1,4 @@
This file intends to give a big picture overview of how mpv is structured.
This file intends to give a big picture of how mpv is structured.
player/*.c:
Essentially makes up the player applications, including the main() function
@@ -31,28 +31,15 @@ player/*.c:
* loop, or exit if no next file or quit is requested
(see enum stop_play_reason)
* call exit_player_with_rc()
* run_playloop():
* calls fill_audio_out_buffers()
This checks whether new audio needs to be decoded, and pushes it
to the AO.
* calls write_video()
Decode new video, and push it to the VO.
* determines whether playback of the current file has ended
* determines when to start playback after seeks
* and calls a whole lot of other stuff
(Really, this function does everything.)
Things worth saying about the playback core:
- most state is in MPContext (core.h), which is not available to the
subsystems
- the currently played tracks are in mpctx->current_tracks, and decoder
state in d_video/d_audio/d_sub
- the currently played tracks are in sh_video and sh_audio
- the timeline stuff is used only with MKV ordered chapters (and some other
minor features: cue, edl)
- most state is in MPContext (mp_core.h), which is not available to the
subsystems
- the other subsystems rarely call back into the frontend, and the frontend
polls them instead (probably a good thing)
- one exceptions are wakeup callbacks, which notify a "higher" component
of a changed situation in a subsystem
I like to call the player/*.c files the "frontend".
@@ -75,7 +62,8 @@ talloc.h & talloc.c:
allocation call will never return NULL.
One very useful feature of talloc is fast tracking of memory leaks. ("Fast"
as in it doesn't require valgrind.) You can enable it by setting the
as in it doesn't require valgrind.) You can enable it by passing the option
--leak-report as first parameter, or better, setting the
MPV_LEAK_REPORT environment variable to "1":
export MPV_LEAK_REPORT=1
This will list all unfree'd allocations on exit.
@@ -115,7 +103,7 @@ options/options.h, options/options.c
options.c. Most default values for options and MPOpts are in
mp_default_opts at the end of options.c.
MPOpts is unfortunately quite monolithic, and virtually accessed by
MPOpts is unfortunarely quite monolithic, and virtually accessed by
everything.But some components (like video outputs and video filters) have
their own sub-option tables separate from MPOpts.
@@ -123,7 +111,7 @@ options/options.h, options/options.c
parser-mpcmd.c, and uses the option table in options.c.
input/input.c:
This translates keyboard input comming from VOs and other sources (such
This translates keyboard input comming from libvo and other sources (such
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
@@ -134,7 +122,7 @@ input/input.c:
of input commands somewhere else.
common/msg.h:
All terminal output must go through mp_msg().
All terminal output should go through mp_msg().
stream/*:
File input is implemented here. stream.h/.c provides a simple stream based
@@ -193,7 +181,7 @@ video/out/:
vo_opengl can pick a windowing system at runtime, e.g. the same binary can
provide both X11 and Cocoa support on OSX.
VOs can be reconfigured at runtime. A vo_config() call can change the video
VOs can be reconfigured at runtime. A config() call can change the video
resolution and format, without destroying the window.
vo_vdpau and vo_opengl should be taken as reference.
@@ -215,12 +203,9 @@ audio/filter/:
audio/out/:
Audio outputs.
Unlike VOs, AOs can't be reconfigured on a format change. On audio format
changes, the AO will simply be closed and re-opened.
There are wrappers to support for two types of audio APIs: push.c and
pull.c. ao.c calls into one of these. They contain generic code to deal
with the data flow these APIs impose.
Unlike VOs, AOs can't be reconfigured on a format change. Without
--gapless-audio, even playing a new file will close and re-open the audio
device.
Note that mpv synchronizes the video to the audio. That's the reason
why buggy audio drivers can have a bad influence on playback quality.
@@ -233,14 +218,12 @@ sub/:
the OSD text renderer (which uses libass, and takes care of all the tricky
fontconfig/freetype API usage and text layouting).
The VOs call osd.c to render OSD and subtitle (via e.g. osd_draw()). osd.c
in turn asks dec_sub.c for subtitle overlay bitmaps, which relays the
request to one of the sd_*.c subtitle decoders/renderers.
Subtitle loading is in demux/. The MPlayer subreader.c is mostly gone - parts
of it survive in demux_subreader.c. It's used as last fallback, or to handle
some text subtitle types on Libav. It should go away eventually. Normally,
subtitles are loaded via demux_lavf.c.
Subtitle loading is now in demux/ instead. demux_libass.c wraps loading
.ass subtitles via libass. demux_lavf.c loads most subtitle types via
FFmpeg. demux_subreader.c is the old MPlayer code. It's used as last
fallback, or to handle some text subtitle types on Libav. (It also can
load UTF-16 encoded subtitles without requiring the use of -sub-codepage.)
demux_subreader.c should eventually go away (maybe).
The subtitles are passed to dec_sub.c and the subtitle decoders in sd_*.c
as they are demuxed. All text subtitles are rendered by sd_ass.c. If text
+4 -5
View File
@@ -1,7 +1,7 @@
waf build system overview
=========================
mpv's new build system is based on waf and it should completely replace the
mpv's new build system is based on waf and it should completly replace the
custom ./configure + Makefile based system inherited from MPlayer.
Goals and the choice of waf
@@ -20,9 +20,8 @@ shortcomings:
part is this pieces are spread apart in the configure and copy pasted for
any single case. That brings us to..
2) --enable-feature has to be overridden by the user and helps them understand that
they have libraries missing and should install them for the feature to be
enabled.
2) --enable-feature has to override the user and help him understand that he
has libraries missing and should install them for the feature to be enabled.
3) Must be customizable, hackable, pleasant to the developer eyes and to work
with in general.
@@ -65,7 +64,7 @@ This defines a feature called ``vdpau`` which can be enabled or disabled by
the users with configure flags (that's the meaning of ``--``). This feature
depends on another feature whose name is ``x11``, and the autodetection check
consists of running ``pkg-config`` and looking for ``vdpau`` with version
``>= 0.2``. If the check succeeds a ``#define HAVE_VDPAU 1`` will be added to
``>= 0.2``. If the check succeds a ``#define HAVE_VDPAU 1`` will be added to
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.
The defines names are automatically prepended with ``HAVE_``, capitalized and
+40 -94
View File
@@ -1,39 +1,19 @@
![http://mpv.io/](https://raw.githubusercontent.com/mpv-player/mpv.io/master/source/images/mpv-logo-128.png)
mpv
===
## mpv
[![Build Status](https://api.travis-ci.org/mpv-player/mpv.png)](https://travis-ci.org/mpv-player/mpv)
--------------
Overview
--------
* [Overview](#overview)
* [Downloads](#downloads)
* [Compilation](#compilation)
* [FFmpeg vs. Libav](#ffmpeg-vs-libav)
* [Release cycle](#release-cycle)
* [Bug reports](#bug-reports)
* [Contributing](#contributing)
* [Relation to MPlayer and mplayer2](#relation-to-mplayer-and-mplayer2)
* [Wiki](https://github.com/mpv-player/mpv/wiki)
* [Man pages](http://mpv.io/manual/master/)
* [Contact](#contact)
* [License](#license)
## Overview
**mpv** is a media player based on MPlayer and mplayer2. It supports a wide
**mpv** is a movie player based on MPlayer and mplayer2. It supports a wide
variety of video file formats, audio and video codecs, and subtitle types.
Releases can be found on the [release list][releases].
## Downloads
For semi-official builds and third-party packages please see
[mpv.io](http://mpv.io/installation/).
## Compilation
If you are wondering what's different from mplayer2 and MPlayer you can read
more about the [changes][changes].
Compilation
-----------
Compiling with full features requires development files for several
external libraries. Below is a list of some important requirements.
@@ -57,29 +37,23 @@ To build the software you can use `./waf build`: the result of the compilation
will be located in `build/mpv`. You can use `./waf install` to install mpv
to the *prefix* after it is compiled.
NOTE: Using the old build system (with `./old-configure`) should still work,
but will be removed in a future version of mpv.
Essential dependencies (incomplete list):
- gcc or clang
- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
- Audio output development headers (libasound/ALSA, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
and either libswresample or libavresample)
At least FFmpeg 2.4.0 or Libav 11 is required.
- zlib
- iconv (normally provided by the system libc)
- libass (OSD, OSC, text subtitles)
- Lua (optional, required for the OSC pseudo-GUI and youtube-dl integration)
- libjpeg (optional, used for screenshots only)
- Enca (optional, for subtitle charset detection)
- vdpau and vaapi libraries for hardware decoding on Linux (optional)
- Audio output development headers (libasound, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavresample)
At least FFmpeg 2.1.4 or Libav 10 is required.
- libjpeg (for screenshots)
- libquvi if you want to play Youtube videos directly
Libass dependencies:
- gcc or clang, yasm on x86 and x86_64
- fribidi, freetype, fontconfig development headers (for libass)
- harfbuzz (optional, required for correct rendering of combining characters,
particularly for correct rendering of non-English text on OSX, and
Arabic/Indic scripts on any platform)
FFmpeg dependencies:
@@ -100,19 +74,16 @@ If you are running Mac OSX and using homebrew we provide [homebrew-mpv][homebrew
to date formula that compiles mpv with sensible dependencies and defaults for
OSX.
If you want to build a Windows binary, you either have to use MSYS2 and MinGW,
or cross-compile from Linux with MinGW. See [Windows compilation][windows_compilation].
## FFmpeg vs. Libav
FFmpeg vs. Libav
----------------
Generally, mpv should work with the latest release as well as the git version
of both FFmpeg and Libav. But FFmpeg is preferred, and some mpv features work
with FFmpeg only (subtitle formats in particular).
with FFmpeg only. See the [wiki article][ffmpeg_vs_libav] about the issue.
## Release cycle
Release cycle
-------------
Every few months, a new release is cut off of the master branch and is assigned
a 0.X.0 version number.
@@ -130,64 +101,39 @@ Releases other than the latest release are unsupported and unmaintained.
See the [release policy document][release-policy] for more information.
## Bug reports
Bug reports
-----------
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug
reports or feature requests.
## Contributing
Contributing
------------
For small changes you can just send us pull requests through GitHub. For bigger
changes come and talk to us on IRC before you start working on them. It will
make code review easier for both parties later on.
## Relation to MPlayer and mplayer2
Contact
-------
These forms of contact are meant to ask questions about mpv usage, give
feedback on mpv and discuss it's development.
mpv is based on mplayer2, which in turn is based on the original MPlayer
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
large part of which is incompatible or completely changes how the player
behaves. Although there are still many similarities to its ancestors, mpv
should generally be treated as a completely different program.
If possible, please avoid posting bugs here and use the [issue tracker][issue-tracker]
instead.
mpv was forked because we wanted to modernize MPlayer. This includes
removing cruft (including features which stopped making sense 10 years ago),
and of course adding modern features. Such huge and intrusive changes made it
infeasible to work directly with MPlayer, which is mostly focused on
preservation, so a fork had to be made. (Actually, mpv is based on mplayer2,
which already started this process of removing cruft.)
- **Users IRC Channel**: `#mpv-player` on `irc.freenode.net`
- **Users Mailing List**: `mpv-users@googlegroups.com` ([Archive / Subscribe][mpv-users]).
- **Devel Mailing List**: `mpv-devel@googlegroups.com` ([Archive / Subscribe][mpv-devel])
In general, mpv should be considered a completely new program, rather than a
MPlayer drop-in replacement.
To contact the `mpv` team in private write to `mpv-team@googlegroups.com`.
If you are wondering what's different from mplayer2 and MPlayer, an incomplete
list of changes is located [here][mplayer-changes].
## Contact
Most activity happens on the IRC channel and the github issue tracker. The
mailing lists are mostly unused.
- **GitHub issue tracker**: [issue tracker][issue-tracker] (report bugs here)
- **User IRC Channel**: `#mpv` on `irc.freenode.net`
- **Developer IRC Channel**: `#mpv-devel` on `irc.freenode.net`
To contact the `mpv` team in private write to `mpv-team@googlegroups.com`. Use
only if discretion is required.
[releases]: https://github.com/mpv-player/mpv/releases
[changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/man/changes.rst
[mpv-build]: https://github.com/mpv-player/mpv-build
[homebrew-mpv]: https://github.com/mpv-player/homebrew-mpv
[issue-tracker]: https://github.com/mpv-player/mpv/issues
[mpv-users]: https://groups.google.com/forum/?hl=en#!forum/mpv-users
[mpv-devel]: https://groups.google.com/forum/?hl=en#!forum/mpv-devel
[ffmpeg_vs_libav]: https://github.com/mpv-player/mpv/wiki/FFmpeg-versus-Libav
[release-policy]: https://github.com/mpv-player/mpv/blob/master/DOCS/release-policy.md
[windows_compilation]: https://github.com/mpv-player/mpv/blob/master/DOCS/compile-windows.md
[mplayer-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/mplayer-changes.rst
## License
[GPLv2](https://github.com/mpv-player/mpv/blob/master/LICENSE)
+146 -1031
View File
File diff suppressed because it is too large. Load diff
-1
View File
@@ -19,7 +19,6 @@ def is_user_lib(objfile, libname):
not "libSystem." in libname and \
not "libc." in libname and \
not "libgcc." in libname and \
not os.path.basename(libname) == 'Python' and \
not os.path.basename(objfile) in libname
def otool(objfile):
+2 -2
View File
@@ -5,8 +5,8 @@
conv() {
echo
identify -format "icon: %w %h" "$1"
convert "$1" -depth 8 rgba:-
identify -format "icon: %w %h" $1
convert $1 -depth 8 rgba:-
}
(echo "# File generated by gen-x11-icon.sh" ;
+59 -56
View File
@@ -1,15 +1,23 @@
#!/bin/sh
: "${MPV:=mpv}"
: "${ILDETECT_MPV:=$MPV}"
: "${ILDETECT_MPVFLAGS:=--start=35% --length=35}"
: "${ILDETECT_DRY_RUN:=}"
: "${ILDETECT_QUIET:=}"
: "${ILDETECT_RUN_INTERLACED_ONLY:=}"
: "${ILDETECT_FORCE_RUN:=}"
case "$0" in
*/*)
MYDIR=${0%/*}
;;
*)
MYDIR=.
;;
esac
# This script uses ffmpeg's "idet" filter for interlace detection. In the
# long run this should replace ildetect.sh+ildetect.so.
: ${MPV:=mpv}
: ${ILDETECT_MPV:=$MPV}
: ${ILDETECT_MPV:=$MPV}
: ${ILDETECT_MPVFLAGS:=--start=35% --length=35}
: ${ILDETECT_DRY_RUN:=}
: ${ILDETECT_QUIET:=}
: ${ILDETECT_RUN_INTERLACED_ONLY:=}
: ${ILDETECT_FORCE_RUN:=}
: ${MAKE:=make}
# exit status:
# 0 progressive
@@ -26,62 +34,57 @@ testfun()
--o= --vo=null --no-audio --untimed \
$ILDETECT_MPVFLAGS \
| { if [ -n "$ILDETECT_QUIET" ]; then cat; else tee /dev/stderr; fi } \
| grep "Parsed_idet_0: Multi frame detection: "
| grep "Parsed_idet_0: Multi frame detection: " | tail -n 1
}
judge()
{
tff=0
bff=0
progressive=0
undetermined=0
while read -r _ _ _ _ _ _ tff1 _ bff1 _ progressive1 _ undetermined1 _; do
case "$tff1$bff1$progressive1$undetermined1" in
*[!0-9]*)
printf >&2 'ERROR: Unrecognized idet output: %s\n' "$out"
exit 16
;;
esac
tff=$((tff + tff1))
bff=$((bff + bff1))
progressive=$((progressive + progressive1))
undetermined=$((undetermined + undetermined1))
done <<EOF
$(testfun "$@" | sed 's/:/: /g')
EOF
out=`testfun "$@"`
tff=${out##* TFF:}; tff=${tff%% *}
bff=${out##* BFF:}; bff=${bff%% *}
progressive=${out##* Progressive:}; progressive=${progressive%% *}
undetermined=${out##* Undetermined:}; undetermined=${undetermined%% *}
case "$tff$bff$progressive$undetermined" in
*[!0-9]*)
echo >&2 "ERROR: Unrecognized idet output: $out"
exit 16
;;
esac
interlaced=$((bff + tff))
determined=$((interlaced + progressive))
if [ "$undetermined" -gt "$determined" ] || [ "$determined" -lt 250 ]; then
if [ $undetermined -gt $determined ] || [ $determined -lt 250 ]; then
echo >&2 "ERROR: Less than 50% or 250 frames are determined."
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
echo >&2 "Assuming interlacing."
if [ "$tff" -gt $((bff * 10)) ]; then
verdict="interlaced-tff"
elif [ "$bff" -gt $((tff * 10)) ]; then
verdict="interlaced-bff"
if [ $tff -gt $((bff * 10)) ]; then
verdict=interlaced-tff
elif [ $bff -gt $((tff * 10)) ]; then
verdict=interlaced-bff
else
verdict="interlaced"
verdict=interlaced
fi
elif [ $((interlaced * 20)) -gt "$progressive" ]; then
elif [ $((interlaced * 20)) -gt $progressive ]; then
# At least 5% of the frames are interlaced!
if [ "$tff" -gt $((bff * 10)) ]; then
verdict="interlaced-tff"
elif [ "$bff" -gt $((tff * 10)) ]; then
verdict="interlaced-bff"
if [ $tff -gt $((bff * 10)) ]; then
verdict=interlaced-tff
elif [ $bff -gt $((tff * 10)) ]; then
verdict=interlaced-bff
else
echo >&2 "ERROR: Content is interlaced, but can't determine field order."
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
echo >&2 "Assuming interlacing with default field order."
verdict="interlaced"
verdict=interlaced
fi
else
# Likely progressive
verdict="progressive"
# Likely progrssive.
verdict=progressive
fi
printf '%s\n' "$verdict"
echo "$verdict"
}
judge "$@" --vf-clr
@@ -91,43 +94,43 @@ case "$verdict" in
[ -n "$ILDETECT_RUN_INTERLACED_ONLY" ] || \
$ILDETECT_MPV "$@"
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 0
;;
interlaced-tff)
judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=tff\],pullup
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=top
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],pullup
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=top
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 1
;;
*)
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],yadif
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=top
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 2
;;
esac
;;
interlaced-bff)
judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=bff\],pullup
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=bottom
case "$verdict" in
progressive)
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],pullup
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=bottom
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 1
;;
*)
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],yadif
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=bottom
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 2
;;
esac
@@ -139,14 +142,14 @@ case "$verdict" in
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=pullup
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 1
;;
*)
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" --vf-pre=yadif
r=$?
[ $r -eq 0 ] || exit $((r | 16))
[ $r -eq 0 ] || exit $(($r | 16))
exit 2
;;
esac
+1 -7
View File
@@ -84,7 +84,7 @@ sub elem_by_hexid {
# used by elem when setting the 'valname' key
use constant TYPE_MAP => {
uint => 'uint64_t',
str => 'char *',
str => 'struct bstr',
binary => 'struct bstr',
ebml_id => 'uint32_t',
float => 'double',
@@ -232,7 +232,6 @@ sub define_matroska {
elem('Block', 'a1', 'binary'),
elem('BlockDuration', '9b', 'uint'),
elem('ReferenceBlock*', 'fb', 'sint'),
elem('DiscardPadding', '75A2', 'sint'),
}),
elem('SimpleBlock*', 'a3', 'binary'),
}),
@@ -257,8 +256,6 @@ sub define_matroska {
elem('CodecPrivate', '63a2', 'binary'),
elem('CodecName', '258688', 'str'),
elem('CodecDecodeAll', 'aa', 'uint'),
elem('CodecDelay', '56AA', 'uint'),
elem('SeekPreRoll', '56BB', 'uint'),
elem('Video', 'e0', {
elem('FlagInterlaced', '9a', 'uint'),
elem('PixelWidth', 'b0', 'uint'),
@@ -268,7 +265,6 @@ sub define_matroska {
elem('DisplayUnit', '54b2', 'uint'),
elem('FrameRate', '2383e3', 'float'),
elem('ColourSpace', '2eb524', 'binary'),
elem('StereoMode', '53b8', 'uint'),
}),
elem('Audio', 'e1', {
elem('SamplingFrequency', 'b5', 'float'),
@@ -296,8 +292,6 @@ sub define_matroska {
elem('CueTrackPositions*', 'b7', {
elem('CueTrack', 'f7', 'uint'),
elem('CueClusterPosition', 'f1', 'uint'),
elem('CueRelativePosition','f0', 'uint'),
elem('CueDuration', 'b2', 'uint'),
}),
}),
}),
+10 -5
View File
@@ -4,11 +4,16 @@ mpv lua scripts
The lua scripts in this folder can be loaded on a one-time basis by
adding the option
--script=/path/to/script.lua
--lua=/path/to/script.lua
to mpv's command line.
Where appropriate, they may also be placed in ~/.config/mpv/scripts/ from
where they will be automatically loaded when mpv starts.
Some of these are just for testing mpv internals.
Unless otherwise specified, they are also suitable for inclusion in
the `~/.mpv/lua` directory where they will be loaded every time mpv
starts, obviating the need to load them with the above `--lua=...`
argument. This is acceptable as they do only basic argument parsing
and key-binding registration, until those bound keys are actually
pressed. They should therefore not interfere with normal playback
(unless you have a conflicting user-defined key-binding, in which
case, you may want to modify either the `mp.add_key_binding()` calls
in the scripts, or your keybinding).
-13
View File
@@ -1,13 +0,0 @@
local utils = require("mp.utils")
mp.observe_property("audio-device-list", "native", function(name, val)
print("Audio device list changed:")
for index, e in ipairs(val) do
print(" - '" .. e.name .. "' (" .. e.description .. ")")
end
end)
mp.observe_property("audio-out-detected-device", "native", function(name, val)
print("Detected audio device changed:")
print(" - '" .. val)
end)
+19 -19
View File
@@ -21,7 +21,7 @@
-- The default delay between insertion of the cropdetect and
-- crop filters may be overridden by adding
--
-- --script-opts=autocrop.detect_seconds=<number of seconds>
-- --lua-opts=autocrop.detect_seconds=<number of seconds>
--
-- to mpv's arguments. This may be desirable to allow cropdetect more
-- time to collect data.
@@ -42,11 +42,11 @@ function del_filter_if_present(label)
-- error if the filter doesn't exist
local vfs = mp.get_property_native("vf")
for i,vf in pairs(vfs) do
if vf["label"] == label then
table.remove(vfs, i)
mp.set_property_native("vf", vfs)
return true
end
if vf["label"] == label then
table.remove(vfs, i)
mp.set_property_native("vf", vfs)
return true
end
end
return false
end
@@ -65,10 +65,10 @@ function autocrop_start()
-- insert the cropdetect filter
ret=mp.command(
string.format(
'vf add @%s:lavfi=graph="cropdetect=limit=24:round=2:reset=0"',
cropdetect_label
)
string.format(
'vf add @%s:lavfi=graph="cropdetect=limit=24:round=2:reset=0"',
cropdetect_label
)
)
-- wait to gather data
timer=mp.add_timeout(detect_seconds, do_crop)
@@ -77,15 +77,15 @@ end
function do_crop()
-- get the metadata
local cropdetect_metadata = mp.get_property_native(
string.format("vf-metadata/%s", cropdetect_label)
string.format("vf-metadata/%s", cropdetect_label)
)
-- use it to crop if its valid
if cropdetect_metadata then
if cropdetect_metadata["lavfi.cropdetect.w"]
and cropdetect_metadata["lavfi.cropdetect.h"]
and cropdetect_metadata["lavfi.cropdetect.x"]
and cropdetect_metadata["lavfi.cropdetect.y"]
then
if cropdetect_metadata["lavfi.cropdetect.w"]
and cropdetect_metadata["lavfi.cropdetect.h"]
and cropdetect_metadata["lavfi.cropdetect.x"]
and cropdetect_metadata["lavfi.cropdetect.y"]
then
mp.command(string.format("vf add @%s:crop=%s:%s:%s:%s",
crop_label,
cropdetect_metadata["lavfi.cropdetect.w"],
@@ -94,9 +94,9 @@ function do_crop()
cropdetect_metadata["lavfi.cropdetect.y"]))
else
mp.msg.error(
"Got empty crop data. You might need to increase detect_seconds."
)
end
"Got empty crop data. You might need to increase detect_seconds."
)
end
else
mp.msg.error(
"No crop data. Was the cropdetect filter successfully inserted?"
-145
View File
@@ -1,145 +0,0 @@
-- This script uses the lavfi idet filter to automatically insert the
-- appropriate deinterlacing filter based on a short section of the
-- currently playing video.
--
-- It registers the key-binding ctrl+d, which when pressed, inserts the filters
-- ``vf=lavfi=idet,pullup,vf=lavfi=idet``. After 4 seconds, it removes these
-- filters and decides whether the content is progressive, interlaced, or
-- telecined and the interlacing field dominance.
--
-- Based on this information, it may set mpv's ``deinterlace`` property (which
-- usually inserts the yadif filter), or insert the ``pullup`` filter if the
-- content is telecined. It also sets mpv's ``field-dominance`` property.
--
-- OPTIONS:
-- The default detection time may be overridden by adding
--
-- --script-opts=autodeint.detect_seconds=<number of seconds>
--
-- to mpv's arguments. This may be desirable to allow idet more
-- time to collect data.
--
-- To see counts of the various types of frames for each detection phase,
-- the verbosity can be increased with
--
-- --msg-level autodeint=v
--
-- This script requires a recent version of ffmpeg for which the idet
-- filter adds the required metadata.
require "mp.msg"
script_name = mp.get_script_name()
detect_label = string.format("%s-detect", script_name)
pullup_label = string.format("%s", script_name)
ivtc_detect_label = string.format("%s-ivtc-detect", script_name)
-- number of seconds to gather cropdetect data
detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name)))
if not detect_seconds then
detect_seconds = 4
end
function del_filter_if_present(label)
-- necessary because mp.command('vf del @label:filter') raises an
-- error if the filter doesn't exist
local vfs = mp.get_property_native("vf")
for i,vf in pairs(vfs) do
if vf["label"] == label then
table.remove(vfs, i)
mp.set_property_native("vf", vfs)
return true
end
end
return false
end
function start_detect()
-- exit if detection is already in progress
if timer then
mp.msg.warn("already detecting!")
return
end
mp.set_property("deinterlace","no")
del_filter_if_present(pullup_label)
-- insert the detection filter
local cmd = string.format('vf add @%s:lavfi=graph="idet",@%s:pullup,@%s:lavfi=graph="idet"',
detect_label, pullup_label, ivtc_detect_label)
if not mp.command(cmd) then
mp.msg.error("failed to insert detection filters")
return
end
-- wait to gather data
timer = mp.add_timeout(detect_seconds, select_filter)
end
function stop_detect()
del_filter_if_present(detect_label)
del_filter_if_present(ivtc_detect_label)
timer = nil
end
progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3, 4
function judge(label)
-- get the metadata
local result = mp.get_property_native(string.format("vf-metadata/%s", label))
num_tff = tonumber(result["lavfi.idet.multiple.tff"])
num_bff = tonumber(result["lavfi.idet.multiple.bff"])
num_progressive = tonumber(result["lavfi.idet.multiple.progressive"])
num_undetermined = tonumber(result["lavfi.idet.multiple.undetermined"])
num_interlaced = num_tff + num_bff
num_determined = num_interlaced + num_progressive
mp.msg.verbose(label.." progressive = "..num_progressive)
mp.msg.verbose(label.." interlaced-tff = "..num_tff)
mp.msg.verbose(label.." interlaced-bff = "..num_bff)
mp.msg.verbose(label.." undetermined = "..num_undetermined)
if num_determined < num_undetermined then
mp.msg.warn("majority undetermined frames")
end
if num_progressive > 20*num_interlaced then
return progressive
elseif num_tff > 10*num_bff then
return interlaced_tff
elseif num_bff > 10*num_tff then
return interlaced_bff
else
return interlaced
end
end
function select_filter()
-- handle the first detection filter results
verdict = judge(detect_label)
if verdict == progressive then
mp.msg.info("progressive: doing nothing")
stop_detect()
return
elseif verdict == interlaced_tff then
mp.set_property("field-dominance", "top")
elseif verdict == interlaced_bff then
mp.set_property("field-dominance", "bottom")
elseif verdict == interlaced then
mp.set_property("field-dominance", "auto")
end
-- handle the ivtc detection filter results
verdict = judge(ivtc_detect_label)
if verdict == progressive then
mp.msg.info(string.format("telecinied with %s field dominance: using pullup", mp.get_property("field-dominance")))
stop_detect()
else
mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", mp.get_property("field-dominance")))
del_filter_if_present(pullup_label)
mp.set_property("deinterlace","yes")
stop_detect()
end
end
mp.add_key_binding("ctrl+d", script_name, start_detect)
+10 -63
View File
@@ -4,19 +4,8 @@
-- alphabetically, and adds entries before and after the current file to
-- the internal playlist. (It stops if the it would add an already existing
-- playlist entry at the same position - this makes it "stable".)
-- Add at most 5000 * 2 files when starting a file (before + after).
MAXENTRIES = 5000
function Set (t)
local set = {}
for _, v in pairs(t) do set[v] = true end
return set
end
EXTENSIONS = Set {
'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp',
'mp3', 'wav', 'ogv', 'flac', 'm4a', 'wma',
}
-- Add at most 5 * 2 files when starting a file (before + after).
MAXENTRIES = 5
mputils = require 'mp.utils'
@@ -29,51 +18,14 @@ function add_files_at(index, files)
end
end
function get_extension(path)
return string.match(path, "%.([^%.]+)$" )
end
table.filter = function(t, iter)
for i = #t, 1, -1 do
if not iter(t[i]) then
table.remove(t, i)
end
end
end
function find_and_add_entries()
local path = mp.get_property("path", "")
local dir, filename = mputils.split_path(path)
if #dir == 0 then
return
end
local pl_count = mp.get_property_number("playlist-count", 1)
if (pl_count > 1 and autoload == nil) or
(pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then
return
else
autoload = true
end
local files = mputils.readdir(dir, "files")
if files == nil then
return
end
table.filter(files, function (v, k)
local ext = get_extension(v)
if ext == nil then
return false
end
return EXTENSIONS[string.lower(ext)]
end)
table.sort(files, function (a, b)
return string.lower(a) < string.lower(b)
end)
if dir == "." then
dir = ""
end
table.sort(files)
local pl = mp.get_property_native("playlist", {})
local pl_current = mp.get_property_number("playlist-pos", 0) + 1
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
@@ -87,36 +39,31 @@ function find_and_add_entries()
if current == nil then
return
end
local append = {[-1] = {}, [1] = {}}
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
for dir = -1, 1, 2 do -- 2 iterations, with dir = -1 and +1
for i = 1, MAXENTRIES do
local file = files[current + i * direction]
local pl_e = pl[pl_current + i * direction]
local file = files[current + i * dir]
local pl_e = pl[pl_current + i * dir]
if file == nil or file[1] == "." then
break
end
local filepath = dir .. file
if pl_e then
-- If there's a playlist entry, and it's the same file, stop.
if pl_e.filename == filepath then
if pl_e.filename == file then
break
end
end
if direction == -1 then
if dir == -1 then
if pl_current == 1 then -- never add additional entries in the middle
mp.msg.info("Prepending " .. file)
table.insert(append[-1], 1, filepath)
table.insert(append[-1], 1, file)
end
else
mp.msg.info("Adding " .. file)
table.insert(append[1], filepath)
table.insert(append[1], file)
end
end
end
add_files_at(pl_current + 1, append[1])
add_files_at(pl_current, append[-1])
end
+3 -3
View File
@@ -20,9 +20,9 @@ pullup_label = string.format("%s-pullup", script_name)
function pullup_on()
for i,vf in pairs(mp.get_property_native('vf')) do
if vf['label'] == pullup_label then
return "yes"
end
if vf['label'] == pullup_label then
return "yes"
end
end
return "no"
end
+31 -31
View File
@@ -4,7 +4,7 @@
-- parameters. See the entry for "drc" under the "AUDIO FILTERS"
-- section of the man page for a complete description of the filter.
--
-- This script registers the key-binding "\" to toggle the filter between
-- This script registers the key-binding "\" to toggle the filter between
--
-- * off
-- * method=1 (single-sample smoothing)
@@ -21,30 +21,30 @@ script_name = mp.get_script_name()
function print_state(params)
if params then
mp.osd_message(script_name..":\n"
.."method = "..params["method"].."\n"
.."target = "..params["target"])
mp.osd_message(script_name..":\n"
.."method = "..params["method"].."\n"
.."target = "..params["target"])
else
mp.osd_message(script_name..":\noff")
mp.osd_message(script_name..":\noff")
end
end
function get_index_of_drc(afs)
for i,af in pairs(afs) do
if af["label"] == script_name then
return i
end
if af["label"] == script_name then
return i
end
end
end
function append_drc(afs)
afs[#afs+1] = {
name = "drc",
label = script_name,
params = {
method = "1",
target = "0.25"
}
name = "drc",
label = script_name,
params = {
method = "1",
target = "0.25"
}
}
print_state(afs[#afs]["params"])
end
@@ -53,34 +53,34 @@ function modify_or_create_af(fun)
afs = mp.get_property_native("af")
i = get_index_of_drc(afs)
if not i then
append_drc(afs)
append_drc(afs)
else
fun(afs, i)
fun(afs, i)
end
mp.set_property_native("af", afs)
end
function drc_toggle_method_handler()
modify_or_create_af(
function (afs, i)
new_method=(afs[i]["params"]["method"]+1)%3
if new_method == 0 then
table.remove(afs, i)
print_state(nil)
else
afs[i]["params"]["method"] = tostring((afs[i]["params"]["method"])%2+1)
print_state(afs[i]["params"])
end
end
function (afs, i)
new_method=(afs[i]["params"]["method"]+1)%3
if new_method == 0 then
table.remove(afs, i)
print_state(nil)
else
afs[i]["params"]["method"] = tostring((afs[i]["params"]["method"])%2+1)
print_state(afs[i]["params"])
end
end
)
end
function drc_scale_target(factor)
modify_or_create_af(
function (afs)
afs[i]["params"]["target"] = tostring(afs[i]["params"]["target"]*factor)
print_state(afs[i]["params"])
end
function (afs)
afs[i]["params"]["target"] = tostring(afs[i]["params"]["target"]*factor)
print_state(afs[i]["params"])
end
)
end
@@ -92,7 +92,7 @@ function drc_quieter_handler()
drc_scale_target(0.5)
end
-- toggle between off, method 1 and method 2
-- toggle between off, method 1 and method 2
mp.add_key_binding("\\", "drc_toggle_method", drc_toggle_method_handler)
-- increase or decrease target volume
mp.add_key_binding("ctrl+9", "drc_quieter", drc_quieter_handler)
+8 -6
View File
@@ -1,14 +1,16 @@
-- Test script for property change notification mechanism.
-- Note that watching/reading some properties can be very expensive, or
-- require the player to synchronously wait on network (when playing
-- remote files), so you should in general only watch properties you
-- are interested in.
local utils = require("mp.utils")
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 '" ..
utils.to_string(val) .. "'")
format_property_val(val) .. "'")
end)
end
-19
View File
@@ -1,19 +0,0 @@
--makes mpv disable ontop when pausing and re-enable it again when resuming playback
--please note that this won't do anything if ontop was not enabled before pausing
local was_ontop = false
mp.observe_property("pause", "bool", function(name, value)
local ontop = mp.get_property_native("ontop")
if value then
if ontop then
mp.set_property_native("ontop", false)
was_ontop = true
end
else
if was_ontop and not ontop then
mp.set_property_native("ontop", true)
end
was_ontop = false
end
end)
-20
View File
@@ -1,20 +0,0 @@
-- This script pauses playback when minimizing the window, and resumes playback
-- if it's brought back again. If the player was already paused when minimizing,
-- then try not to mess with the pause state.
local did_minimize = false
mp.observe_property("window-minimized", "bool", function(name, value)
local pause = mp.get_property_native("pause")
if value == true then
if pause == false then
mp.set_property_native("pause", true)
did_minimize = true
end
elseif value == false then
if did_minimize and (pause == true) then
mp.set_property_native("pause", false)
end
did_minimize = false
end
end)
+1 -3
View File
@@ -1,7 +1,5 @@
-- Rebuild the terminal status line as a lua script
-- Be aware that this will require more cpu power!
-- Also, this is based on a rather old version of the
-- builtin mpv status line.
-- Add a string to the status line
function atsl(s)
@@ -30,7 +28,7 @@ function update_status_line()
atsl(mp.get_property_osd("time-pos"))
atsl(" / ");
atsl(mp.get_property_osd("duration"));
atsl(mp.get_property_osd("length"));
atsl(" (")
atsl(mp.get_property_osd("percent-pos", -1))
-74
View File
@@ -1,74 +0,0 @@
-- zones.lua: mpv script for handling commands depending on where the mouse pointer is at,
-- mostly for mouse wheel handling, by configuring it via input.conf, e.g.:
--
-- Ported from avih's ( https://github.com/avih ) zones.js
--
-- Vertical positions can be top, middle, bottom or "*" to represent the whole column.
-- Horizontal positions can be left, middle, bottom or "*" to represent the whole row.
-- "default" will be the fallback command to be used if no command is assigned to that area.
--
-- input.conf example of use:
-- # wheel up/down with mouse
-- MOUSE_BTN3 script_message_to zones commands "middle-right: add brightness 1" "*-left: add volume 5" "default: seek 10"
-- MOUSE_BTN4 script_message_to zones commands "middle-right: add brightness -1" "*-left: add volume -5" "default: seek -10"
local ZONE_THRESH_PERCENTAGE = 20;
-- sides get 20% each, mid gets 60%, same vertically
local msg = mp.msg
function getMouseZone()
-- returns the mouse zone as two strings [top/middle/bottom], [left/middle/right], e.g. "middle", "right"
local screenW, screenH = mp.get_osd_resolution()
local mouseX, mouseY = mp.get_mouse_pos()
local threshY = screenH * ZONE_THRESH_PERCENTAGE / 100
local threshX = screenW * ZONE_THRESH_PERCENTAGE / 100
local yZone = (mouseY < threshY) and "top" or (mouseY < (screenH - threshY)) and "middle" or "bottom"
local xZone = (mouseX < threshX) and "left" or (mouseX < (screenW - threshX)) and "middle" or "right"
return yZone, xZone
end
function main (...)
local arg={...}
msg.debug('commands: \n\t'..table.concat(arg,'\n\t'))
local keyY, keyX = getMouseZone()
msg.debug("mouse at: " .. keyY .. '-' .. keyX)
local fallback = nil
for i, v in ipairs(arg) do
cmdY = v:match("^([%w%*]+)%-?[%w%*]*:")
cmdX = v:match("^[%w%*]*%-([%w%*]+)%s*:")
cmd = v:match("^[%S]-%s*:%s*(.+)")
msg.debug('cmdY: '..tostring(cmdY))
msg.debug('cmdX: '..tostring(cmdX))
msg.debug('cmd : '..tostring(cmd))
if (cmdY == keyY and cmdX == keyX) then
msg.verbose("running cmd: "..cmd)
mp.command(cmd)
return
elseif (cmdY == "*" and cmdX == keyX) or
(cmdY == keyY and cmdX == "*") then
msg.verbose("running cmd: "..cmd)
mp.command(cmd)
return
elseif cmdY == "default" then
fallback = cmd
end
end
if fallback ~= nil then
msg.verbose("running cmd: "..fallback)
mp.command(fallback)
return
else
msg.debug("no command assigned for "..keyY .. '-' .. keyX)
return
end
end
mp.register_script_message("commands", main)
+1 -1
View File
@@ -64,7 +64,7 @@ sub generate_c_header {
# Output a counter variable for each element
# (presence/absence for scalars, item count for arrays)
for my $subel (sort values %{$el->{subelements}}) {
for my $subel (values %{$el->{subelements}}) {
print " int n_$subel->{fieldname};\n"
}
print "};\n";
+150 -127
View File
@@ -17,134 +17,157 @@
# When multiple files were specified, their info will be put into FOO_* for the
# first file, FOO_1_* for the second file, FOO_2_* for the third file, etc.
__midentify__main() {
case "$0" in
mpv_identify.sh|*/mpv_identify.sh)
# we are NOT being sourced
[ -n "$1" ] && set -- '' "$@"
;;
esac
if [ "$#" -lt 2 ]; then
cat >&2 <<EOF
Usage 1 (for humans only): $0 filename.mkv
will print all property values.
Note that this output really shouldn't be parsed, as the
format is subject to change.
Usage 2 (for use by scripts): see top of this file
NOTE: for mkv with ordered chapters, this may
not always identify the specified file, but the
file providing the first chapter. Specify
--no-ordered-chapters to prevent this.
EOF
return 2
fi
local LF="
"
local nextprefix="$1"
shift
if [ -n "$nextprefix" ]; then
# in case of error, we always want this unset
unset "${nextprefix}path"
fi
local allprops="
filename
path
stream-start
stream-end
stream-length
demuxer
length
chapters
editions
titles
audio
audio-bitrate
audio-codec
audio-format
channels
samplerate
video
angle
video-bitrate
video-codec
video-format
video-aspect
fps
width
height
dwidth
dheight
sub
"
# TODO add metadata support once mpv can do it
local propstr="X-MIDENTIFY-START:$LF"
local key
for key in $allprops; do
propstr="${propstr}X-MIDENTIFY: $key \${=$key}$LF"
key="$(printf '%s\n' "$key" | tr - _)"
unset "$nextprefix$key"
done
local fileindex=0
local prefix=
local line
while IFS= read -r line; do
case "$line" in
X-MIDENTIFY-START:)
if [ -n "$nextprefix" ]; then
prefix="$nextprefix"
if [ "$fileindex" -gt 0 ]; then
nextprefix="${prefix%${fileindex}_}"
fi
fileindex="$((fileindex+1))"
nextprefix="${nextprefix}${fileindex}_"
for key in $allprops; do
key="$(printf '%s\n' "$key" | tr - _)"
unset "$nextprefix$key"
done
else
if [ "$fileindex" -gt 0 ]; then
printf '\n'
fi
fileindex="$((fileindex+1))"
fi
case "$0" in
mpv_identify.sh|*/mpv_identify.sh)
# we are NOT being sourced
case "$1" in
'')
;;
X-MIDENTIFY:\ *)
local key="${line#X-MIDENTIFY: }"
local value="${key#* }"
key="${key%% *}"
key="$(printf '%s\n' "$key" | tr - _)"
if [ -n "$nextprefix" ]; then
if [ -z "$prefix" ]; then
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
elif [ -n "$value" ]; then
eval "$prefix$key"='"$value"'
fi
else
if [ -n "$value" ]; then
printf '%s=%s\n' "$key" "$value"
fi
fi
*)
set -- '' "$@"
;;
esac
done <<EOF
$(${MPV:-mpv} --term-playing-msg="$propstr" --vo=null --ao=null \
--frames=1 --quiet --no-cache --no-config -- "$@")
EOF
}
;;
esac
__midentify__main "$@"
if [ $# -lt 2 ]; then
echo >&2 "Usage 1 (for humans only): $0 filename.mkv"
echo >&2 "will print all property values."
echo >&2 "Note that this output really shouldn't be parsed, as the"
echo >&2 "format is subject to change."
echo >&2
echo >&2 "Usage 2 (for use by scripts): see top of this file"
echo >&2
echo >&2 "NOTE: for mkv with ordered chapters, this may"
echo >&2 "not always identify the specified file, but the"
echo >&2 "file providing the first chapter. Specify"
echo >&2 "--no-ordered-chapters to prevent this."
exit 1
fi
if [ -z "$MPV" ]; then
MPV="mpv"
fi
__midentify__LF="
"
__midentify__nextprefix=$1
shift
if [ -n "$__midentify__nextprefix" ]; then
# in case of error, we always want this unset
eval unset $__midentify__nextprefix'path'
fi
__midentify__allprops="
filename
path
stream-start
stream-end
stream-length
demuxer
length
chapters
editions
titles
audio
audio-bitrate
audio-codec
audio-format
channels
samplerate
video
angle
video-bitrate
video-codec
video-format
video-aspect
fps
width
height
dwidth
dheight
sub
"
# TODO add metadata support once mpv can do it
__midentify__propstr="X-MIDENTIFY-START:$__midentify__LF"
for __midentify__key in $__midentify__allprops; do
__midentify__propstr=$__midentify__propstr"X-MIDENTIFY: $__midentify__key \${=$__midentify__key}$__midentify__LF"
__midentify__key=`echo "$__midentify__key" | tr - _`
eval unset $__midentify__nextprefix$__midentify__key
done
__midentify__output=`$MPV --term-playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet --no-cache --no-config "$@"`
__midentify__fileindex=0
__midentify__prefix=
while :; do
case "$__midentify__output" in
'')
break
;;
*$__midentify__LF*)
__midentify__line=${__midentify__output%%$__midentify__LF*}
__midentify__output=${__midentify__output#*$__midentify__LF}
;;
*)
__midentify__line=$__midentify__output
__midentify__output=
;;
esac
case "$__midentify__line" in
X-MIDENTIFY-START:)
if [ -n "$__midentify__nextprefix" ]; then
__midentify__prefix=$__midentify__nextprefix
if [ $__midentify__fileindex -gt 0 ]; then
__midentify__nextprefix=${__midentify__prefix%$__midentify__fileindex\_}
fi
__midentify__fileindex=$(($__midentify__fileindex+1))
__midentify__nextprefix=$__midentify__nextprefix$__midentify__fileindex\_
for __midentify__key in $__midentify__allprops; do
__midentify__key=`echo "$__midentify__key" | tr - _`
eval unset $__midentify__nextprefix$__midentify__key
done
else
if [ $__midentify__fileindex -gt 0 ]; then
echo
fi
__midentify__fileindex=$(($__midentify__fileindex+1))
fi
;;
X-MIDENTIFY:\ *)
__midentify__key=${__midentify__line#X-MIDENTIFY:\ }
__midentify__value=${__midentify__key#* }
__midentify__key=${__midentify__key%% *}
__midentify__key=`echo "$__midentify__key" | tr - _`
if [ -n "$__midentify__nextprefix" ]; then
if [ -z "$__midentify__prefix" ]; then
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
elif [ -n "$__midentify__value" ]; then
eval $__midentify__prefix$__midentify__key=\$__midentify__value
fi
else
if [ -n "$__midentify__value" ]; then
echo "$__midentify__key=$__midentify__value"
fi
fi
;;
esac
done
unset __midentify__fileindex
unset __midentify__allprops
unset __midentify__key
unset __midentify__LF
unset __midentify__line
unset __midentify__output
unset __midentify__nextprefix
unset __midentify__prefix
unset __midentify__propstr
unset __midentify__value
+2 -4
View File
@@ -3,7 +3,6 @@
import os
import shutil
import sys
import fileinput
from optparse import OptionParser
def sh(command):
@@ -36,11 +35,10 @@ def copy_binary(binary_name):
shutil.copy(binary_name, target_binary(binary_name))
def apply_plist_template(plist_file, version):
for line in fileinput.input(plist_file, inplace=1):
print (line.rstrip().replace('${VERSION}', version))
sh("sed -i -e 's/${VERSION}/%s/g' %s" % (version, plist_file))
def main():
version = sh("./version.sh").strip()
version = sh("./version.sh --print").strip()
usage = "usage: %prog [options] arg"
parser = OptionParser(usage)
@@ -88,7 +88,6 @@
<string>VOB</string>
<string>WEBM</string>
<string>WMV</string>
<string>MK3D</string>
<string>asf</string>
<string>avi</string>
<string>cpk</string>
@@ -123,7 +122,6 @@
<string>vob</string>
<string>webm</string>
<string>wmv</string>
<string>mk3d</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>document.icns</string>
@@ -210,7 +208,6 @@
<string>mms</string>
<string>mmst</string>
<string>http</string>
<string>https</string>
<string>httpproxy</string>
<string>rtp</string>
<string>rtsp</string>
@@ -516,47 +513,6 @@
<key>public.filename-extension</key>
<array>
<string>mkv</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.movie</string>
</array>
<key>UTTypeDescription</key>
<string>Matroska stereoscopic/3D video</string>
<key>UTTypeIconFile</key>
<string>document.icns</string>
<key>UTTypeIdentifier</key>
<string>io.mpv.mk3d</string>
<key>UTTypeReferenceURL</key>
<string>http://www.matroska.org</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mk3d</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.movie</string>
</array>
<key>UTTypeDescription</key>
<string>WebM Video</string>
<key>UTTypeIconFile</key>
<string>document.icns</string>
<key>UTTypeIdentifier</key>
<string>io.mpv.webm</string>
<key>UTTypeReferenceURL</key>
<string>http://www.webmproject.org</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>webm</string>
</array>
</dict>
@@ -1 +0,0 @@
profile=pseudo-gui
+36 -87
View File
@@ -1,15 +1,9 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plot
import sys
import re
filename = sys.argv[1]
events = ".*"
if len(sys.argv) > 2:
events = sys.argv[2]
event_regex = re.compile(events)
"""
This script is meant to display stats written by mpv --dump-stats=filename.
In general, each line in that file is an event of the form:
@@ -24,124 +18,79 @@ e.g.:
Currently, the following event types are supported:
'signal' <name> singular event
'start' <name> start of the named event
'end' <name> end of the named event
'value' <float> <name> a normal value (as opposed to event)
'event-timed' <ts> <name> singular event at the given timestamp
'value-timed' <ts> <float> <name> a value for an event at the given timestamp
'range-timed' <ts1> <ts2> <name> like start/end, but explicit times
<name> singular event (same as 'signal')
'start' <name> start of the named event
'end' <name> end of the named event
'value' <float> <name> a normal value (as opposed to event)
<event> singular event
"""
class G:
events = {}
sevents = [] # events, deterministically sorted
start = None
# http://matplotlib.org/api/markers_api.html#module-matplotlib.markers
markers = ["o", "8", "s", "p", "*", "h", "+", "x", "D"]
def find_marker():
if len(G.markers) == 0:
return "o"
m = G.markers[0]
G.markers = G.markers[1:]
return m
class Event:
pass
def get_event(event, evtype):
def get_event(event):
if event not in G.events:
e = Event()
G.events[event] = e
e.name = event
e.vals = []
e.type = evtype
e.marker = "o"
if e.type == "event-signal":
e.marker = find_marker()
if not event_regex.match(e.name):
return e
G.events[event] = e
e.type = "unknown"
G.sevents = list(G.events.values())
G.sevents.sort(key=lambda x: x.name)
return G.events[event]
SCALE = 1e6 # microseconds to seconds
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
line = line.strip()
if not line:
continue
ts, event = line.split(" ", 1)
ts = int(ts) / SCALE
ts = int(ts) / 1000 # milliseconds
if G.start is None:
G.start = ts
ts = ts - G.start
if event.startswith("start "):
e = get_event(event[6:], "event")
e = get_event(event[6:])
e.type = "event"
e.vals.append((ts, 0))
e.vals.append((ts, 1))
elif event.startswith("end "):
e = get_event(event[4:], "event")
e = get_event(event[4:])
e.type = "event"
e.vals.append((ts, 1))
e.vals.append((ts, 0))
elif event.startswith("value "):
_, val, name = event.split(" ", 2)
val = float(val)
e = get_event(name, "value")
e = get_event(name)
e.type = "value"
e.vals.append((ts, val))
elif event.startswith("event-timed "):
_, val, name = event.split(" ", 2)
val = int(val) / SCALE - G.start
e = get_event(name, "event-signal")
e.vals.append((val, 1))
elif event.startswith("range-timed "):
_, ts1, ts2, name = event.split(" ", 3)
ts1 = int(ts1) / SCALE - G.start
ts2 = int(ts2) / SCALE - G.start
e = get_event(name, "event")
e.vals.append((ts1, 0))
e.vals.append((ts1, 1))
e.vals.append((ts2, 1))
e.vals.append((ts2, 0))
elif event.startswith("value-timed "):
_, tsval, val, name = event.split(" ", 3)
tsval = int(tsval) / SCALE - G.start
val = float(val)
e = get_event(name, "value")
e.vals.append((tsval, val))
elif event.startswith("signal "):
name = event.split(" ", 2)[1]
e = get_event(name, "event-signal")
e.vals.append((ts, 1))
else:
e = get_event(event, "event-signal")
e = get_event(event)
e.type = "event-signal"
e.vals.append((ts, 1))
# deterministically sort them; make sure the legend is sorted too
G.sevents = list(G.events.values())
G.sevents.sort(key=lambda x: x.name)
hasval = False
for e, index in zip(G.sevents, range(len(G.sevents))):
m = len(G.sevents)
if e.type == "value":
hasval = True
else:
e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals]
fig = plot.figure()
fig.hold(True)
ax = [None, None]
plots = 2 if hasval else 1
ax[0] = fig.add_subplot(plots, 1, 1)
if hasval:
ax[1] = fig.add_subplot(plots, 1, 2, sharex=ax[0])
legends = [[], []]
ao_events = ["ao fill", "audio wait"]
for e in G.sevents:
cur = ax[1 if e.type == "value" else 0]
pl, = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
if e.name in ao_events:
e.vals = [(x, y * 0.5) for (x, y) in e.vals]
plot.hold(True)
mainpl = plot.subplot(2, 1, 1)
legend = []
for e in G.sevents:
if e.type == "value":
plot.subplot(2, 1, 2, sharex=mainpl)
else:
plot.subplot(2, 1, 1)
pl, = plot.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
if e.type == "event-signal":
plot.setp(pl, marker = e.marker, linestyle = "None")
for cur in ax:
if cur is not None:
cur.legend()
plot.setp(pl, marker = "o", linestyle = "None")
legend.append(pl)
plot.subplot(2, 1, 1)
plot.legend(legend, [pl.get_label() for pl in legend])
plot.show()
+41 -16
View File
@@ -21,13 +21,20 @@ script after that will start a new mpv instance.
Note that you can control the mpv instance by writing to the command fifo:
echo "cycle fullscreen" > ~/.umpv_fifo
echo "cycle fullscreen" > /tmp/umpv-fifo-*
Note: you can supply custom mpv path and options with the MPV environment
variable. The environment variable will be split on whitespace, and the
first item is used as path to mpv binary and the rest is passed as options
_if_ the script starts mpv. If mpv is not started by the script (i.e. mpv
is already running), this will be ignored.
Note: you can supply custom options with the UMPV_OPTIONS environment variable.
The environment variable will be split on whitespace, and each item is
passed as option to mpv _if_ the script starts mpv. If mpv is not started
by the script (i.e. mpv is already running), the options will be ignored.
Warning:
The script attempts to make sure the FIFO is safely created (i.e. not world-
writable), and checks that it's really a FIFO. This is important for security,
because the FIFO allows anyone with write access to run arbitrary commands
in mpv's context using the "run" input command. If you are worried about
security, you should verify that the code handles these concerns correctly.
"""
import sys
@@ -38,17 +45,21 @@ import fcntl
import stat
import string
if len(sys.argv) < 1:
sys.exit(1)
files = sys.argv[1:]
# this is the same method mpv uses to decide this
# this is about the same method mpv uses to decide this
def is_url(filename):
parts = filename.split("://", 1)
parts = filename.split(":", 1)
if len(parts) < 2:
return False
# protocol prefix has no special characters => it's an URL
allowed_symbols = string.ascii_letters + string.digits + '_'
prefix = parts[0]
return all(map(lambda c: c in allowed_symbols, prefix))
for c in prefix:
if string.ascii_letters.find(c) < 0:
return False
return True
# make them absolute; also makes them safe against interpretation as options
def make_abs(filename):
@@ -57,7 +68,10 @@ def make_abs(filename):
return filename
files = [make_abs(f) for f in files]
FIFO = os.path.join(os.getenv("HOME"), ".umpv_fifo")
opts_env = os.getenv("UMPV_OPTIONS")
opts_env = opts_env.split() if opts_env else []
FIFO = "/tmp/umpv-fifo-" + os.getenv("USER")
fifo_fd = -1
try:
@@ -70,6 +84,14 @@ except OSError as e:
else:
raise e
if fifo_fd >= 0:
st = os.fstat(fifo_fd)
if (((st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)) != 0) or
(not stat.S_ISFIFO(st.st_mode)) or
(st.st_uid != os.getuid())):
print("error: command FIFO is not a FIFO or has bogus permissions")
sys.exit(1)
if fifo_fd >= 0:
# Unhandled race condition: what if mpv is terminating right now?
fcntl.fcntl(fifo_fd, fcntl.F_SETFL, 0) # set blocking mode
@@ -86,12 +108,15 @@ else:
try:
os.unlink(FIFO)
except OSError as e:
pass
os.mkfifo(FIFO, 0o600)
if e.errno == errno.ENOENT:
pass
else:
raise e
os.mkfifo(FIFO, int("0600", 8))
opts = (os.getenv("MPV") or "mpv").split()
opts.extend(["--no-terminal", "--force-window", "--input-file=" + FIFO,
"--"])
opts = ["mpv", "--no-terminal", "--force-window", "--input-file=" + FIFO]
opts.extend(opts_env)
opts.append("--")
opts.extend(files)
subprocess.check_call(opts)
+1 -1
View File
@@ -19,7 +19,7 @@
# 02110-1301 USA
#
FILTERS = telecine tile rectangle framestep ildetect
FILTERS = showqscale telecine tile rectangle framestep ildetect
COMMON = filterutils.o
OBJECTS = $(patsubst %,%.o,$(FILTERS)) $(COMMON)
+2 -1
View File
@@ -90,7 +90,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
if (argc != 1 && argc != 2)
return -1;
framestep_data_t *framestep = calloc(1,sizeof(framestep_data_t));
framestep_data_t *framestep = malloc(sizeof(framestep_data_t));
memset(framestep, 0, sizeof(*framestep));
framestep->step = atoi(argv[0]);
framestep->pos = (argc >= 2) ? atoi(argv[1]) : 0;
+2 -1
View File
@@ -267,7 +267,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
(void) argc;
(void) argv;
ildetect_data_t *il = calloc(1,sizeof(ildetect_data_t));
ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
memset(il, 0, sizeof(*il));
#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
il->method = A(0, 0);
+1
View File
@@ -11,6 +11,7 @@ esac
: ${MPV:=mpv}
: ${ILDETECT_MPV:=$MPV}
: ${ILDETECT_MPV:=$MPV}
: ${ILDETECT_MPVFLAGS:=--start=35% --length=35}
: ${ILDETECT_DRY_RUN:=}
: ${ILDETECT_QUIET:=}
+2 -1
View File
@@ -348,7 +348,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
{ "yuv420p", "yuv420p" },
{ NULL, NULL }
};
privdata *priv = calloc(1,sizeof(privdata));
privdata *priv = malloc(sizeof(privdata));
memset(priv, 0, sizeof(*priv));
priv->step = 8;
if(argc >= 1)
priv->w = atoi(argv[0]);
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2012 Rudolf Polzer <divVerent@xonotic.org>
*
* This file is part of mpv's vf_dlopen examples.
*
* mpv's vf_dlopen examples are free software; you can redistribute them and/or
* modify them under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* mpv's vf_dlopen examples are distributed in the hope that they will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv's vf_dlopen examples; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vf_dlopen.h"
/*
* qscale visualizer
*
* usage: --vf=dlopen=/path/to/showqscale.so
*
* uses reddish colors for high QPs, and greenish colors for low QPs
*/
#define PLANE_Y 0
#define PLANE_U 1
#define PLANE_V 2
static int qs_put_image(struct vf_dlopen_context *ctx)
{
unsigned int x, y, p;
assert(ctx->inpic.planes == ctx->outpic[0].planes);
for (p = 0; p < ctx->outpic[0].planes; ++p) {
assert(ctx->inpic.planewidth[p] == ctx->outpic[0].planewidth[p]);
assert(ctx->inpic.planeheight[p] == ctx->outpic[0].planeheight[p]);
if ((p == PLANE_U || p == PLANE_V) && ctx->inpic_qscale)
continue;
#if 0
// copy as is
for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y)
memcpy(
&ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y],
&inpic[ctx->outpic[0].planeofs[p] + ctx->inpic.planestride[p] * y],
ctx->outpic[0].planewidth[p]
);
#else
// reduce contrast
for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y)
for (x = 0; x < ctx->outpic[0].planewidth[p]; ++x)
ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y + x] =
0x20 + ((ctx->inpic.plane[p][ctx->inpic.planestride[p] * y + x] * 3) >> 2);
#endif
}
if (ctx->inpic_qscale) {
int qmin = 255;
int qmax = -255;
// clear U plane
p = PLANE_U;
for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y)
memset(
&ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y],
0x80,
ctx->outpic[0].planewidth[p]
);
// replace V by the qp (0 = green, 12 = red)
p = PLANE_V;
for (y = 0; y < ctx->outpic[0].planeheight[p]; ++y)
for (x = 0; x < ctx->outpic[0].planewidth[p]; ++x) {
int q = ctx->inpic_qscale[
(x >> (ctx->inpic_qscaleshift - ctx->inpic.planexshift[p])) +
(y >> (ctx->inpic_qscaleshift - ctx->inpic.planeyshift[p])) * ctx->inpic_qscalestride];
if (q < qmin)
qmin = q;
if (q > qmax)
qmax = q;
int v = 128 + 21 * (q - 6); // range: 0 = green, 12 = red
if (v < 0)
v = 0;
if (v > 255)
v = 255;
ctx->outpic[0].plane[p][ctx->outpic[0].planestride[p] * y + x] = v;
}
// printf("qscale range: %d .. %d\n", qmin, qmax);
}
ctx->outpic[0].pts = ctx->inpic.pts;
return 1;
}
int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv)
{
VF_DLOPEN_CHECK_VERSION(ctx);
(void) argc;
(void) argv;
static struct vf_dlopen_formatpair map[] = {
{ "yuv420p", "yuv420p" },
{ NULL, NULL }
};
ctx->format_mapping = map;
ctx->put_image = qs_put_image;
return 1;
}
+2 -1
View File
@@ -237,7 +237,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
if (!a0[0] || a0[1] || !a1[0] || argc > 2)
return -1;
tc_data_t *tc = calloc(1,sizeof(tc_data_t));
tc_data_t *tc = malloc(sizeof(tc_data_t));
memset(tc, 0, sizeof(*tc));
if (a0[0] == 't')
tc->firstfield = 0;
+2 -1
View File
@@ -150,7 +150,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
if (argc != 2)
return -1;
tile_data_t *tile = calloc(1,sizeof(tile_data_t));
tile_data_t *tile = malloc(sizeof(tile_data_t));
memset(tile, 0, sizeof(*tile));
tile->cols = atoi(argv[0]);
tile->rows = atoi(argv[1]);
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# Example of script for using mpv with youtube-dl
set -e
cookies_dir="$(mktemp -d /tmp/youtube-dl_mpv.XXXX)"
cookies_file="${cookies_dir}/cookies"
user_agent="$(youtube-dl --dump-user-agent)" # or set whatever you want
video_url="$(youtube-dl \
--user-agent="$user_agent" \
--cookies="$cookies_file" \
--get-url \
"$1")"
shift
mpv \
--cookies \
--cookies-file="$cookies_file" \
--user-agent="$user_agent" \
"$@" -- $video_url
rm -rf "$cookies_dir"
+54 -134
View File
@@ -7,15 +7,15 @@ use warnings;
my $mpv = $ARGV[0] || 'mpv';
my @opts = parse_main_opts('--list-options', '^ (\-\-[^\s\*]*)\*?\s*(.*)');
my @opts = parse_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)', 1);
my @ao = parse_opts('--ao=help', '^ ([^\s\:]*)\s*: (.*)');
my @vo = parse_opts('--vo=help', '^ ([^\s\:]*)\s*: (.*)');
my @ao = parse_opts("$mpv --ao=help", '^ ([^\s\:]*)\s*: (.*)');
my @vo = parse_opts("$mpv --vo=help", '^ ([^\s\:]*)\s*: (.*)');
my @af = parse_opts('--af=help', '^ ([^\s\:]*)\s*: (.*)');
my @vf = parse_opts('--vf=help', '^ ([^\s\:]*)\s*: (.*)');
my @af = parse_opts("$mpv --af=help", '^ ([^\s\:]*)\s*: (.*)');
my @vf = parse_opts("$mpv --vf=help", '^ ([^\s\:]*)\s*: (.*)');
my @protos = parse_opts('--list-protocols', '^ ([^\s]*)');
my @protos = parse_opts("$mpv --list-protocols", '^ ([^\s]*)');
my ($opts_str, $ao_str, $vo_str, $af_str, $vf_str, $protos_str);
@@ -34,37 +34,8 @@ chomp $af_str;
$vf_str .= qq{ '$_' \\\n} foreach (@vf);
chomp $vf_str;
$protos_str = join(' ', @protos);
my $profile_comp = <<'EOS';
local -a profiles
local current
for current in "${(@f)$($words[1] --profile=help)}"; do
current=${current//\*/\\\*}
current=${current//\:/\\\:}
current=${current//\[/\\\[}
current=${current//\]/\\\]}
if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
if [[ -n $match[2] ]]; then
current="$match[1][$match[2]]"
else
current="$match[1]"
fi
profiles=($profiles $current)
fi
done
if [[ $state == show-profile ]]; then
# For --show-profile, only one allowed
if (( ${#profiles} > 0 )); then
_values 'profile' $profiles && rc=0
fi
else
# For --profile, multiple allowed
profiles=($profiles 'help[list profiles]')
_values -s , 'profile(s)' $profiles && rc=0
fi
EOS
chomp $profile_comp;
$protos_str .= qq{$_ } foreach (@protos);
chomp $protos_str;
my $tmpl = <<"EOS";
#compdef mpv
@@ -82,37 +53,39 @@ $opts_str
case \$state in
ao)
_values -s , 'audio outputs' \\
local -a values
values=(
$ao_str
&& rc=0
)
_describe -t values 'audio outputs' values && rc=0
;;
vo)
_values -s , 'video outputs' \\
local -a values
values=(
$vo_str
&& rc=0
)
_describe -t values 'video outputs' values && rc=0
;;
af)
_values -s , 'audio filters' \\
local -a values
values=(
$af_str
&& rc=0
)
_describe -t values 'audio filters' values && rc=0
;;
vf)
_values -s , 'video filters' \\
local -a values
values=(
$vf_str
&& rc=0
;;
)
profile|show-profile)
$profile_comp
;;
files)
compset -P '*,'
compset -S ',*'
_files -r ',/ \\t\\n\\-' && rc=0
_describe -t values 'video filters' values && rc=0
;;
mfiles)
@@ -120,7 +93,7 @@ $profile_comp
_tags files urls
while _tags; do
_requested files expl 'media file' _files -g \\
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
if _requested urls; then
while _next_label urls expl URL; do
_urls "\$expl[@]" && rc=0
@@ -137,76 +110,11 @@ EOS
print $tmpl;
sub parse_main_opts {
my ($cmd, $regex) = @_;
my @list;
my @lines = call_mpv($cmd);
foreach my $line (@lines) {
my ($name, $desc) = ($line =~ /^$regex/) or next;
next if ($desc eq 'removed' || $desc eq 'alias');
if ($desc =~ /^Flag/) {
push @list, $name;
$name =~ /^--(.*)/;
if ($1 !~ /^(\{|\}|v|list-options|really-quiet|no-.*)$/) {
push @list, "--no-$1";
}
} elsif ($desc =~ /^Print/) {
push @list, $name;
} else {
# Option takes argument
my $entry = $name;
$desc =~ s/\:/\\:/g;
$entry .= "=-:$desc:";
if ($desc =~ /^Choices\\: ([^(]*)/) {
my $choices = $1;
$choices =~ s/ +$//; # strip trailing space
$entry .= "($choices)";
# If "no" is one of the choices, it can also be
# negated like a flag (--no-whatever).
if ($choices =~ /\bno\b/) {
$name =~ s/^--/--no-/;
push @list, $name;
}
} elsif ($line =~ /\[file\]/) {
$entry .= '->files';
} elsif ($name =~ /^--(ao|vo|af|vf|profile|show-profile)$/) {
$entry .= "->$1";
}
push @list, $entry;
}
}
# Sort longest first, because zsh won't complete an option listed
# after one that's a prefix of it.
@list = sort {
$a =~ /([^=]*)/; my $ma = $1;
$b =~ /([^=]*)/; my $mb = $1;
length($mb) <=> length($ma)
} @list;
return @list;
}
sub parse_opts {
my ($cmd, $regex) = @_;
my ($cmd, $regex, $parsing_main_options) = @_;
my @list;
my @lines = call_mpv($cmd);
my @lines = split /\n/, `$cmd`;
foreach my $line (@lines) {
if ($line !~ /^$regex/) {
@@ -215,25 +123,37 @@ sub parse_opts {
my $entry = $1;
if ($parsing_main_options) {
$entry .= '=-';
}
if (defined $2) {
my $desc = $2;
$desc =~ s/\:/\\:/g;
$entry .= "[$desc]";
$entry .= ':' . $desc;
}
if ($parsing_main_options) {
$entry .= ':';
$entry .= '->ao' if ($1 eq '--ao');
$entry .= '->vo' if ($1 eq '--vo');
$entry .= '->af' if ($1 eq '--af');
$entry .= '->vf' if ($1 eq '--vf');
}
push @list, $entry
}
if ($parsing_main_options) {
@list = sort {
$a =~ /(.*?)\:/; my $ma = $1;
$b =~ /(.*?)\:/; my $mb = $1;
length($mb) <=> length($ma)
} @list;
}
return @list;
}
sub call_mpv {
my ($cmd) = @_;
my $output = `"$mpv" --no-config $cmd`;
if ($? == -1) {
die "Could not run mpv: $!";
} elsif ((my $exit_code = $? >> 8) != 0) {
die "mpv returned $exit_code with output:\n$output";
}
return split /\n/, $output;
}
+1 -1
View File
@@ -1 +1 @@
0.11.0
0.5.4
+44 -230
View File
@@ -20,13 +20,10 @@
#include <stdlib.h>
#include <assert.h>
#include <libavutil/buffer.h>
#include <libavutil/frame.h>
#include <libavutil/version.h>
#include <libavutil/mem.h>
#include "talloc.h"
#include "common/common.h"
#include "fmt-conversion.h"
#include "audio.h"
static void update_redundant_info(struct mp_audio *mpa)
@@ -34,7 +31,7 @@ static void update_redundant_info(struct mp_audio *mpa)
assert(mp_chmap_is_empty(&mpa->channels) ||
mp_chmap_is_valid(&mpa->channels));
mpa->nch = mpa->channels.num;
mpa->bps = af_fmt_to_bytes(mpa->format);
mpa->bps = af_fmt2bps(mpa->format);
if (af_fmt_is_planar(mpa->format)) {
mpa->spf = 1;
mpa->num_planes = mpa->nch;
@@ -58,6 +55,13 @@ void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
update_redundant_info(mpa);
}
// Use old MPlayer/ALSA channel layout.
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
{
mp_chmap_from_channels_alsa(&mpa->channels, num_channels);
update_redundant_info(mpa);
}
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
{
mpa->channels = *chmap;
@@ -84,16 +88,18 @@ bool mp_audio_config_valid(const struct mp_audio *mpa)
&& mpa->rate >= 1 && mpa->rate < 10000000;
}
char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa)
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format)
{
char ch[128];
mp_chmap_to_str_buf(ch, sizeof(ch), &mpa->channels);
char *hr_ch = mp_chmap_to_str_hr(&mpa->channels);
if (strcmp(hr_ch, ch) != 0)
mp_snprintf_cat(ch, sizeof(ch), " (%s)", hr_ch);
snprintf(buf, buf_sz, "%dHz %s %dch %s", mpa->rate,
ch, mpa->channels.num, af_fmt_to_str(mpa->format));
return buf;
char *chstr = mp_chmap_to_str(chmap);
char *res = talloc_asprintf(NULL, "%dHz %s %dch %s", srate, chstr,
chmap->num, af_fmt_to_str(format));
talloc_free(chstr);
return res;
}
char *mp_audio_config_to_str(struct mp_audio *mpa)
{
return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format);
}
void mp_audio_force_interleaved_format(struct mp_audio *mpa)
@@ -110,27 +116,19 @@ int mp_audio_psize(struct mp_audio *mpa)
void mp_audio_set_null_data(struct mp_audio *mpa)
{
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
for (int n = 0; n < MP_NUM_CHANNELS; n++)
mpa->planes[n] = NULL;
mpa->allocated[n] = NULL;
}
mpa->samples = 0;
}
static int get_plane_size(const struct mp_audio *mpa, int samples)
{
if (samples < 0 || !mp_audio_config_valid(mpa))
return -1;
if (samples >= INT_MAX / mpa->sstride)
return -1;
return MPMAX(samples * mpa->sstride, 1);
}
static void mp_audio_destructor(void *ptr)
{
struct mp_audio *mpa = ptr;
for (int n = 0; n < MP_NUM_CHANNELS; n++)
av_buffer_unref(&mpa->allocated[n]);
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
// Note: don't free if not allocated by mp_audio_realloc
if (mpa->allocated[n])
av_free(mpa->planes[n]);
}
}
/* Reallocate the data stored in mpa->planes[n] so that enough samples are
@@ -149,19 +147,27 @@ static void mp_audio_destructor(void *ptr)
*/
void mp_audio_realloc(struct mp_audio *mpa, int samples)
{
int size = get_plane_size(mpa, samples);
if (size < 0)
abort(); // oom or invalid parameters
assert(samples >= 0);
if (samples >= INT_MAX / mpa->sstride)
abort(); // oom
int size = MPMAX(samples * mpa->sstride, 1);
for (int n = 0; n < mpa->num_planes; n++) {
if (!mpa->allocated[n] || size != mpa->allocated[n]->size) {
if (av_buffer_realloc(&mpa->allocated[n], size) < 0)
abort(); // OOM
if (size != mpa->allocated[n]) {
// Note: av_realloc() can't be used (see libavutil doxygen)
void *new = av_malloc(size);
if (!new)
abort();
if (mpa->allocated[n])
memcpy(new, mpa->planes[n], MPMIN(mpa->allocated[n], size));
av_free(mpa->planes[n]);
mpa->planes[n] = new;
mpa->allocated[n] = size;
}
mpa->planes[n] = mpa->allocated[n]->data;
}
for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) {
av_buffer_unref(&mpa->allocated[n]);
av_free(mpa->planes[n]);
mpa->planes[n] = NULL;
mpa->allocated[n] = 0;
}
talloc_set_destructor(mpa, mp_audio_destructor);
}
@@ -188,18 +194,8 @@ int mp_audio_get_allocated_size(struct mp_audio *mpa)
{
int size = 0;
for (int n = 0; n < mpa->num_planes; n++) {
for (int i = 0; i < MP_NUM_CHANNELS && mpa->allocated[i]; i++) {
uint8_t *start = mpa->allocated[i]->data;
uint8_t *end = start + mpa->allocated[i]->size;
uint8_t *plane = mpa->planes[n];
if (plane >= start && plane < end) {
int s = MPMIN((end - plane) / mpa->sstride, INT_MAX);
size = n == 0 ? s : MPMIN(size, s);
goto next;
}
}
return 0; // plane is not covered by any buffer
next: ;
int s = mpa->allocated[n] / mpa->sstride;
size = n == 0 ? s : MPMIN(size, s);
}
return size;
}
@@ -234,14 +230,6 @@ void mp_audio_copy(struct mp_audio *dst, int dst_offset,
}
}
// Copy fields that describe characteristics of the audio frame, but which are
// not part of the core format (format/channels/rate), and not part of the
// data (samples).
void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src)
{
// nothing yet
}
// Set data to the audio after the given number of samples (i.e. slice it).
void mp_audio_skip_samples(struct mp_audio *data, int samples)
{
@@ -252,177 +240,3 @@ void mp_audio_skip_samples(struct mp_audio *data, int samples)
data->samples -= samples;
}
// Return false if the frame data is shared, true otherwise.
// Will return true for non-refcounted frames.
bool mp_audio_is_writeable(struct mp_audio *data)
{
bool ok = true;
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
if (data->allocated[n])
ok &= av_buffer_is_writable(data->allocated[n]);
}
return ok;
}
static void mp_audio_steal_data(struct mp_audio *dst, struct mp_audio *src)
{
talloc_set_destructor(dst, mp_audio_destructor);
mp_audio_destructor(dst);
*dst = *src;
talloc_set_destructor(src, NULL);
talloc_free(src);
}
// Make sure the frame owns the audio data, and if not, copy the data.
// Return negative value on failure (which means it can't be made writeable).
// Non-refcounted frames are always considered writeable.
int mp_audio_make_writeable(struct mp_audio *data)
{
if (!mp_audio_is_writeable(data)) {
struct mp_audio *new = talloc(NULL, struct mp_audio);
*new = *data;
mp_audio_set_null_data(new); // use format only
mp_audio_realloc(new, data->samples);
new->samples = data->samples;
mp_audio_copy(new, 0, data, 0, data->samples);
mp_audio_steal_data(data, new);
}
return 0;
}
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe)
{
AVFrame *tmp = NULL;
struct mp_audio *new = talloc_zero(NULL, struct mp_audio);
talloc_set_destructor(new, mp_audio_destructor);
mp_audio_set_format(new, af_from_avformat(avframe->format));
struct mp_chmap lavc_chmap;
mp_chmap_from_lavc(&lavc_chmap, avframe->channel_layout);
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being special again
if (lavc_chmap.num != avframe->channels)
mp_chmap_from_channels(&lavc_chmap, avframe->channels);
#endif
new->rate = avframe->sample_rate;
mp_audio_set_channels(new, &lavc_chmap);
// Force refcounted frame.
if (!avframe->buf[0]) {
tmp = av_frame_alloc();
if (!tmp)
goto fail;
if (av_frame_ref(tmp, avframe) < 0)
goto fail;
avframe = tmp;
}
// If we can't handle the format (e.g. too many channels), bail out.
if (!mp_audio_config_valid(new) || avframe->nb_extended_buf)
goto fail;
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++) {
if (!avframe->buf[n])
break;
if (n >= MP_NUM_CHANNELS)
goto fail;
new->allocated[n] = av_buffer_ref(avframe->buf[n]);
if (!new->allocated[n])
goto fail;
}
for (int n = 0; n < new->num_planes; n++)
new->planes[n] = avframe->data[n];
new->samples = avframe->nb_samples;
return new;
fail:
talloc_free(new);
av_frame_free(&tmp);
return NULL;
}
struct mp_audio_pool {
AVBufferPool *avpool;
int element_size;
};
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent)
{
return talloc_zero(ta_parent, struct mp_audio_pool);
}
static void mp_audio_pool_destructor(void *p)
{
struct mp_audio_pool *pool = p;
av_buffer_pool_uninit(&pool->avpool);
}
// Allocate data using the given format and number of samples.
// Returns NULL on error.
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
const struct mp_audio *fmt, int samples)
{
int size = get_plane_size(fmt, samples);
if (size < 0)
return NULL;
if (!pool->avpool || size > pool->element_size) {
size_t alloc = ta_calc_prealloc_elems(size);
if (alloc >= INT_MAX)
return NULL;
av_buffer_pool_uninit(&pool->avpool);
pool->element_size = alloc;
pool->avpool = av_buffer_pool_init(pool->element_size, NULL);
if (!pool->avpool)
return NULL;
talloc_set_destructor(pool, mp_audio_pool_destructor);
}
struct mp_audio *new = talloc_ptrtype(NULL, new);
talloc_set_destructor(new, mp_audio_destructor);
*new = *fmt;
mp_audio_set_null_data(new);
new->samples = samples;
for (int n = 0; n < new->num_planes; n++) {
new->allocated[n] = av_buffer_pool_get(pool->avpool);
if (!new->allocated[n]) {
talloc_free(new);
return NULL;
}
new->planes[n] = new->allocated[n]->data;
}
return new;
}
// Return a copy of the given frame.
// Returns NULL on error.
struct mp_audio *mp_audio_pool_new_copy(struct mp_audio_pool *pool,
struct mp_audio *frame)
{
struct mp_audio *new = mp_audio_pool_get(pool, frame, frame->samples);
if (new) {
mp_audio_copy(new, 0, frame, 0, new->samples);
mp_audio_copy_attributes(new, frame);
}
return new;
}
// Exactly like mp_audio_make_writeable(), but get the data from the pool.
int mp_audio_pool_make_writeable(struct mp_audio_pool *pool,
struct mp_audio *data)
{
if (mp_audio_is_writeable(data))
return 0;
struct mp_audio *new = mp_audio_pool_get(pool, data, data->samples);
if (!new)
return -1;
mp_audio_copy(new, 0, data, 0, data->samples);
mp_audio_copy_attributes(new, data);
mp_audio_steal_data(data, new);
return 0;
}
+6 -24
View File
@@ -35,24 +35,22 @@ struct mp_audio {
int nch; // number of channels (redundant with chmap)
int spf; // sub-samples per sample on each plane
int num_planes; // number of planes
int bps; // size of sub-samples (af_fmt_to_bytes(format))
int bps; // size of sub-samples (af_fmt2bps(format))
// --- private
// These do not necessarily map directly to planes[]. They can have
// different order or count. There shouldn't be more buffers than planes.
// If allocated[n] is NULL, allocated[n+1] must also be NULL.
struct AVBufferRef *allocated[MP_NUM_CHANNELS];
// private
int allocated[MP_NUM_CHANNELS]; // use mp_audio_get_allocated_size()
};
void mp_audio_set_format(struct mp_audio *mpa, int format);
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels);
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels);
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap);
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src);
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b);
bool mp_audio_config_valid(const struct mp_audio *mpa);
char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa);
#define mp_audio_config_to_str(m) mp_audio_config_to_str_buf((char[64]){0}, 64, (m))
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format);
char *mp_audio_config_to_str(struct mp_audio *mpa);
void mp_audio_force_interleaved_format(struct mp_audio *mpa);
@@ -68,22 +66,6 @@ void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length);
void mp_audio_copy(struct mp_audio *dst, int dst_offset,
struct mp_audio *src, int src_offset, int length);
void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src);
void mp_audio_skip_samples(struct mp_audio *data, int samples);
bool mp_audio_is_writeable(struct mp_audio *data);
int mp_audio_make_writeable(struct mp_audio *data);
struct AVFrame;
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe);
struct mp_audio_pool;
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
const struct mp_audio *fmt, int samples);
struct mp_audio *mp_audio_pool_new_copy(struct mp_audio_pool *pool,
struct mp_audio *frame);
int mp_audio_pool_make_writeable(struct mp_audio_pool *pool,
struct mp_audio *frame);
#endif
+53 -109
View File
@@ -18,9 +18,6 @@
#include <stdlib.h>
#include <assert.h>
#include <libavutil/common.h>
#include "common/common.h"
#include "common/msg.h"
#include "chmap.h"
@@ -52,7 +49,6 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
[MP_SPEAKER_ID_SDL] = {"sdl", "surround direct left"},
[MP_SPEAKER_ID_SDR] = {"sdr", "surround direct right"},
[MP_SPEAKER_ID_LFE2] = {"lfe2", "low frequency 2"},
[MP_SPEAKER_ID_NA] = {"na", "not available"},
};
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
@@ -60,9 +56,7 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
static const char *const std_layout_names[][2] = {
{"empty", ""}, // not in lavc
{"mono", "fc"},
{"1.0", "fc"}, // not in lavc
{"stereo", "fl-fr"},
{"2.0", "fl-fr"}, // not in lavc
{"2.1", "fl-fr-lfe"},
{"3.0", "fl-fr-fc"},
{"3.0(back)", "fl-fr-bc"},
@@ -70,7 +64,6 @@ static const char *const std_layout_names[][2] = {
{"quad", "fl-fr-bl-br"},
{"quad(side)", "fl-fr-sl-sr"},
{"3.1", "fl-fr-fc-lfe"},
{"3.1(back)", "fl-fr-lfe-bc"}, // not in lavc
{"5.0", "fl-fr-fc-bl-br"},
{"5.0(alsa)", "fl-fr-bl-br-fc"}, // not in lavc
{"5.0(side)", "fl-fr-fc-sl-sr"},
@@ -84,23 +77,19 @@ static const char *const std_layout_names[][2] = {
{"hexagonal", "fl-fr-fc-bl-br-bc"},
{"6.1", "fl-fr-fc-lfe-bc-sl-sr"},
{"6.1(back)", "fl-fr-fc-lfe-bl-br-bc"}, // lavc calls this "6.1" too
{"6.1(top)", "fl-fr-fc-lfe-bl-br-tc"}, // not in lavc
{"6.1(front)", "fl-fr-lfe-flc-frc-sl-sr"},
{"7.0", "fl-fr-fc-bl-br-sl-sr"},
{"7.0(front)", "fl-fr-fc-flc-frc-sl-sr"},
{"7.0(rear)", "fl-fr-fc-bl-br-sdl-sdr"}, // not in lavc
{"7.1", "fl-fr-fc-lfe-bl-br-sl-sr"},
{"7.1(alsa)", "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
{"7.1(wide)", "fl-fr-fc-lfe-bl-br-flc-frc"},
{"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
{"7.1(rear)", "fl-fr-fc-lfe-bl-br-sdl-sdr"},
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
{"downmix", "dl-dr"},
{"auto", ""}, // not in lavc
{0}
};
static const struct mp_chmap default_layouts[] = {
static const struct mp_chmap default_layouts[MP_NUM_CHANNELS + 1] = {
{0}, // empty
MP_CHMAP_INIT_MONO, // mono
MP_CHMAP2(FL, FR), // stereo
@@ -133,8 +122,7 @@ bool mp_chmap_is_valid(const struct mp_chmap *src)
int sp = src->speaker[n];
if (sp >= MP_SPEAKER_ID_COUNT || mapped[sp])
return false;
if (sp != MP_SPEAKER_ID_NA)
mapped[sp] = true;
mapped[sp] = true;
}
return src->num > 0;
}
@@ -149,10 +137,12 @@ bool mp_chmap_is_empty(const struct mp_chmap *src)
bool mp_chmap_is_unknown(const struct mp_chmap *src)
{
for (int n = 0; n < src->num; n++) {
if (src->speaker[n] != MP_SPEAKER_ID_NA)
return false;
int speaker = src->speaker[n];
if (speaker >= MP_SPEAKER_ID_UNKNOWN0 &&
speaker <= MP_SPEAKER_ID_UNKNOWN_LAST)
return true;
}
return mp_chmap_is_valid(src);
return false;
}
// Note: empty channel maps compare as equal. Invalid ones can equal too.
@@ -176,6 +166,15 @@ bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *
return mp_chmap_equals(&t1, &t2);
}
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b)
{
if (mp_chmap_equals(a, b))
return true;
if (a->num == b->num && (mp_chmap_is_unknown(a) || mp_chmap_is_unknown(b)))
return true;
return false;
}
bool mp_chmap_is_stereo(const struct mp_chmap *src)
{
static const struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
@@ -195,36 +194,16 @@ void mp_chmap_reorder_norm(struct mp_chmap *map)
qsort(arr, map->num, 1, comp_uint8);
}
// Remove silent (NA) channels, if any.
void mp_chmap_remove_na(struct mp_chmap *map)
{
struct mp_chmap new = {0};
for (int n = 0; n < map->num; n++) {
int sp = map->speaker[n];
if (sp != MP_SPEAKER_ID_NA)
new.speaker[new.num++] = map->speaker[n];
}
*map = new;
}
// Add silent (NA) channels to map until map->num >= num.
void mp_chmap_fill_na(struct mp_chmap *map, int num)
{
assert(num <= MP_NUM_CHANNELS);
while (map->num < num)
map->speaker[map->num++] = MP_SPEAKER_ID_NA;
}
// Set *dst to a standard layout with the given number of channels.
// If the number of channels is invalid, an invalid map is set, and
// mp_chmap_is_valid(dst) will return false.
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels)
{
*dst = (struct mp_chmap) {0};
if (num_channels >= 0 && num_channels < MP_ARRAY_SIZE(default_layouts))
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
*dst = (struct mp_chmap) {0};
} else {
*dst = default_layouts[num_channels];
if (!dst->num)
mp_chmap_set_unknown(dst, num_channels);
}
}
// Try to do what mplayer/mplayer2/mpv did before channel layouts were
@@ -243,8 +222,6 @@ void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels)
// Set *dst to an unknown layout for the given numbers of channels.
// If the number of channels is invalid, an invalid map is set, and
// mp_chmap_is_valid(dst) will return false.
// A mp_chmap with all entries set to NA is treated specially in some
// contexts (watch out for mp_chmap_is_unknown()).
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
{
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
@@ -252,7 +229,7 @@ void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
} else {
dst->num = num_channels;
for (int n = 0; n < dst->num; n++)
dst->speaker[n] = MP_SPEAKER_ID_NA;
dst->speaker[n] = MP_SPEAKER_ID_UNKNOWN0 + n;
}
}
@@ -295,7 +272,6 @@ void mp_chmap_remove_useless_channels(struct mp_chmap *map,
}
// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
// Speakers not representable by ffmpeg/libav are dropped.
// Warning: this ignores the order of the channels, and will return a channel
// mask even if the order is different from libavcodec's.
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
@@ -305,10 +281,8 @@ uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
if (mp_chmap_is_unknown(&t))
mp_chmap_from_channels(&t, t.num);
uint64_t mask = 0;
for (int n = 0; n < t.num; n++) {
if (t.speaker[n] < 64) // ignore MP_SPEAKER_ID_NA etc.
mask |= 1ULL << t.speaker[n];
}
for (int n = 0; n < t.num; n++)
mask |= 1ULL << t.speaker[n];
return mask;
}
@@ -369,77 +343,66 @@ void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
// Get reordering array for from->to reordering. from->to must have the same set
// of speakers (i.e. same number and speaker IDs, just different order). Then,
// for each speaker n, src[n] will be set such that:
// to->speaker[n] = from->speaker[src[n]]
// (src[n] gives the source channel for destination channel n)
// If *from and *to don't contain the same set of speakers, then the above
// invariant is not guaranteed. Instead, src[n] can be set to -1 if the channel
// at to->speaker[n] is unmapped.
void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
// for each speaker n, dst[n] will be set such that:
// to->speaker[dst[n]] = from->speaker[n]
// (dst[n] gives the source channel for destination channel n)
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
const struct mp_chmap *to)
{
for (int n = 0; n < MP_NUM_CHANNELS; n++)
src[n] = -1;
assert(from->num == to->num);
if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) {
for (int n = 0; n < to->num; n++)
src[n] = n < from->num ? n : -1;
for (int n = 0; n < from->num; n++)
dst[n] = n;
return;
}
for (int n = 0; n < to->num; n++) {
for (int i = 0; i < from->num; i++) {
if (to->speaker[n] == from->speaker[i]) {
src[n] = i;
// Same set of speakers required
assert(mp_chmap_equals_reordered(from, to));
for (int n = 0; n < from->num; n++) {
int src = from->speaker[n];
dst[n] = -1;
for (int i = 0; i < to->num; i++) {
if (src == to->speaker[i]) {
dst[n] = i;
break;
}
}
assert(dst[n] != -1);
}
for (int n = 0; n < to->num; n++)
assert(src[n] < 0 || (to->speaker[n] == from->speaker[src[n]]));
}
// Return the number of channels only in a.
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b)
{
uint64_t a_mask = mp_chmap_to_lavc_unchecked(a);
uint64_t b_mask = mp_chmap_to_lavc_unchecked(b);
return av_popcount64((a_mask ^ b_mask) & a_mask);
for (int n = 0; n < from->num; n++)
assert(to->speaker[dst[n]] == from->speaker[n]);
}
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
// order, return that, e.g. "3.0" instead of "fl-fr-fc".
// Unassigned but valid speakers get names like "sp28".
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src)
char *mp_chmap_to_str(const struct mp_chmap *src)
{
buf[0] = '\0';
char *res = talloc_strdup(NULL, "");
if (mp_chmap_is_unknown(src)) {
snprintf(buf, buf_size, "unknown%d", src->num);
return buf;
}
if (mp_chmap_is_unknown(src))
return talloc_asprintf_append_buffer(res, "unknown%d", src->num);
for (int n = 0; n < src->num; n++) {
int sp = src->speaker[n];
const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL;
char sp_buf[10];
char buf[10];
if (!s) {
snprintf(sp_buf, sizeof(sp_buf), "sp%d", sp);
s = sp_buf;
snprintf(buf, sizeof(buf), "sp%d", sp);
s = buf;
}
mp_snprintf_cat(buf, buf_size, "%s%s", n > 0 ? "-" : "", s);
res = talloc_asprintf_append_buffer(res, "%s%s", n > 0 ? "-" : "", s);
}
// To standard layout name
for (int n = 0; std_layout_names[n][0]; n++) {
if (strcmp(buf, std_layout_names[n][1]) == 0) {
snprintf(buf, buf_size, "%s", std_layout_names[n][0]);
if (res && strcmp(res, std_layout_names[n][1]) == 0) {
talloc_free(res);
res = talloc_strdup(NULL, std_layout_names[n][0]);
break;
}
}
return buf;
return res;
}
// If src can be parsed as channel map (as produced by mp_chmap_to_str()),
@@ -508,25 +471,6 @@ bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
return true;
}
// Output a human readable "canonical" channel map string. Converting this from
// a string back to a channel map can yield a different map, but the string
// looks nicer. E.g. "fc-fl-fr-na" becomes "3.0".
char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *src)
{
struct mp_chmap map = *src;
mp_chmap_remove_na(&map);
for (int n = 0; std_layout_names[n][0]; n++) {
struct mp_chmap s;
if (mp_chmap_from_str(&s, bstr0(std_layout_names[n][0])) &&
mp_chmap_equals_reordered(&s, &map))
{
map = s;
break;
}
}
return mp_chmap_to_str_buf(buf, buf_size, &map);
}
void mp_chmap_print_help(struct mp_log *log)
{
mp_info(log, "Speakers:\n");
+10 -21
View File
@@ -20,7 +20,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include "misc/bstr.h"
#include "bstr/bstr.h"
#define MP_NUM_CHANNELS 8
@@ -47,7 +47,7 @@ enum mp_speaker_id {
MP_SPEAKER_ID_TBL, // TOP_BACK_LEFT
MP_SPEAKER_ID_TBC, // TOP_BACK_CENTER
MP_SPEAKER_ID_TBR, // TOP_BACK_RIGHT
// Unofficial/libav* extensions
// Inofficial/libav* extensions
MP_SPEAKER_ID_DL = 29, // STEREO_LEFT (stereo downmix special speakers)
MP_SPEAKER_ID_DR, // STEREO_RIGHT
MP_SPEAKER_ID_WL, // WIDE_LEFT
@@ -56,15 +56,12 @@ enum mp_speaker_id {
MP_SPEAKER_ID_SDR, // SURROUND_DIRECT_RIGHT
MP_SPEAKER_ID_LFE2, // LOW_FREQUENCY_2
// Speaker IDs >= 64 are not representable in WAVEFORMATEXTENSIBLE or libav*.
// Special mpv-specific speaker entries reserved for channels which have no
// known meaning.
MP_SPEAKER_ID_UNKNOWN0 = 64,
MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
// "Silent" channels. These are sometimes used to insert padding for
// unused channels. Unlike other speaker types, multiple of these can
// occur in a single mp_chmap.
MP_SPEAKER_ID_NA = 64,
// Including the unassigned IDs in between. This is not a valid ID anymore,
// but is still within uint8_t.
// Including the unassigned IDs in between. This is not a valid ID anymore.
MP_SPEAKER_ID_COUNT,
};
@@ -100,11 +97,10 @@ bool mp_chmap_is_empty(const struct mp_chmap *src);
bool mp_chmap_is_unknown(const struct mp_chmap *src);
bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b);
bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b);
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b);
bool mp_chmap_is_stereo(const struct mp_chmap *src);
void mp_chmap_reorder_norm(struct mp_chmap *map);
void mp_chmap_remove_na(struct mp_chmap *map);
void mp_chmap_fill_na(struct mp_chmap *map, int num);
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels);
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels);
@@ -120,17 +116,10 @@ void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src);
bool mp_chmap_is_lavc(const struct mp_chmap *src);
void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
const struct mp_chmap *to);
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b);
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
#define mp_chmap_to_str(m) mp_chmap_to_str_buf((char[64]){0}, 64, (m))
char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
#define mp_chmap_to_str_hr(m) mp_chmap_to_str_hr_buf((char[128]){0}, 128, (m))
char *mp_chmap_to_str(const struct mp_chmap *src);
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
struct mp_log;
+12 -155
View File
@@ -17,7 +17,6 @@
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include "common/common.h"
#include "chmap_sel.h"
@@ -58,29 +57,6 @@ static bool replace_speakers(struct mp_chmap *map, struct mp_chmap list[2])
return false;
}
// These go strictly from the first to the second entry and always use the
// full layout (possibly reordered and/or padding channels added).
static const struct mp_chmap preferred_remix[][2] = {
// mono can be perfectly played as stereo
{ MP_CHMAP_INIT_MONO, MP_CHMAP_INIT_STEREO },
};
// Conversion from src to dst is explicitly encouraged and should be preferred
// over "mathematical" upmixes or downmixes (which minimize lost channels).
static bool test_preferred_remix(const struct mp_chmap *src,
const struct mp_chmap *dst)
{
struct mp_chmap src_p = *src, dst_p = *dst;
mp_chmap_remove_na(&src_p);
mp_chmap_remove_na(&dst_p);
for (int n = 0; n < MP_ARRAY_SIZE(preferred_remix); n++) {
if (mp_chmap_equals_reordered(&src_p, &preferred_remix[n][0]) &&
mp_chmap_equals_reordered(&dst_p, &preferred_remix[n][1]))
return true;
}
return false;
}
// Allow all channel layouts that can be expressed with mp_chmap.
// (By default, all layouts are rejected.)
void mp_chmap_sel_add_any(struct mp_chmap_sel *s)
@@ -97,7 +73,7 @@ void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s)
// Classic ALSA-based MPlayer layouts.
void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
{
for (int n = 1; n <= MP_NUM_CHANNELS; n++) {
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
struct mp_chmap t;
mp_chmap_from_channels_alsa(&t, n);
if (t.num)
@@ -105,27 +81,20 @@ void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
}
}
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
// Add a channel map that should be allowed.
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
{
if (!mp_chmap_is_valid(map))
return;
if (!s->chmaps)
s->chmaps = s->chmaps_storage;
if (s->num_chmaps == MP_ARRAY_SIZE(s->chmaps_storage)) {
if (!s->tmp)
return;
s->chmaps = talloc_memdup(s->tmp, s->chmaps, sizeof(s->chmaps_storage));
}
if (s->chmaps != s->chmaps_storage)
MP_TARRAY_GROW(s->tmp, s->chmaps, s->num_chmaps);
s->chmaps[s->num_chmaps++] = *map;
assert(s->num_chmaps < ARRAY_LEN(s->chmaps));
if (mp_chmap_is_valid(map))
s->chmaps[s->num_chmaps++] = *map;
}
// Allow all waveext formats in default order.
void mp_chmap_sel_add_waveext_def(struct mp_chmap_sel *s)
{
for (int n = 1; n <= MP_NUM_CHANNELS; n++) {
for (int n = 1; n < MP_NUM_CHANNELS; n++) {
struct mp_chmap map;
mp_chmap_from_channels(&map, n);
mp_chmap_sel_add_map(s, &map);
@@ -199,10 +168,6 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
return true;
}
}
if (mp_chmap_sel_fallback(s, map))
return true;
for (int i = 0; i < MP_ARRAY_SIZE(speaker_replacements); i++) {
struct mp_chmap t = *map;
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
@@ -211,126 +176,18 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
return true;
}
}
// Fallback to mono/stereo as last resort
*map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
if (test_layout(s, map))
return true;
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
if (map->num == 1) {
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
} else if (map->num >= 2) {
*map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
}
if (test_layout(s, map))
return true;
*map = (struct mp_chmap) {0};
return false;
}
// Like mp_chmap_diffn(), but find the minimum difference with all possible
// speaker replacements considered.
static int mp_chmap_diffn_r(const struct mp_chmap *a, const struct mp_chmap *b)
{
int mindiff = INT_MAX;
for (int i = -1; i < (int)MP_ARRAY_SIZE(speaker_replacements); i++) {
struct mp_chmap ar = *a;
if (i >= 0) {
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
if (!replace_speakers(&ar, r))
continue;
}
int d = mp_chmap_diffn(&ar, b);
if (d < mindiff)
mindiff = d;
}
// Special-case: we consider stereo a replacement for mono. (This is not
// true in the other direction. Also, fl-fr is generally not a replacement
// for fc. Thus it's not part of the speaker replacement list.)
struct mp_chmap mono = MP_CHMAP_INIT_MONO;
struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
if (mp_chmap_equals(&mono, b) && mp_chmap_equals(&stereo, a))
mindiff = 0;
return mindiff;
}
// Decide whether we should prefer old or new for the requested layout.
// Return true if new should be used, false if old should be used.
// If old is empty, always return new (initial case).
static bool mp_chmap_is_better(struct mp_chmap *req, struct mp_chmap *old,
struct mp_chmap *new)
{
// Initial case
if (!old->num)
return true;
// Exact pick - this also ensures that the best layout is chosen if the
// layouts are the same, but with different order of channels.
if (mp_chmap_equals(req, old))
return false;
if (mp_chmap_equals(req, new))
return true;
// If there's no exact match, strictly do a preferred conversion.
bool old_pref = test_preferred_remix(req, old);
bool new_pref = test_preferred_remix(req, new);
if (old_pref && !new_pref)
return false;
if (!old_pref && new_pref)
return true;
int old_lost_r = mp_chmap_diffn_r(req, old); // num. channels only in req
int new_lost_r = mp_chmap_diffn_r(req, new);
// Imperfect upmix (no real superset) - minimize lost channels
if (new_lost_r != old_lost_r)
return new_lost_r < old_lost_r;
int old_lost = mp_chmap_diffn(req, old);
int new_lost = mp_chmap_diffn(req, new);
// If the situation is equal with replaced speakers, but one of them loses
// less if no replacements are performed, pick the better one, even if it
// means an upmix. This prefers exact supersets over inexact equivalents.
if (new_lost != old_lost)
return new_lost < old_lost;
struct mp_chmap old_p = *old, new_p = *new;
mp_chmap_remove_na(&old_p);
mp_chmap_remove_na(&new_p);
// Some kind of upmix. If it's perfect, prefer the smaller one. Even if not,
// both have equal loss, so also prefer the smaller one.
// Drop padding channels (NA) for the sake of this check, as the number of
// padding channels isn't really meaningful.
if (new_p.num != old_p.num)
return new_p.num < old_p.num;
// Again, with physical channels (minimizes number of NA channels).
return new->num < old->num;
}
// Determine which channel map to fallback to given a source channel map.
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
{
struct mp_chmap best = {0};
for (int n = 0; n < s->num_chmaps; n++) {
struct mp_chmap e = s->chmaps[n];
if (mp_chmap_is_unknown(&e))
continue;
if (mp_chmap_is_better(map, &best, &e))
best = e;
}
if (best.num) {
*map = best;
return true;
}
return false;
}
// Set map to a default layout with num channels. Used for audio APIs that
// return a channel count as part of format negotiation, but give no
// information about the channel layout.
+1 -6
View File
@@ -26,12 +26,8 @@ struct mp_chmap_sel {
// should be considered opaque
bool allow_any, allow_waveext;
bool speakers[MP_SPEAKER_ID_COUNT];
struct mp_chmap *chmaps;
struct mp_chmap chmaps[20];
int num_chmaps;
struct mp_chmap chmaps_storage[20];
void *tmp; // set to any talloc context to allow more chmaps entries
};
void mp_chmap_sel_add_any(struct mp_chmap_sel *s);
@@ -41,7 +37,6 @@ void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s);
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map);
void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id);
bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map);
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map);
bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
int num);
+6 -5
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_AD_H
@@ -35,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_packet)(struct dec_audio *da, struct mp_audio **out);
int (*decode_packet)(struct dec_audio *da);
};
enum ad_ctrl {
+153 -62
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -24,7 +25,6 @@
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/common.h>
#include <libavutil/intreadwrite.h>
#include "talloc.h"
@@ -37,13 +37,14 @@
#include "ad.h"
#include "audio/fmt-conversion.h"
#include "compat/libav.h"
struct priv {
AVCodecContext *avctx;
AVFrame *avframe;
struct mp_audio frame;
bool force_channel_map;
struct demux_packet *packet;
uint32_t skip_samples;
};
static void uninit(struct dec_audio *da);
@@ -58,7 +59,7 @@ struct ad_lavc_params {
const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 6),
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 2),
OPT_FLAG("downmix", downmix, 0),
OPT_INTRANGE("threads", threads, 0, 1, 16),
OPT_KEYVALUELIST("o", avopts, 0),
@@ -66,12 +67,123 @@ const struct m_sub_options ad_lavc_conf = {
},
.size = sizeof(struct ad_lavc_params),
.defaults = &(const struct ad_lavc_params){
.ac3drc = 0,
.ac3drc = 1.,
.downmix = 1,
.threads = 1,
},
};
struct pcm_map
{
int tag;
const char *codecs[6]; // {any, 1byte, 2bytes, 3bytes, 4bytes, 8bytes}
};
// NOTE: these are needed to make rawaudio with demux_mkv work.
static const struct pcm_map tag_map[] = {
// Microsoft PCM
{0x0, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
{0x1, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
// MS PCM, Extended
{0xfffe, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
// IEEE float
{0x3, {"pcm_f32le", [5] = "pcm_f64le"}},
// 'raw '
{0x20776172, {"pcm_s16be", [1] = "pcm_u8"}},
// 'twos', used by demux_mkv.c internally
{MKTAG('t', 'w', 'o', 's'),
{NULL, "pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"}},
{-1},
};
// For demux_rawaudio.c; needed because ffmpeg doesn't have these sample
// formats natively.
static const struct pcm_map af_map[] = {
{AF_FORMAT_U8, {"pcm_u8"}},
{AF_FORMAT_S8, {"pcm_u8"}},
{AF_FORMAT_U16_LE, {"pcm_u16le"}},
{AF_FORMAT_U16_BE, {"pcm_u16be"}},
{AF_FORMAT_S16_LE, {"pcm_s16le"}},
{AF_FORMAT_S16_BE, {"pcm_s16be"}},
{AF_FORMAT_U24_LE, {"pcm_u24le"}},
{AF_FORMAT_U24_BE, {"pcm_u24be"}},
{AF_FORMAT_S24_LE, {"pcm_s24le"}},
{AF_FORMAT_S24_BE, {"pcm_s24be"}},
{AF_FORMAT_U32_LE, {"pcm_u32le"}},
{AF_FORMAT_U32_BE, {"pcm_u32be"}},
{AF_FORMAT_S32_LE, {"pcm_s32le"}},
{AF_FORMAT_S32_BE, {"pcm_s32be"}},
{AF_FORMAT_FLOAT_LE, {"pcm_f32le"}},
{AF_FORMAT_FLOAT_BE, {"pcm_f32be"}},
{AF_FORMAT_DOUBLE_LE, {"pcm_f64le"}},
{AF_FORMAT_DOUBLE_BE, {"pcm_f64be"}},
{-1},
};
static const char *find_pcm_decoder(const struct pcm_map *map, int format,
int bits_per_sample)
{
int bytes = (bits_per_sample + 7) / 8;
if (bytes == 8)
bytes = 5; // 64 bit entry
for (int n = 0; map[n].tag != -1; n++) {
const struct pcm_map *entry = &map[n];
if (entry->tag == format) {
const char *dec = NULL;
if (bytes >= 1 && bytes <= 5)
dec = entry->codecs[bytes];
if (!dec)
dec = entry->codecs[0];
if (dec)
return dec;
}
}
return NULL;
}
static void set_data_from_avframe(struct dec_audio *da)
{
struct priv *priv = da->priv;
AVCodecContext *lavc_context = priv->avctx;
// Note: invalid parameters are rejected by dec_audio.c
int fmt = lavc_context->sample_fmt;
mp_audio_set_format(&da->decoded, af_from_avformat(fmt));
if (!da->decoded.format)
MP_FATAL(da, "unsupported lavc format %s", av_get_sample_fmt_name(fmt));
da->decoded.rate = lavc_context->sample_rate;
struct mp_chmap lavc_chmap;
mp_chmap_from_lavc(&lavc_chmap, lavc_context->channel_layout);
// No channel layout or layout disagrees with channel count
if (lavc_chmap.num != lavc_context->channels)
mp_chmap_from_channels(&lavc_chmap, lavc_context->channels);
if (priv->force_channel_map) {
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);
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)
{
avctx->channels = wf->nChannels;
avctx->sample_rate = wf->nSamplesPerSec;
avctx->bit_rate = wf->nAvgBytesPerSec * 8;
avctx->block_align = wf->nBlockAlign;
avctx->bits_per_coded_sample = wf->wBitsPerSample;
if (wf->cbSize > 0)
mp_lavc_set_extradata(avctx, wf + 1, wf->cbSize);
}
static int init(struct dec_audio *da, const char *decoder)
{
struct MPOpts *mpopts = da->opts;
@@ -84,7 +196,13 @@ static int init(struct dec_audio *da, const char *decoder)
struct priv *ctx = talloc_zero(NULL, struct priv);
da->priv = ctx;
ctx->force_channel_map = sh_audio->force_channels;
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
decoder = find_pcm_decoder(tag_map, sh->format,
sh_audio->wf->wBitsPerSample);
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
decoder = find_pcm_decoder(af_map, sh->format, 0);
ctx->force_channel_map = true;
}
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
@@ -109,29 +227,27 @@ static int init(struct dec_audio *da, const char *decoder)
av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
AV_OPT_SEARCH_CHILDREN);
#if HAVE_AVFRAME_SKIP_SAMPLES
// Let decoder add AV_FRAME_DATA_SKIP_SAMPLES.
av_opt_set(lavc_context, "flags2", "+skip_manual", AV_OPT_SEARCH_CHILDREN);
#endif
mp_set_avopts(da->log, lavc_context, opts->avopts);
lavc_context->codec_tag = sh->codec_tag;
lavc_context->codec_tag = sh->format;
lavc_context->sample_rate = sh_audio->samplerate;
lavc_context->bit_rate = sh_audio->bitrate;
lavc_context->block_align = sh_audio->block_align;
lavc_context->bits_per_coded_sample = sh_audio->bits_per_coded_sample;
lavc_context->channels = sh_audio->channels.num;
if (!mp_chmap_is_unknown(&sh_audio->channels))
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
// demux_mkv
mp_lavc_set_extradata(lavc_context, sh->extradata, sh->extradata_size);
if (sh_audio->wf)
set_from_wf(lavc_context, sh_audio->wf);
// demux_mkv, demux_mpg
if (sh_audio->codecdata_len && sh_audio->codecdata &&
!lavc_context->extradata) {
mp_lavc_set_extradata(lavc_context, sh_audio->codecdata,
sh_audio->codecdata_len);
}
if (sh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
mp_set_avcodec_threads(da->log, lavc_context, opts->threads);
mp_set_avcodec_threads(lavc_context, opts->threads);
/* open it */
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
@@ -140,6 +256,11 @@ static int init(struct dec_audio *da, const char *decoder)
return 0;
}
if (lavc_context->bit_rate != 0)
da->bitrate = lavc_context->bit_rate;
else if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
da->bitrate = sh_audio->wf->nAvgBytesPerSec * 8;
return 1;
}
@@ -165,19 +286,21 @@ static int control(struct dec_audio *da, int cmd, void *arg)
switch (cmd) {
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
mp_audio_set_null_data(&da->decoded);
talloc_free(ctx->packet);
ctx->packet = NULL;
ctx->skip_samples = 0;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static int decode_packet(struct dec_audio *da, struct mp_audio **out)
static int decode_packet(struct dec_audio *da)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
mp_audio_set_null_data(&da->decoded);
struct demux_packet *mpkt = priv->packet;
if (!mpkt) {
if (demux_read_packet_async(da->header, &mpkt) == 0)
@@ -224,55 +347,23 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
if (!got_frame)
return mpkt ? AD_OK : AD_EOF;
set_data_from_avframe(da);
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
if (out_pts != MP_NOPTS_VALUE) {
da->pts = out_pts;
da->pts_offset = 0;
}
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
return AD_ERR;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
if (priv->force_channel_map) {
struct sh_audio *sh_audio = da->header->audio;
if (lavc_chmap.num == sh_audio->channels.num)
lavc_chmap = sh_audio->channels;
}
mp_audio_set_channels(mpframe, &lavc_chmap);
#if HAVE_AVFRAME_SKIP_SAMPLES
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
char *d = sd->data;
priv->skip_samples += AV_RL32(d + 0);
uint32_t pad = AV_RL32(d + 4);
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
if (skip) {
mp_audio_skip_samples(mpframe, skip);
da->pts_offset += skip;
priv->skip_samples -= skip;
}
if (pad <= mpframe->samples)
mpframe->samples -= pad;
}
#endif
*out = mpframe;
av_frame_unref(priv->avframe);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, mpframe->samples);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, da->decoded.samples);
return 0;
}
static void add_decoders(struct mp_decoder_list *list)
{
mp_add_lavc_decoders(list, AVMEDIA_TYPE_AUDIO);
mp_add_decoder(list, "lavc", "pcm", "pcm", "Raw PCM");
mp_add_decoder(list, "lavc", "mp-pcm", "mp-pcm", "Raw PCM");
}
const struct ad_functions ad_lavc = {
+303
View File
@@ -0,0 +1,303 @@
/*
* MPEG 1.0/2.0/2.5 audio layer I, II, III decoding with libmpg123
*
* Copyright (C) 2010-2013 Thomas Orgis <thomas@orgis.org>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#include "ad.h"
#include "common/msg.h"
#include <mpg123.h>
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION < 33)
#error "This should not happen"
#endif
struct ad_mpg123_context {
mpg123_handle *handle;
int sample_size;
/* Running mean for bit rate, stream length estimation. */
float mean_rate;
unsigned int mean_count;
/* Time delay for updates. */
short delay;
/* If the stream is actually VBR. */
char vbr;
};
static void uninit(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
mpg123_close(con->handle);
mpg123_delete(con->handle);
mpg123_exit();
}
/* This initializes libmpg123 and prepares the handle, including funky
* parameters. */
static int init(struct dec_audio *da, const char *decoder)
{
int err;
struct ad_mpg123_context *con;
/* Assumption: You always call preinit + init + uninit, on every file.
* But you stop at preinit in case it fails.
* If that is not true, one must ensure not to call mpg123_init / exit
* twice in a row. */
if (mpg123_init() != MPG123_OK)
return 0;
da->priv = talloc_zero(NULL, struct ad_mpg123_context);
con = da->priv;
/* Auto-choice of optimized decoder (first argument NULL). */
con->handle = mpg123_new(NULL, &err);
if (!con->handle)
goto bad_end;
/* Basic settings.
* Don't spill messages, enable better resync with non-seekable streams.
* Give both flags individually without error checking to keep going with
* old libmpg123. Generally, it is not fatal if the flags are not
* honored */
mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0);
/* Do not bail out on malformed streams at all.
* MPlayer does not handle a decoder throwing the towel on crappy input. */
mpg123_param(con->handle, MPG123_RESYNC_LIMIT, -1, 0.0);
/* Open decisions: Configure libmpg123 to force encoding (or stay open about
* library builds that support only float or int32 output), (de)configure
* gapless decoding (won't work with seeking in MPlayer, though).
* Don't forget to eventually enable ReplayGain/RVA support, too.
* Let's try to run with the default for now. */
/* That would produce floating point output.
* You can get 32 and 24 bit ints, even 8 bit via format matrix.
* If wanting a specific encoding here, configure format matrix and
* make sure it is in set_format(). */
/* mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.); */
/* Example for RVA choice (available since libmpg123 1.0.0):
mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
/* Prevent funky automatic resampling.
* This way, we can be sure that one frame will never produce
* more than 1152 stereo samples.
* Background:
* Going to decode directly to the output buffer. It is important to have
* MPG123_AUTO_RESAMPLE disabled for the buffer size being an all-time
* limit.
* We need at least 1152 samples. dec_audio.c normally guarantees this. */
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));
}
uninit(da);
return 0;
}
static int mpg123_format_to_af(int mpg123_encoding)
{
/* Without external force, mpg123 will always choose signed encoding,
* and non-16-bit only on builds that don't support it.
* Be reminded that it doesn't matter to the MPEG file what encoding
* is produced from it. */
switch (mpg123_encoding) {
case MPG123_ENC_SIGNED_8: return AF_FORMAT_S8;
case MPG123_ENC_SIGNED_16: return AF_FORMAT_S16;
case MPG123_ENC_SIGNED_32: return AF_FORMAT_S32;
case MPG123_ENC_FLOAT_32: return AF_FORMAT_FLOAT;
}
return 0;
}
/* Compute bitrate from frame size. */
static int compute_bitrate(struct mpg123_frameinfo *i)
{
static const int samples_per_frame[4][4] = {
{-1, 384, 1152, 1152}, /* MPEG 1 */
{-1, 384, 1152, 576}, /* MPEG 2 */
{-1, 384, 1152, 576}, /* MPEG 2.5 */
{-1, -1, -1, -1}, /* Unknown */
};
return (int) ((i->framesize + 4) * 8 * i->rate /
samples_per_frame[i->version][i->layer] + 0.5);
}
/* Update mean bitrate. This could be dropped if accurate time display
* on audio file playback is not desired. */
static void update_info(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
struct mpg123_frameinfo finfo;
if (mpg123_info(con->handle, &finfo) != MPG123_OK)
return;
/* finfo.bitrate is expressed in kilobits */
const int bitrate = finfo.bitrate * 1000;
if (finfo.vbr != MPG123_CBR) {
if (--con->delay < 1) {
if (++con->mean_count > ((unsigned int) -1) / 2)
con->mean_count = ((unsigned int) -1) / 4;
/* Might not be numerically optimal, but works fine enough. */
con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
bitrate) / con->mean_count;
da->bitrate = (int) (con->mean_rate + 0.5);
con->delay = 10;
}
} else {
da->bitrate = bitrate ? bitrate : compute_bitrate(&finfo);
con->delay = 1;
con->mean_rate = 0.;
con->mean_count = 0;
}
}
/* 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;
int ret;
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;
}
/* Have to use mpg123_feed() to avoid decoding here. */
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
talloc_free(pkt);
if (ret != MPG123_OK)
goto mpg123_fail;
unsigned char *audio = NULL;
size_t bytes = 0;
ret = mpg123_decode_frame(con->handle, NULL, &audio, &bytes);
if (ret == MPG123_NEED_MORE)
return 0;
if (ret != MPG123_OK && ret != MPG123_DONE && ret != MPG123_NEW_FORMAT)
goto mpg123_fail;
ret = set_format(da);
if (ret != MPG123_OK)
goto mpg123_fail;
if (con->sample_size < 1) {
MP_ERR(da, "no sample size\n");
return AD_ERR;
}
int got_samples = bytes / con->sample_size;
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 AD_ERR;
}
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct ad_mpg123_context *con = da->priv;
switch (cmd) {
case ADCTRL_RESET:
mp_audio_set_null_data(&da->decoded);
mpg123_close(con->handle);
if (mpg123_open_feed(con->handle) != MPG123_OK) {
MP_ERR(da, "mpg123 failed to reopen stream: %s\n",
mpg123_strerror(con->handle));
return CONTROL_FALSE;
}
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static void add_decoders(struct mp_decoder_list *list)
{
mp_add_decoder(list, "mpg123", "mp3", "mp3",
"High-performance decoder using libmpg123");
}
const struct ad_functions ad_mpg123 = {
.name = "mpg123",
.add_decoders = add_decoders,
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
};
+45 -109
View File
@@ -1,20 +1,21 @@
/*
* This file is part of MPlayer.
*
* Copyright (C) 2012 Naoya OYAMA
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
@@ -34,13 +35,11 @@
struct spdifContext {
struct mp_log *log;
enum AVCodecID codec_id;
AVFormatContext *lavf_ctx;
int iec61937_packet_size;
int out_buffer_len;
uint8_t out_buffer[OUTBUF_SIZE];
bool need_close;
bool use_dts_hd;
struct mp_audio fmt;
};
static int write_packet(void *p, uint8_t *buf, int buf_size)
@@ -78,78 +77,17 @@ static int init(struct dec_audio *da, const char *decoder)
struct spdifContext *spdif_ctx = talloc_zero(NULL, struct spdifContext);
da->priv = spdif_ctx;
spdif_ctx->log = da->log;
spdif_ctx->use_dts_hd = da->opts->dtshd;
if (strcmp(decoder, "dts-hd") == 0) {
decoder = "dts";
spdif_ctx->use_dts_hd = true;
}
spdif_ctx->codec_id = mp_codec_to_av_codec_id(decoder);
return spdif_ctx->codec_id != AV_CODEC_ID_NONE;
}
static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt)
{
struct spdifContext *spdif_ctx = da->priv;
int profile = FF_PROFILE_UNKNOWN;
AVCodecContext *ctx = NULL;
AVFrame *frame = NULL;
AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id);
if (!codec)
goto done;
frame = av_frame_alloc();
if (!frame)
goto done;
ctx = avcodec_alloc_context3(codec);
if (!ctx)
goto done;
if (avcodec_open2(ctx, codec, NULL) < 0) {
av_free(ctx); // don't attempt to avcodec_close() an unopened ctx
ctx = NULL;
goto done;
}
int got_frame = 0;
if (avcodec_decode_audio4(ctx, frame, &got_frame, pkt) < 1 || !got_frame)
goto done;
profile = ctx->profile;
done:
av_frame_free(&frame);
if (ctx)
avcodec_close(ctx);
avcodec_free_context(&ctx);
if (profile == FF_PROFILE_UNKNOWN)
MP_WARN(da, "Failed to parse codec profile.\n");
return profile;
}
static int init_filter(struct dec_audio *da, AVPacket *pkt)
{
struct spdifContext *spdif_ctx = da->priv;
int profile = FF_PROFILE_UNKNOWN;
if (spdif_ctx->codec_id == AV_CODEC_ID_DTS)
profile = determine_codec_profile(da, pkt);
AVFormatContext *lavf_ctx = avformat_alloc_context();
if (!lavf_ctx)
goto fail;
spdif_ctx->lavf_ctx = lavf_ctx;
lavf_ctx->oformat = av_guess_format("spdif", NULL, NULL);
if (!lavf_ctx->oformat)
goto fail;
spdif_ctx->lavf_ctx = lavf_ctx;
void *buffer = av_mallocz(OUTBUF_SIZE);
if (!buffer)
abort();
@@ -169,60 +107,64 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
if (!stream)
goto fail;
stream->codec->codec_id = spdif_ctx->codec_id;
stream->codec->codec_id = mp_codec_to_av_codec_id(decoder);
AVDictionary *format_opts = NULL;
int num_channels = 0;
int sample_format = 0;
int samplerate = 0;
switch (spdif_ctx->codec_id) {
switch (stream->codec->codec_id) {
case AV_CODEC_ID_AAC:
sample_format = AF_FORMAT_S_AAC;
spdif_ctx->iec61937_packet_size = 16384;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_AC3:
sample_format = AF_FORMAT_S_AC3;
spdif_ctx->iec61937_packet_size = 6144;
sample_format = AF_FORMAT_AC3_LE;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_DTS: {
bool is_hd = profile == FF_PROFILE_DTS_HD_HRA ||
profile == FF_PROFILE_DTS_HD_MA;
if (spdif_ctx->use_dts_hd && is_hd) {
case AV_CODEC_ID_DTS:
if (da->opts->dtshd) {
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
sample_format = AF_FORMAT_S_DTSHD;
samplerate = 192000;
num_channels = 2*4;
spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 2*4;
} else {
sample_format = AF_FORMAT_S_DTS;
samplerate = 48000;
num_channels = 2;
spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_AC3_LE;
samplerate = 48000;
num_channels = 2;
}
break;
}
case AV_CODEC_ID_EAC3:
sample_format = AF_FORMAT_S_EAC3;
spdif_ctx->iec61937_packet_size = 24576;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 2;
break;
case AV_CODEC_ID_MP3:
sample_format = AF_FORMAT_S_MP3;
spdif_ctx->iec61937_packet_size = 4608;
sample_format = AF_FORMAT_MPEG2;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_TRUEHD:
sample_format = AF_FORMAT_S_TRUEHD;
spdif_ctx->iec61937_packet_size = 61440;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 8;
break;
default:
abort();
}
mp_audio_set_num_channels(&spdif_ctx->fmt, num_channels);
mp_audio_set_format(&spdif_ctx->fmt, sample_format);
spdif_ctx->fmt.rate = samplerate;
mp_audio_set_num_channels(&da->decoded, num_channels);
mp_audio_set_format(&da->decoded, sample_format);
da->decoded.rate = samplerate;
if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
MP_FATAL(da, "libavformat spdif initialization failed.\n");
@@ -233,16 +175,19 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
spdif_ctx->need_close = true;
return 0;
return 1;
fail:
uninit(da);
return -1;
return 0;
}
static int decode_packet(struct dec_audio *da, struct mp_audio **out)
static int decode_packet(struct dec_audio *da)
{
struct spdifContext *spdif_ctx = da->priv;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
mp_audio_set_null_data(&da->decoded);
spdif_ctx->out_buffer_len = 0;
@@ -256,26 +201,19 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
pkt.pts = pkt.dts = 0;
MP_VERBOSE(da, "spdif packet, size=%d\n", pkt.size);
if (mpkt->pts != MP_NOPTS_VALUE) {
da->pts = mpkt->pts;
da->pts_offset = 0;
}
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
return AD_ERR;
}
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
int ret = av_write_frame(lavf_ctx, &pkt);
talloc_free(mpkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
avio_flush(lavf_ctx->pb);
if (ret < 0)
return AD_ERR;
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
*out = mp_audio_pool_get(da->pool, &spdif_ctx->fmt, samples);
if (!*out)
return AD_ERR;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
da->decoded.planes[0] = spdif_ctx->out_buffer;
da->decoded.samples = spdif_ctx->out_buffer_len / da->decoded.sstride;
return 0;
}
@@ -304,8 +242,6 @@ static void add_decoders(struct mp_decoder_list *list)
"libavformat/spdifenc audio pass-through decoder");
}
}
mp_add_decoder(list, "spdif", "dts", "dts-hd",
"libavformat/spdifenc audio pass-through decoder");
}
const struct ad_functions ad_spdif = {
+180 -83
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -24,9 +25,10 @@
#include "demux/codec_tags.h"
#include "config.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "misc/bstr.h"
#include "bstr/bstr.h"
#include "stream/stream.h"
#include "demux/demux.h"
@@ -41,18 +43,34 @@
#include "audio/filter/af.h"
extern const struct ad_functions ad_mpg123;
extern const struct ad_functions ad_lavc;
extern const struct ad_functions ad_spdif;
static const struct ad_functions * const ad_drivers[] = {
#if HAVE_MPG123
&ad_mpg123,
#endif
&ad_lavc,
&ad_spdif,
NULL
};
// 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)
{
if (!mp_audio_config_valid(&da->decoded)) {
MP_ERR(da, "Audio decoder did not specify audio "
"format, or requested an unsupported configuration!\n");
return false;
}
mp_audio_buffer_reinit(da->decode_buffer, &da->decoded);
return true;
}
static void uninit_decoder(struct dec_audio *d_audio)
{
audio_reset_decoding(d_audio);
if (d_audio->ad_driver) {
MP_VERBOSE(d_audio, "Uninit audio decoder.\n");
d_audio->ad_driver->uninit(d_audio);
@@ -60,8 +78,6 @@ static void uninit_decoder(struct dec_audio *d_audio)
d_audio->ad_driver = NULL;
talloc_free(d_audio->priv);
d_audio->priv = NULL;
d_audio->afilter->initialized = -1;
d_audio->decode_format = (struct mp_audio){0};
}
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
@@ -73,6 +89,7 @@ static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
return 0;
}
d_audio->decode_buffer = mp_audio_buffer_create(NULL);
return 1;
}
@@ -84,21 +101,11 @@ struct mp_decoder_list *audio_decoder_list(void)
return list;
}
static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio)
static struct mp_decoder_list *audio_select_decoders(const char *codec,
char *selection)
{
struct MPOpts *opts = d_audio->opts;
const char *codec = d_audio->header->codec;
struct mp_decoder_list *list = audio_decoder_list();
struct mp_decoder_list *new =
mp_select_decoders(list, codec, opts->audio_decoders);
if (d_audio->spdif_passthrough) {
struct mp_decoder_list *spdif =
mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif);
mp_append_decoders(spdif, new);
talloc_free(new);
new = spdif;
}
struct mp_decoder_list *new = mp_select_decoders(list, codec, selection);
talloc_free(list);
return new;
}
@@ -112,13 +119,14 @@ static const struct ad_functions *find_driver(const char *name)
return NULL;
}
int audio_init_best_codec(struct dec_audio *d_audio)
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders)
{
uninit_decoder(d_audio);
assert(!d_audio->ad_driver);
audio_reset_decoding(d_audio);
struct mp_decoder_entry *decoder = NULL;
struct mp_decoder_list *list = audio_select_decoders(d_audio);
struct mp_decoder_list *list =
audio_select_decoders(d_audio->header->codec, audio_decoders);
mp_print_decoders(d_audio->log, MSGL_V, "Codec list:", list);
@@ -156,30 +164,55 @@ void audio_uninit(struct dec_audio *d_audio)
{
if (!d_audio)
return;
MP_VERBOSE(d_audio, "Uninit audio filters...\n");
if (d_audio->afilter) {
MP_VERBOSE(d_audio, "Uninit audio filters...\n");
af_destroy(d_audio->afilter);
d_audio->afilter = NULL;
}
uninit_decoder(d_audio);
af_destroy(d_audio->afilter);
talloc_free(d_audio->waiting);
talloc_free(d_audio->decode_buffer);
talloc_free(d_audio);
}
static int decode_new_frame(struct dec_audio *da)
int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format)
{
while (!da->waiting) {
int ret = da->ad_driver->decode_packet(da, &da->waiting);
if (ret < 0)
return ret;
if (!d_audio->afilter)
d_audio->afilter = af_new(d_audio->global);
struct af_stream *afs = d_audio->afilter;
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
da->pts = 0;
// input format: same as codec's output format:
mp_audio_buffer_get_format(d_audio->decode_buffer, &afs->input);
// Sample rate can be different when adjusting playback speed
afs->input.rate = in_samplerate;
if (da->waiting) {
da->pts_offset += da->waiting->samples;
da->decode_format = *da->waiting;
mp_audio_set_null_data(&da->decode_format);
}
// output format: same as ao driver's input format (if missing, fallback to input)
afs->output.rate = *out_samplerate;
mp_audio_set_channels(&afs->output, out_channels);
mp_audio_set_format(&afs->output, *out_format);
afs->replaygain_data = d_audio->replaygain_data;
char *s_from = mp_audio_config_to_str(&afs->input);
char *s_to = mp_audio_config_to_str(&afs->output);
MP_VERBOSE(d_audio, "Building audio filter chain for %s -> %s...\n", s_from, s_to);
talloc_free(s_from);
talloc_free(s_to);
// let's autoprobe it!
if (af_init(afs) != 0) {
af_destroy(afs);
d_audio->afilter = NULL;
return 0; // failed :(
}
return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
*out_samplerate = afs->output.rate;
*out_channels = afs->output.channels;
*out_format = afs->output.format;
return 1;
}
/* Decode packets until we know the audio format. Then reinit the buffer.
@@ -188,22 +221,79 @@ static int decode_new_frame(struct dec_audio *da)
*/
int initial_audio_decode(struct dec_audio *da)
{
return decode_new_frame(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;
}
static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
int minsamples, bool eof)
// 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)
{
while (mp_audio_buffer_samples(outbuf) < minsamples) {
if (af_output_frame(afs, eof) < 0)
return true; // error, stop doing stuff
struct mp_audio *mpa = af_read_output_frame(afs);
if (!mpa)
return false; // out of data
mp_audio_buffer_append(outbuf, mpa);
talloc_free(mpa);
bool format_change = false;
int error = 0;
assert(len > 0); // would break EOF logic below
while (mp_audio_buffer_samples(da->decode_buffer) < len) {
// 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;
}
return true;
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 = error == AD_EOF && filter_data.samples == 0;
if (af_filter(da->afilter, &filter_data, eof ? AF_FILTER_FLAG_EOF : 0) < 0)
return AD_ERR;
mp_audio_buffer_append(outbuf, &filter_data);
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);
// 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 = AD_ERR; // switch to invalid format
}
return error;
}
/* Try to get at least minsamples decoded+filtered samples in outbuf
@@ -211,44 +301,52 @@ static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
* Return 0 on success, or negative AD_* error code.
* In the former case outbuf has at least minsamples buffered on return.
* In case of EOF/error it might or might not be. */
int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf,
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
int minsamples)
{
struct af_stream *afs = da->afilter;
if (afs->initialized < 1)
if (!d_audio->afilter)
return AD_ERR;
MP_STATS(da, "start audio");
// Indicates that a filter seems to be buffering large amounts of data
int huge_filter_buffer = 0;
int res;
while (1) {
res = 0;
/* Filter output size will be about filter_multiplier times input size.
* If some filter buffers audio in big blocks this might only hold
* as average over time. */
double filter_multiplier = af_calc_filter_multiplier(d_audio->afilter);
if (copy_output(afs, outbuf, minsamples, false))
int prev_buffered = -1;
int res = 0;
MP_STATS(d_audio, "start audio");
while (res >= 0 && minsamples >= 0) {
int buffered = mp_audio_buffer_samples(outbuf);
if (minsamples < buffered || buffered == prev_buffered)
break;
prev_buffered = buffered;
res = decode_new_frame(da);
if (res < 0) {
// drain filters first (especially for true EOF case)
copy_output(afs, outbuf, minsamples, true);
break;
int decsamples = (minsamples - buffered) / filter_multiplier;
// + 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
* input length didn't produce enough output from filters.
* Feed the filters 250 samples at a time until we have enough
* output. Very small amounts could make filtering inefficient while
* large amounts can make mpv demux the file unnecessarily far ahead
* to get audio data and buffer video frames in memory while doing
* so. However the performance impact of either is probably not too
* significant as long as the value is not completely insane. */
decsamples = 250;
}
// On format change, make sure to drain the filter chain.
if (!mp_audio_config_equals(&afs->input, da->waiting)) {
copy_output(afs, outbuf, minsamples, true);
res = AD_NEW_FMT;
break;
}
/* if this iteration does not fill buffer, we must have lots
* of buffering in filters */
huge_filter_buffer = 1;
struct mp_audio *mpa = da->waiting;
da->waiting = NULL;
if (af_filter_frame(afs, mpa) < 0)
return AD_ERR;
res = filter_n_bytes(d_audio, outbuf, decsamples);
}
MP_STATS(da, "end audio");
MP_STATS(d_audio, "end audio");
return res;
}
@@ -256,11 +354,10 @@ void audio_reset_decoding(struct dec_audio *d_audio)
{
if (d_audio->ad_driver)
d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
af_seek_reset(d_audio->afilter);
if (d_audio->afilter)
af_control_all(d_audio->afilter, AF_CONTROL_RESET, NULL);
d_audio->pts = MP_NOPTS_VALUE;
d_audio->pts_offset = 0;
if (d_audio->waiting) {
talloc_free(d_audio->waiting);
d_audio->waiting = NULL;
}
if (d_audio->decode_buffer)
mp_audio_buffer_clear(d_audio->decode_buffer);
}
+13 -9
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_DEC_AUDIO_H
@@ -30,16 +31,15 @@ struct dec_audio {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
bool spdif_passthrough;
const struct ad_functions *ad_driver;
struct sh_stream *header;
struct mp_audio_buffer *decode_buffer;
struct af_stream *afilter;
char *decoder_desc;
struct replaygain_data *replaygain_data;
int init_retries;
struct mp_audio_pool *pool;
struct mp_audio decode_format;
struct mp_audio *waiting; // used on format-change
// set by decoder
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;
@@ -58,11 +58,15 @@ enum {
};
struct mp_decoder_list *audio_decoder_list(void);
int audio_init_best_codec(struct dec_audio *d_audio);
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders);
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
int minsamples);
int initial_audio_decode(struct dec_audio *d_audio);
void audio_reset_decoding(struct dec_audio *d_audio);
void audio_uninit(struct dec_audio *d_audio);
int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format);
#endif /* MPLAYER_DEC_AUDIO_H */
+210 -265
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
@@ -27,41 +28,71 @@
#include "options/m_option.h"
#include "options/m_config.h"
#include "audio/audio_buffer.h"
#include "af.h"
// Static list of filters
extern const struct af_info af_info_dummy;
extern const struct af_info af_info_delay;
extern const struct af_info af_info_channels;
extern const struct af_info af_info_format;
extern const struct af_info af_info_force;
extern const struct af_info af_info_volume;
extern const struct af_info af_info_equalizer;
extern const struct af_info af_info_pan;
extern const struct af_info af_info_surround;
extern const struct af_info af_info_sub;
extern const struct af_info af_info_export;
extern const struct af_info af_info_drc;
extern const struct af_info af_info_extrastereo;
extern const struct af_info af_info_lavcac3enc;
extern const struct af_info af_info_lavrresample;
extern const struct af_info af_info_sweep;
extern const struct af_info af_info_hrtf;
extern const struct af_info af_info_ladspa;
extern const struct af_info af_info_center;
extern const struct af_info af_info_sinesuppress;
extern const struct af_info af_info_karaoke;
extern const struct af_info af_info_scaletempo;
extern const struct af_info af_info_bs2b;
extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_rubberband;
extern const struct af_info af_info_convert24;
extern const struct af_info af_info_convertsignendian;
static const struct af_info *const filter_list[] = {
&af_info_dummy,
&af_info_delay,
&af_info_channels,
&af_info_format,
&af_info_volume,
&af_info_equalizer,
&af_info_pan,
&af_info_surround,
&af_info_sub,
#if HAVE_SYS_MMAN_H
&af_info_export,
#endif
&af_info_drc,
&af_info_extrastereo,
&af_info_lavcac3enc,
&af_info_lavrresample,
#if HAVE_RUBBERBAND
&af_info_rubberband,
&af_info_sweep,
&af_info_hrtf,
#if HAVE_LADSPA
&af_info_ladspa,
#endif
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
&af_info_scaletempo,
#if HAVE_LIBBS2B
&af_info_bs2b,
#endif
#if HAVE_LIBAVFILTER
&af_info_lavfi,
#endif
// Must come last, because they're fallback format conversion filter
&af_info_convert24,
&af_info_convertsignendian,
NULL
};
@@ -90,17 +121,11 @@ const struct m_obj_list af_obj_list = {
},
};
static void af_forget_frames(struct af_instance *af)
static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
{
for (int n = 0; n < af->num_out_queued; n++)
talloc_free(af->out_queued[n]);
af->num_out_queued = 0;
}
static void af_chain_forget_frames(struct af_stream *s)
{
for (struct af_instance *cur = s->first; cur; cur = cur->next)
af_forget_frames(cur);
return a->format == b->format
&& mp_chmap_equals(&a->channels, &b->channels)
&& a->rate == b->rate;
}
static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src)
@@ -137,15 +162,14 @@ static int output_control(struct af_instance* af, int cmd, void* arg)
*filter_output = *output;
af_copy_unset_fields(filter_output, in);
*in = *filter_output;
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
return af_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
}
return AF_UNKNOWN;
}
static int dummy_filter(struct af_instance *af, struct mp_audio *frame)
static int dummy_filter(struct af_instance* af, struct mp_audio* data, int f)
{
af_add_output_frame(af, frame);
return 0;
}
@@ -159,15 +183,28 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
return NULL;
}
const struct af_info *info = desc.p;
/* Make sure that the filter is not already in the list if it is
non-reentrant */
if (info->flags & AF_FLAGS_NOT_REENTRANT) {
for (struct af_instance *cur = s->first; cur; cur = cur->next) {
if (cur->info == info) {
MP_ERR(s, "There can only be one "
"instance of the filter '%s' in each stream\n", name);
return NULL;
}
}
}
MP_VERBOSE(s, "Adding filter %s \n", name);
struct af_instance *af = talloc_zero(NULL, struct af_instance);
*af = (struct af_instance) {
.info = desc.p,
.info = info,
.mul = 1,
.data = talloc_zero(af, struct mp_audio),
.log = mp_log_new(af, s->log, name),
.replaygain_data = s->replaygain_data,
.out_pool = mp_audio_pool_create(af),
};
struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
@@ -252,7 +289,6 @@ static void af_remove(struct af_stream *s, struct af_instance *af)
if (af->uninit)
af->uninit(af);
af_forget_frames(af);
talloc_free(af);
}
@@ -276,8 +312,11 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
while (af) {
char b[128] = {0};
mp_snprintf_cat(b, sizeof(b), " [%s] ", af->info->name);
if (af->data)
mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data));
if (af->data) {
char *info = mp_audio_config_to_str(af->data);
mp_snprintf_cat(b, sizeof(b), "%s", info);
talloc_free(info);
}
if (af == at)
mp_snprintf_cat(b, sizeof(b), " <-");
MP_MSG(s, msg_level, "%s\n", b);
@@ -285,12 +324,94 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
af = af->next;
}
MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output));
char *info = mp_audio_config_to_str(&s->output);
MP_MSG(s, msg_level, " [ao] %s\n", info);
talloc_free(info);
}
static int af_count_filters(struct af_stream *s)
{
int count = 0;
for (struct af_instance *af = s->first; af; af = af->next)
count++;
return count;
}
// Finds the first conversion filter on the way from srcfmt to dstfmt.
// Conversions form a DAG: each node is a format/filter pair, and possible
// conversions are edges. We search the DAG for the shortest path.
// Some cases visit the same filter multiple times, but with different formats
// (like u24le->s8), so one node per format or filter separate is not enough.
// Returns the filter and dest. format for the first conversion step.
// (So we know what conversion filter with what format to insert next.)
static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
{
#define MAX_NODES (64 * 32)
int num_fmt = 0, num_filt = 0;
for (int n = 0; filter_list[n]; n++)
num_filt = n + 1;
for (int n = 0; af_fmtstr_table[n].format; n++)
num_fmt = n + 1;
int num_nodes = num_fmt * num_filt;
assert(num_nodes < MAX_NODES);
bool visited[MAX_NODES] = {0};
unsigned char distance[MAX_NODES];
short previous[MAX_NODES] = {0};
for (int n = 0; n < num_nodes; n++) {
distance[n] = 255;
if (af_fmtstr_table[n % num_fmt].format == srcfmt)
distance[n] = 0;
}
while (1) {
int next = -1;
for (int n = 0; n < num_nodes; n++) {
if (!visited[n] && (next < 0 || (distance[n] < distance[next])))
next = n;
}
if (next < 0 || distance[next] == 255)
return NULL;
visited[next] = true;
int fmt = next % num_fmt;
if (af_fmtstr_table[fmt].format == *dstfmt) {
// Best match found
for (int cur = next; cur >= 0; cur = previous[cur] - 1) {
if (distance[cur] == 1) {
*dstfmt = af_fmtstr_table[cur % num_fmt].format;
return (char *)filter_list[cur / num_fmt]->name;
}
}
return NULL;
}
for (int n = 0; filter_list[n]; n++) {
const struct af_info *af = filter_list[n];
if (!af->test_conversion)
continue;
for (int i = 0; af_fmtstr_table[i].format; i++) {
if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format,
af_fmtstr_table[i].format))
{
int other = n * num_fmt + i;
int ndist = distance[next] + 1;
if (ndist < distance[other]) {
distance[other] = ndist;
previous[other] = next + 1;
}
}
}
}
}
assert(0);
#undef MAX_NODES
}
static bool af_is_conversion_filter(struct af_instance *af)
{
return af && strcmp(af->info->name, "lavrresample") == 0;
return af && af->info->test_conversion != NULL;
}
// in is what af can take as input - insert a conversion filter if the actual
@@ -310,24 +431,21 @@ static int af_fix_format_conversion(struct af_stream *s,
if (actual.format == in.format)
return AF_FALSE;
int dstfmt = in.format;
char *filter = "lavrresample";
if (!af_lavrresample_test_conversion(actual.format, dstfmt))
char *filter = af_find_conversion_filter(actual.format, &dstfmt);
if (!filter)
return AF_ERROR;
if (strcmp(filter, prev->info->name) == 0) {
if (prev->control(prev, AF_CONTROL_SET_FORMAT, &dstfmt) == AF_OK) {
*p_af = prev;
return AF_OK;
}
return AF_ERROR;
}
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt))) {
af_remove(s, new);
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt)))
return rv;
}
*p_af = new;
return AF_OK;
}
@@ -381,15 +499,6 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
return AF_OK;
}
static void reset_formats(struct af_stream *s)
{
for (struct af_instance *af = s->first; af; af = af->next) {
af->control(af, AF_CONTROL_SET_RESAMPLE_RATE, &(int){0});
af->control(af, AF_CONTROL_SET_CHANNELS, &(struct mp_chmap){0});
af->control(af, AF_CONTROL_SET_FORMAT, &(int){0});
}
}
// Return AF_OK on success or AF_ERROR on failure.
// Warning:
// A failed af_reinit() leaves the audio chain behind in a useless, broken
@@ -399,14 +508,11 @@ static void reset_formats(struct af_stream *s)
static int af_reinit(struct af_stream *s)
{
remove_auto_inserted_filters(s);
af_chain_forget_frames(s);
reset_formats(s);
s->first->fmt_in = s->first->fmt_out = s->input;
// Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
struct af_instance *af = s->first->next;
// Up to 4 retries per filter (channel, rate, format conversions)
int max_retry = 4;
// Up to 7 retries per filter (channel, rate, 5x format conversions)
int max_retry = af_count_filters(s) * 7;
int retry = 0;
while (af) {
if (retry >= max_retry)
@@ -417,18 +523,11 @@ static int af_reinit(struct af_stream *s)
// Reset just in case...
mp_audio_set_null_data(&in);
if (!mp_audio_config_valid(&in))
goto error;
af->fmt_in = in;
int rv = af->control(af, AF_CONTROL_REINIT, &in);
if (rv == AF_OK && !mp_audio_config_equals(&in, af->prev->data))
rv = AF_FALSE; // conversion filter needed
switch (rv) {
case AF_OK:
if (!mp_audio_config_valid(af->data))
goto error;
af->fmt_out = *af->data;
af = af->next;
break;
case AF_FALSE: { // Configuration filter is needed
@@ -447,23 +546,6 @@ static int af_reinit(struct af_stream *s)
retry++;
continue;
}
// If the format conversion is (probably) caused by spdif, then
// (as a feature) drop the filter, instead of failing hard.
int fmt_in1 = af->prev->data->format;
int fmt_in2 = in.format;
if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
bool spd1 = af_fmt_is_spdif(fmt_in1);
bool spd2 = af_fmt_is_spdif(fmt_in2);
if (spd1 != spd2 && af->next) {
MP_WARN(af, "Filter %s apparently cannot be used due to "
"spdif passthrough - removing it.\n",
af->info->name);
struct af_instance *aft = af->prev;
af_remove(s, af);
af = aft->next;
break;
}
}
goto negotiate_error;
}
case AF_DETACH: { // Filter is redundant and wants to be unloaded
@@ -476,29 +558,22 @@ static int af_reinit(struct af_stream *s)
MP_ERR(s, "Reinitialization did not work, "
"audio filter '%s' returned error code %i\n",
af->info->name, rv);
goto error;
af_print_filter_chain(s, af, MSGL_ERR);
return AF_ERROR;
}
if (af && !af->auto_inserted)
retry = 0;
}
af_print_filter_chain(s, NULL, MSGL_V);
/* Set previously unset fields in s->output to those of the filter chain
* output. This is used to make the output format fixed, and even if you
* insert new filters or change the input format, the output format won't
* change. (Audio outputs generally can't change format at runtime.) */
af_copy_unset_fields(&s->output, &s->filter_output);
if (mp_audio_config_equals(&s->output, &s->filter_output)) {
s->initialized = 1;
af_print_filter_chain(s, NULL, MSGL_V);
return AF_OK;
}
goto error;
return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
negotiate_error:
MP_ERR(s, "Unable to convert audio input format to output format.\n");
error:
s->initialized = -1;
af_print_filter_chain(s, af, MSGL_ERR);
return AF_ERROR;
}
@@ -508,40 +583,35 @@ void af_uninit(struct af_stream *s)
{
while (s->first->next && s->first->next != s->last)
af_remove(s, s->first->next);
af_chain_forget_frames(s);
s->initialized = 0;
}
struct af_stream *af_new(struct mpv_global *global)
{
struct af_stream *s = talloc_zero(NULL, struct af_stream);
s->log = mp_log_new(s, global->log, "!af");
static const struct af_info in = { .name = "in" };
s->first = talloc(s, struct af_instance);
*s->first = (struct af_instance) {
.info = &in,
.log = s->log,
.control = input_control,
.filter_frame = dummy_filter,
.filter = dummy_filter,
.priv = s,
.data = &s->input,
.mul = 1.0,
};
static const struct af_info out = { .name = "out" };
s->last = talloc(s, struct af_instance);
*s->last = (struct af_instance) {
.info = &out,
.log = s->log,
.control = output_control,
.filter_frame = dummy_filter,
.filter = dummy_filter,
.priv = s,
.data = &s->filter_output,
.mul = 1.0,
};
s->first->next = s->last;
s->last->prev = s->first;
s->opts = global->opts;
s->log = mp_log_new(s, global->log, "!af");
return s;
}
@@ -562,6 +632,10 @@ void af_destroy(struct af_stream *s)
The return value is 0 if success and -1 if failure */
int af_init(struct af_stream *s)
{
// Sanity check
if (!s)
return -1;
// Precaution in case caller is misbehaving
mp_audio_set_null_data(&s->input);
mp_audio_set_null_data(&s->output);
@@ -571,20 +645,17 @@ int af_init(struct af_stream *s)
// Add all filters in the list (if there are any)
struct m_obj_settings *list = s->opts->af_settings;
for (int i = 0; list && list[i].name; i++) {
struct af_instance *af =
af_prepend(s, s->last, list[i].name, list[i].attribs);
if (!af) {
if (!af_prepend(s, s->last, list[i].name, list[i].attribs)) {
af_uninit(s);
s->initialized = -1;
return -1;
}
af->label = talloc_strdup(af, list[i].label);
}
}
if (af_reinit(s) != AF_OK) {
// Something is stuffed audio out will not work
MP_ERR(s, "Could not create audio filter chain.\n");
af_uninit(s);
return -1;
}
return 0;
@@ -594,15 +665,12 @@ int af_init(struct af_stream *s)
to the stream s. The filter will be inserted somewhere nice in the
list of filters. The return value is a pointer to the new filter,
If the filter couldn't be added the return value is NULL. */
struct af_instance *af_add(struct af_stream *s, char *name, char *label,
char **args)
struct af_instance *af_add(struct af_stream *s, char *name, char **args)
{
assert(label);
if (af_find_by_label(s, label))
return NULL;
struct af_instance *new;
// Sanity check
if (!s || !s->first || !name)
return NULL;
// Insert the filter somewhere nice
if (af_is_conversion_filter(s->first->next))
new = af_append(s, s->first->next, name, args);
@@ -610,40 +678,47 @@ struct af_instance *af_add(struct af_stream *s, char *name, char *label,
new = af_prepend(s, s->first->next, name, args);
if (!new)
return NULL;
new->label = talloc_strdup(new, label);
// Reinitialize the filter list
if (af_reinit(s) != AF_OK) {
af_remove_by_label(s, label);
return NULL;
}
return af_find_by_label(s, label);
}
struct af_instance *af_find_by_label(struct af_stream *s, char *label)
{
for (struct af_instance *af = s->first; af; af = af->next) {
if (af->label && strcmp(af->label, label) == 0)
return af;
}
return NULL;
}
/* Remove the first filter that matches this name. Return number of filters
* removed (0, 1), or a negative error code if reinit after removing failed.
*/
int af_remove_by_label(struct af_stream *s, char *label)
{
struct af_instance *af = af_find_by_label(s, label);
if (!af)
return 0;
af_remove(s, af);
// Reinitalize the filter list
if (af_reinit(s) != AF_OK) {
af_uninit(s);
af_init(s);
return -1;
return NULL;
}
return 1;
return new;
}
/* Filter data chunk through the filters in the list.
* On success, *data is set to the filtered data/format.
* Warning: input audio data will be overwritten.
*/
int af_filter(struct af_stream *s, struct mp_audio *data, int flags)
{
struct af_instance *af = s->first;
assert(mp_audio_config_equals(af->data, data));
// Iterate through all filters
while (af) {
int r = af->filter(af, data, flags);
if (r < 0)
return r;
assert(mp_audio_config_equals(af->data, data));
af = af->next;
}
return 0;
}
// Calculate average ratio of filter output samples to input samples.
// e.g: num_output_samples = mul * num_input_samples
double af_calc_filter_multiplier(struct af_stream *s)
{
struct af_instance *af = s->first;
double mul = 1;
do {
mul *= af->mul;
af = af->next;
} while (af);
return mul;
}
/* Calculate the total delay [seconds of output] caused by the filters */
@@ -653,8 +728,6 @@ double af_calc_delay(struct af_stream *s)
double delay = 0.0;
while (af) {
delay += af->delay;
for (int n = 0; n < af->num_out_queued; n++)
delay += af->out_queued[n]->samples / (double)af->data->rate;
af = af->next;
}
return delay;
@@ -681,131 +754,3 @@ void af_control_all(struct af_stream *s, int cmd, void *arg)
for (struct af_instance *af = s->first; af; af = af->next)
af->control(af, cmd, arg);
}
int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label)
{
char *label_str = bstrdup0(NULL, label);
struct af_instance *cur = af_find_by_label(s, label_str);
talloc_free(label_str);
if (cur) {
return cur->control ? cur->control(cur, cmd, arg) : CONTROL_NA;
} else {
return CONTROL_UNKNOWN;
}
}
// Used by filters to add a filtered frame to the output queue.
// Ownership of frame is transferred from caller to the filter chain.
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame)
{
if (frame) {
assert(mp_audio_config_equals(&af->fmt_out, frame));
MP_TARRAY_APPEND(af, af->out_queued, af->num_out_queued, frame);
}
}
static bool af_has_output_frame(struct af_instance *af)
{
if (!af->num_out_queued && af->filter_out) {
if (af->filter_out(af) < 0)
MP_ERR(af, "Error filtering frame.\n");
}
return af->num_out_queued > 0;
}
static struct mp_audio *af_dequeue_output_frame(struct af_instance *af)
{
struct mp_audio *res = NULL;
if (af_has_output_frame(af)) {
res = af->out_queued[0];
MP_TARRAY_REMOVE_AT(af->out_queued, af->num_out_queued, 0);
}
return res;
}
static void read_remaining(struct af_instance *af)
{
int num_frames;
do {
num_frames = af->num_out_queued;
if (!af->filter_out || af->filter_out(af) < 0)
break;
} while (num_frames != af->num_out_queued);
}
static int af_do_filter(struct af_instance *af, struct mp_audio *frame)
{
if (frame)
assert(mp_audio_config_equals(&af->fmt_in, frame));
int r = af->filter_frame(af, frame);
if (r < 0)
MP_ERR(af, "Error filtering frame.\n");
return r;
}
// Input a frame into the filter chain. Ownership of frame is transferred.
// Return >= 0 on success, < 0 on failure (even if output frames were produced)
int af_filter_frame(struct af_stream *s, struct mp_audio *frame)
{
assert(frame);
if (s->initialized < 1) {
talloc_free(frame);
return -1;
}
return af_do_filter(s->first, frame);
}
// Output the next queued frame (if any) from the full filter chain.
// The frame can be retrieved with af_read_output_frame().
// eof: if set, assume there's no more input i.e. af_filter_frame() will
// not be called (until reset) - flush all internally delayed frames
// returns: -1: error, 0: no output, 1: output available
int af_output_frame(struct af_stream *s, bool eof)
{
if (s->last->num_out_queued)
return 1;
if (s->initialized < 1)
return -1;
while (1) {
struct af_instance *last = NULL;
for (struct af_instance * cur = s->first; cur; cur = cur->next) {
// Flush remaining frames on EOF, but do that only if the previous
// filters have been flushed (i.e. they have no more output).
if (eof && !last) {
read_remaining(cur);
int r = af_do_filter(cur, NULL);
if (r < 0)
return r;
}
if (af_has_output_frame(cur))
last = cur;
}
if (!last)
return 0;
if (!last->next)
return 1;
int r = af_do_filter(last->next, af_dequeue_output_frame(last));
if (r < 0)
return r;
}
}
struct mp_audio *af_read_output_frame(struct af_stream *s)
{
if (!s->last->num_out_queued)
af_output_frame(s, false);
return af_dequeue_output_frame(s->last);
}
// Make sure the caller can change data referenced by the frame.
// Return negative error code on failure (i.e. you can't write).
int af_make_writeable(struct af_instance *af, struct mp_audio *frame)
{
return mp_audio_pool_make_writeable(af->out_pool, frame);
}
void af_seek_reset(struct af_stream *s)
{
af_control_all(s, AF_CONTROL_RESET, NULL);
af_chain_forget_frames(s);
}
+30 -44
View File
@@ -1,18 +1,19 @@
/*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_AF_H
@@ -27,7 +28,6 @@
#include "audio/chmap.h"
#include "audio/audio.h"
#include "common/msg.h"
#include "common/common.h"
struct af_instance;
struct mpv_global;
@@ -35,6 +35,10 @@ struct mpv_global;
// Number of channels
#define AF_NCH MP_NUM_CHANNELS
// Flags used for defining the behavior of an audio filter
#define AF_FLAGS_REENTRANT 0x00000000
#define AF_FLAGS_NOT_REENTRANT 0x00000001
// Flags for af->filter()
#define AF_FILTER_FLAG_EOF 1
@@ -43,7 +47,9 @@ struct mpv_global;
struct af_info {
const char *info;
const char *name;
const int flags;
int (*open)(struct af_instance *vf);
bool (*test_conversion)(int src_format, int dst_format);
int priv_size;
const void *priv_defaults;
const struct m_option *options;
@@ -56,35 +62,24 @@ struct af_instance {
struct replaygain_data *replaygain_data;
int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af);
/* Feed a frame. The frame is NULL if EOF was reached, and the filter
* should drain all remaining buffered data.
* Use af_add_output_frame() to output data. The optional filter_out
* callback can be set to produce output frames gradually.
/* flags is a bit mask of AF_FILTER_FLAG_* values
* returns 0 on success, negative value on error
*/
int (*filter_frame)(struct af_instance *af, struct mp_audio *frame);
int (*filter_out)(struct af_instance *af);
int (*filter)(struct af_instance *af, struct mp_audio *data, int flags);
void *priv;
struct mp_audio *data; // configuration and buffer for outgoing data stream
struct af_instance *next;
struct af_instance *prev;
double delay; /* Delay caused by the filter, in seconds of audio consumed
* without corresponding output */
double mul; /* length multiplier: how much does this instance change
* the number of samples passed though. (Ratio of input
* and output, e.g. mul=4 => 1 sample becomes 4 samples) .*/
bool auto_inserted; // inserted by af.c, such as conversion filters
char *label;
struct mp_audio fmt_in, fmt_out;
struct mp_audio **out_queued;
int num_out_queued;
struct mp_audio_pool *out_pool;
};
// Current audio stream
struct af_stream {
int initialized; // 0: no, 1: yes, -1: attempted to, but failed
// The first and last filter in the list
struct af_instance *first;
struct af_instance *last;
@@ -100,12 +95,13 @@ struct af_stream {
};
// Return values
#define AF_DETACH (CONTROL_OK + 1)
#define AF_OK CONTROL_OK
#define AF_TRUE CONTROL_TRUE
#define AF_FALSE CONTROL_FALSE
#define AF_UNKNOWN CONTROL_UNKNOWN
#define AF_ERROR CONTROL_ERROR
#define AF_DETACH 2
#define AF_OK 1
#define AF_TRUE 1
#define AF_FALSE 0
#define AF_UNKNOWN -1
#define AF_ERROR -2
#define AF_FATAL -3
// Parameters for af_control_*
enum af_control {
@@ -121,8 +117,6 @@ enum af_control {
AF_CONTROL_SET_PAN_BALANCE,
AF_CONTROL_GET_PAN_BALANCE,
AF_CONTROL_SET_PLAYBACK_SPEED,
AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE,
AF_CONTROL_GET_METADATA,
};
// Argument for AF_CONTROL_SET_PAN_LEVEL
@@ -135,28 +129,20 @@ struct af_stream *af_new(struct mpv_global *global);
void af_destroy(struct af_stream *s);
int af_init(struct af_stream *s);
void af_uninit(struct af_stream *s);
struct af_instance *af_add(struct af_stream *s, char *name, char *label,
char **args);
int af_remove_by_label(struct af_stream *s, char *label);
struct af_instance *af_find_by_label(struct af_stream *s, char *label);
struct af_instance *af_add(struct af_stream *s, char *name, char **args);
int af_filter(struct af_stream *s, struct mp_audio *data, int flags);
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
void af_control_all(struct af_stream *s, int cmd, void *arg);
int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label);
void af_seek_reset(struct af_stream *s);
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame);
int af_filter_frame(struct af_stream *s, struct mp_audio *frame);
int af_output_frame(struct af_stream *s, bool eof);
struct mp_audio *af_read_output_frame(struct af_stream *s);
int af_make_writeable(struct af_instance *af, struct mp_audio *frame);
double af_calc_filter_multiplier(struct af_stream *s);
double af_calc_delay(struct af_stream *s);
int af_test_output(struct af_instance *af, struct mp_audio *out);
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
int af_to_dB(int n, float *in, float *out, float k);
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
int af_to_ms(int n, int *in, float *out, int rate);
float af_softclip(float a);
bool af_lavrresample_test_conversion(int src_format, int dst_format);
#endif /* MPLAYER_AF_H */
+206
View File
@@ -0,0 +1,206 @@
/*
* The Bauer stereophonic-to-binaural DSP using bs2b library:
* http://bs2b.sourceforge.net/
*
* Copyright (c) 2009 Andrew Savchenko
*
* 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 <bs2b.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "af.h"
#include "options/m_option.h"
/// Internal specific data of the filter
struct af_bs2b {
int fcut; ///< cut frequency in Hz
int feed; ///< feed level for low frequencies in 0.1*dB
int profile ; ///< profile (available crossfeed presets)
t_bs2bdp filter; ///< instance of a library filter
};
#define FILTER(name, type) \
static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
{ \
/* filter is called for all pairs of samples available in the buffer */ \
bs2b_cross_feed_##name(((struct af_bs2b*)(af->priv))->filter, \
(type*)(data->planes[0]), data->samples); \
\
return 0; \
}
FILTER(f, float)
FILTER(fbe, float)
FILTER(fle, float)
FILTER(s32be, int32_t)
FILTER(u32be, uint32_t)
FILTER(s32le, int32_t)
FILTER(u32le, uint32_t)
FILTER(s24be, bs2b_int24_t)
FILTER(u24be, bs2b_uint24_t)
FILTER(s24le, bs2b_int24_t)
FILTER(u24le, bs2b_uint24_t)
FILTER(s16be, int16_t)
FILTER(u16be, uint16_t)
FILTER(s16le, int16_t)
FILTER(u16le, uint16_t)
FILTER(s8, int8_t)
FILTER(u8, uint8_t)
/// Initialization and runtime control
static int control(struct af_instance *af, int cmd, void *arg)
{
struct af_bs2b *s = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
int format;
// Sanity check
if (!arg) return AF_ERROR;
format = ((struct mp_audio*)arg)->format;
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_set_num_channels(af->data, 2); // bs2b is useful only for 2ch audio
mp_audio_set_format(af->data, format);
/* check for formats supported by libbs2b
and assign corresponding handlers */
switch (format) {
case AF_FORMAT_FLOAT_BE:
af->filter = filter_fbe;
break;
case AF_FORMAT_FLOAT_LE:
af->filter = filter_fle;
break;
case AF_FORMAT_S32_BE:
af->filter = filter_s32be;
break;
case AF_FORMAT_U32_BE:
af->filter = filter_u32be;
break;
case AF_FORMAT_S32_LE:
af->filter = filter_s32le;
break;
case AF_FORMAT_U32_LE:
af->filter = filter_u32le;
break;
case AF_FORMAT_S24_BE:
af->filter = filter_s24be;
break;
case AF_FORMAT_U24_BE:
af->filter = filter_u24be;
break;
case AF_FORMAT_S24_LE:
af->filter = filter_s24le;
break;
case AF_FORMAT_U24_LE:
af->filter = filter_u24le;
break;
case AF_FORMAT_S16_BE:
af->filter = filter_s16be;
break;
case AF_FORMAT_U16_BE:
af->filter = filter_u16be;
break;
case AF_FORMAT_S16_LE:
af->filter = filter_s16le;
break;
case AF_FORMAT_U16_LE:
af->filter = filter_u16le;
break;
case AF_FORMAT_S8:
af->filter = filter_s8;
break;
case AF_FORMAT_U8:
af->filter = filter_u8;
break;
default:
af->filter = filter_f;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
break;
}
// bs2b have srate limits, try to resample if needed
if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) {
af->data->rate = BS2B_DEFAULT_SRATE;
MP_WARN(af, "Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
"Trying to resample to %d Hz.\n",
af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE);
}
bs2b_set_srate(s->filter, (long)af->data->rate);
MP_VERBOSE(af, "using format %s\n",
af_fmt_to_str(af->data->format));
return af_test_output(af,(struct mp_audio*)arg);
}
}
return AF_UNKNOWN;
}
/// Deallocate memory and close library
static void uninit(struct af_instance *af)
{
struct af_bs2b *s = af->priv;
if (s->filter)
bs2b_close(s->filter);
}
/// Allocate memory, set function pointers and init library
static int af_open(struct af_instance *af)
{
struct af_bs2b *s = af->priv;
af->control = control;
af->uninit = uninit;
// NULL means failed initialization
if (!(s->filter = bs2b_open())) {
return AF_ERROR;
}
if (s->profile)
bs2b_set_level(s->filter, s->profile);
// set fcut and feed only if specified, otherwise defaults will be used
if (s->fcut)
bs2b_set_level_fcut(s->filter, s->fcut);
if (s->feed)
bs2b_set_level_feed(s->filter, s->feed);
return AF_OK;
}
#define OPT_BASE_STRUCT struct af_bs2b
/// Description of this filter
const struct af_info af_info_bs2b = {
.info = "Bauer stereophonic-to-binaural audio filter",
.name = "bs2b",
.open = af_open,
.priv_size = sizeof(struct af_bs2b),
.options = (const struct m_option[]) {
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
OPT_INTRANGE("feed", feed, 0, BS2B_MINFEED, BS2B_MAXFEED),
OPT_CHOICE("profile", profile, 0,
({"default", BS2B_DEFAULT_CLEVEL},
{"cmoy", BS2B_CMOY_CLEVEL},
{"jmeier", BS2B_JMEIER_CLEVEL})),
{0}
},
};
+99
View File
@@ -0,0 +1,99 @@
/*
* This filter adds a center channel to the audio stream by
* averaging the left and right channel.
* There are two runtime controls one for setting which channel
* to insert the center-audio into called AF_CONTROL_SUB_CH.
*
* FIXME: implement a high-pass filter for better results.
*
* copyright (c) 2005 Alex Beregszaszi
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
typedef struct af_center_s
{
int ch; // Channel number which to insert the filtered data
}af_center_t;
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_center_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
// Sanity check
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
return af_test_output(af,(struct mp_audio*)arg);
}
}
return AF_UNKNOWN;
}
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_center_t* s = af->priv; // Setup for this instance
float* a = c->planes[0]; // Audio data
int nch = c->nch; // Number of channels
int len = c->samples*c->nch; // Number of samples in current audio block
int ch = s->ch; // Channel in which to insert the center audio
register int i;
// Run filter
for(i=0;i<len;i+=nch){
// Average left and right
a[i+ch] = (a[i]/2) + (a[i+1]/2);
}
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->filter=filter;
return AF_OK;
}
#define OPT_BASE_STRUCT af_center_t
const struct af_info af_info_center = {
.info = "Audio filter for adding a center channel",
.name = "center",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_center_t),
.options = (const struct m_option[]) {
OPT_INTRANGE("channel", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(1)),
{0}
},
};
+15 -17
View File
@@ -5,20 +5,21 @@
*
* Original author: Anders
*
* This file is part of mpv.
* This file is part of MPlayer.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -180,20 +181,15 @@ static int control(struct af_instance* af, int cmd, void* arg)
return AF_UNKNOWN;
}
static int filter_frame(struct af_instance *af, struct mp_audio *c)
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
struct mp_audio* l = af->data; // Local data
af_channels_t* s = af->priv;
int i;
if (!c)
return 0;
struct mp_audio *l = mp_audio_pool_get(af->out_pool, &af->fmt_out, c->samples);
if (!l) {
talloc_free(c);
return -1;
}
mp_audio_copy_attributes(l, c);
mp_audio_realloc_min(af->data, data->samples);
// Reset unused channels
memset(l->planes[0],0,mp_audio_psize(c) / c->nch * l->nch);
@@ -203,15 +199,17 @@ static int filter_frame(struct af_instance *af, struct mp_audio *c)
copy(af, c->planes[0],l->planes[0],c->nch,s->route[i][FR],
l->nch,s->route[i][TO],mp_audio_psize(c),c->bps);
talloc_free(c);
af_add_output_frame(af, l);
// Set output data
c->planes[0] = l->planes[0];
mp_audio_set_channels(c, &l->channels);
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->filter_frame = filter_frame;
af->filter=filter;
af_channels_t *s = af->priv;
// If router scan commandline for routing pairs
+116
View File
@@ -0,0 +1,116 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "audio/format.h"
#include "af.h"
static bool test_conversion(int src_format, int dst_format)
{
return (src_format == AF_FORMAT_U24 && dst_format == AF_FORMAT_U32) ||
(src_format == AF_FORMAT_S24 && dst_format == AF_FORMAT_S32) ||
(src_format == AF_FORMAT_U32 && dst_format == AF_FORMAT_U24) ||
(src_format == AF_FORMAT_S32 && dst_format == AF_FORMAT_S24);
}
static int control(struct af_instance *af, int cmd, void *arg)
{
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (!test_conversion(in->format, out->format))
return AF_DETACH;
if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_24BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 32));
} else if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_32BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 24));
} else {
abort();
}
out->rate = in->rate;
mp_audio_set_channels(out, &in->channels);
assert(test_conversion(in->format, out->format));
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_SET_FORMAT: {
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// The LSB is always ignored.
#if BYTE_ORDER == BIG_ENDIAN
#define SHIFT(x) ((3-(x))*8)
#else
#define SHIFT(x) (((x)+1)*8)
#endif
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
mp_audio_realloc_min(af->data, data->samples);
struct mp_audio *out = af->data;
size_t len = mp_audio_psize(data) / data->bps;
if (data->bps == 4) {
for (int s = 0; s < len; s++) {
uint32_t val = *((uint32_t *)data->planes[0] + s);
uint8_t *ptr = (uint8_t *)out->planes[0] + s * 3;
ptr[0] = val >> SHIFT(0);
ptr[1] = val >> SHIFT(1);
ptr[2] = val >> SHIFT(2);
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 24));
} else {
for (int s = 0; s < len; s++) {
uint8_t *ptr = (uint8_t *)data->planes[0] + s * 3;
uint32_t val = ptr[0] << SHIFT(0)
| ptr[1] << SHIFT(1)
| ptr[2] << SHIFT(2);
*((uint32_t *)out->planes[0] + s) = val;
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 32));
}
data->planes[0] = out->planes[0];
return 0;
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->filter = filter;
return AF_OK;
}
const struct af_info af_info_convert24 = {
.info = "Convert between 24 and 32 bit sample format",
.name = "convert24",
.open = af_open,
.test_conversion = test_conversion,
};
Loaded 100 of 601 files, more files were not shown because too many files have changed in this diff. Show more