Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27996bf1de | ||
|
|
e3d3a47c0d | ||
|
|
b109d20ef7 | ||
|
|
3c7d49c38f | ||
|
|
0ba2da8a76 | ||
|
|
1871f2a703 | ||
|
|
4a8e5ea269 | ||
|
|
367665b4c4 | ||
|
|
46437e96c6 | ||
|
|
29d3bbdaa2 | ||
|
|
16c3b27d0a | ||
|
|
f8dc208d0c | ||
|
|
6d006a5a39 | ||
|
|
6a355959d9 | ||
|
|
36bdbbe716 | ||
|
|
c269fa1609 | ||
|
|
0f1d6c09e0 | ||
|
|
b677937c27 | ||
|
|
fdd648bdf9 | ||
|
|
334fd10f1c | ||
|
|
226735331c | ||
|
|
5523370ab9 | ||
|
|
8c7d61f1d9 | ||
|
|
f021b5a379 | ||
|
|
43e164964c | ||
|
|
aeb64723fd | ||
|
|
f935838b2d | ||
|
|
ec46ad79e2 | ||
|
|
d6ccc4d8de | ||
|
|
8664dc3c47 | ||
|
|
51ce535822 | ||
|
|
e33df9b7bc | ||
|
|
6310bd60e6 | ||
|
|
6729e39983 | ||
|
|
88f247c0af |
No files matched your search
@@ -25,13 +25,8 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.6.0 is released ---
|
||||
1.5 - change in X11 and "--wid" behavior again. The previous change didn't
|
||||
work as expected, and now the behavior can be explicitly controlled
|
||||
with the "input-x11-keyboard" option. This is only a temporary
|
||||
measure until XEmbed is implemented and confirmed working.
|
||||
--- 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 is released ---
|
||||
1.3 - add MPV_MAKE_VERSION()
|
||||
1.2 - remove "stream-time-pos" property (no replacement)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// Plays a video from the command line in a window provided by mpv.
|
||||
// You likely want to play the video in your own window instead,
|
||||
// but that's not quite ready yet.
|
||||
// You may need a basic Info.plist and MainMenu.xib to make this work.
|
||||
|
||||
#include "../../libmpv/client.h"
|
||||
#include "shared.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
mpv_handle *mpv;
|
||||
dispatch_queue_t queue;
|
||||
}
|
||||
@end
|
||||
|
||||
static void wakeup(void *);
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
// Read filename
|
||||
NSArray *args = [NSProcessInfo processInfo].arguments;
|
||||
if (args.count < 2) {
|
||||
NSLog(@"Expected filename on command line");
|
||||
exit(1);
|
||||
}
|
||||
NSString *filename = args[1];
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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"));
|
||||
|
||||
check_error(mpv_initialize(mpv));
|
||||
|
||||
// Register to be woken up whenever mpv generates new events.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self);
|
||||
|
||||
// Load the indicated file
|
||||
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
|
||||
check_error(mpv_command(mpv, cmd));
|
||||
});
|
||||
}
|
||||
|
||||
- (void) handleEvent:(mpv_event *)event
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_SHUTDOWN:
|
||||
// Clean up and shut down.
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSApplication sharedApplication] terminate:nil];
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
}
|
||||
}
|
||||
|
||||
- (void) readEvents
|
||||
{
|
||||
dispatch_async(queue, ^{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
[self handleEvent:event];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void wakeup(void *context) {
|
||||
AppDelegate *a = (__bridge AppDelegate *) context;
|
||||
[a readEvents];
|
||||
}
|
||||
|
||||
// Ostensibly, mpv's window would be hooked up to this.
|
||||
- (BOOL) windowShouldClose:(id)sender
|
||||
{
|
||||
[self shutdown];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
if (mpv) {
|
||||
const char *args[] = {"quit", NULL};
|
||||
mpv_command(mpv, args);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
// Delete this if you already have a main.m.
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication new];
|
||||
AppDelegate *delegate = [AppDelegate new];
|
||||
app.delegate = delegate;
|
||||
[app run];
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -45,7 +45,6 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
mpv_container = new QWidget(this);
|
||||
setCentralWidget(mpv_container);
|
||||
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);
|
||||
|
||||
@@ -53,10 +52,6 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
// 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-x11-keyboard on the manpage.
|
||||
mpv_set_option_string(mpv, "input-x11-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);
|
||||
@@ -89,24 +84,6 @@ void MainWindow::handle_mpv_event(mpv_event *event)
|
||||
}
|
||||
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_SHUTDOWN: {
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
|
||||
@@ -21,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-x11-keyboard", "yes");
|
||||
int val = 1;
|
||||
check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));
|
||||
|
||||
|
||||
@@ -26,15 +26,6 @@ created scripts to help ease the process. These are the two recommended methods:
|
||||
Note that MinGW environments included in Linux distributions are often broken,
|
||||
outdated and useless, and usually don't use MinGW-w64.
|
||||
|
||||
Additional dependencies
|
||||
-----------------------
|
||||
|
||||
You need a pthread wrapper. It must be interoperable with native Windows
|
||||
threads. pthreads-win32 or MinGW pthreads might work.
|
||||
|
||||
If you want ``--input-file=...`` to work, you need libwaio. It's available
|
||||
from: git://midipix.org/waio
|
||||
|
||||
Example with MXE
|
||||
----------------
|
||||
|
||||
|
||||
+5
-6
@@ -77,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
|
||||
@@ -94,7 +92,7 @@ Available filters are:
|
||||
|
||||
``minchn=<n>``
|
||||
If the input channel number is less than ``<minchn>``, the filter will
|
||||
detach itself (default: 3).
|
||||
detach itself (default: 5).
|
||||
|
||||
``sweep[=speed]``
|
||||
Produces a sine sweep.
|
||||
@@ -265,8 +263,9 @@ Available filters are:
|
||||
Filter for internal use only. Converts between 24-bit and 32-bit sample
|
||||
formats.
|
||||
|
||||
``convertsign``
|
||||
Filter for internal use only. Converts between signed/unsigned formats.
|
||||
``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
|
||||
|
||||
@@ -181,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
|
||||
|
||||
+15
-69
@@ -159,9 +159,7 @@ List of Input Commands
|
||||
Take a single screenshot.
|
||||
<each-frame>
|
||||
Take a screenshot each frame. Issue this command again to stop taking
|
||||
screenshots. Note that you should disable frame-dropping when using
|
||||
this mode - or you might receive duplicate images in cases when a
|
||||
frame was dropped.
|
||||
screenshots.
|
||||
|
||||
``screenshot_to_file "<filename>" [subtitles|video|window]``
|
||||
Take a screenshot and save it to a given file. The format of the file will
|
||||
@@ -260,7 +258,7 @@ List of Input Commands
|
||||
exactly as in the ``quit`` command.
|
||||
|
||||
``sub_add "<file>"``
|
||||
Load the given subtitle file. It is selected as current subtitle after
|
||||
Load the given subtitle file. It is not selected as current subtitle after
|
||||
loading.
|
||||
|
||||
``sub_remove [<id>]``
|
||||
@@ -268,7 +266,7 @@ List of Input Commands
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
``sub_reload [<id>]``
|
||||
Reload the given subtitle tracks. If the ``id`` argument is missing, reload
|
||||
Reload the given subtitle tracks. If the ``id`` argument is missing, remove
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
This works by unloading and re-adding the subtitle track.
|
||||
@@ -327,12 +325,6 @@ List of Input Commands
|
||||
Write the resume config file that the ``quit_watch_later`` command writes,
|
||||
but continue playback normally.
|
||||
|
||||
``stop``
|
||||
Stop playback and clear playlist. With default settings, this is
|
||||
essentially like ``quit``. Useful for the client API: playback can be
|
||||
stopped without terminating the player.
|
||||
|
||||
|
||||
Input Commands that are Possibly Subject to Change
|
||||
--------------------------------------------------
|
||||
|
||||
@@ -509,8 +501,8 @@ Input Commands that are Possibly Subject to Change
|
||||
be used in input.conf to reassign such bindings.)
|
||||
|
||||
|
||||
Undocumented commands: ``tv_last_channel`` (TV/DVB only), ``get_property`` (?),
|
||||
``vo_cmdline`` (experimental).
|
||||
Undocumented commands: ``tv_last_channel`` (TV/DVB only), ``stop`` (questionable
|
||||
use), ``get_property`` (?), ``vo_cmdline`` (experimental).
|
||||
|
||||
Input Command Prefixes
|
||||
----------------------
|
||||
@@ -602,18 +594,6 @@ Property list
|
||||
``${stream-end}``. For ordered chapters and such, the
|
||||
size of the currently played segment is returned.)
|
||||
|
||||
``estimated-frame-count``
|
||||
Total number of frames in current file.
|
||||
|
||||
.. note:: This is only an estimate. (It's computed from two unreliable
|
||||
quantities: fps and stream length.)
|
||||
|
||||
``estimated-frame-number``
|
||||
Number of current frame in current stream.
|
||||
|
||||
.. note:: This is only an estimate. (It's computed from two unreliable
|
||||
quantities: fps and possibly rounded timestamps.)
|
||||
|
||||
``path``
|
||||
Full path of the currently played file.
|
||||
|
||||
@@ -655,12 +635,8 @@ Property list
|
||||
disabled.
|
||||
|
||||
``drop-frame-count``
|
||||
Frames dropped because they arrived too late. Doesn't necessarily indicate
|
||||
actual frame-drops, just the number of times the decoder was asked to drop.
|
||||
Unavailable if video is disabled
|
||||
|
||||
``vo-drop-frame-count``
|
||||
Frames dropped by VO (when using ``--framedrop=vo``).
|
||||
Frames dropped because they arrived to late. Unavailable if video
|
||||
is disabled
|
||||
|
||||
``percent-pos`` (RW)
|
||||
Position in current file (0-100). The advantage over using this instead of
|
||||
@@ -845,15 +821,6 @@ Property list
|
||||
Returns ``yes`` if the cache is idle, which means the cache is filled as
|
||||
much as possible, and is currently not reading more data.
|
||||
|
||||
``demuxer-cache-duration``
|
||||
Approximate duration of video buffered in the demuxer, in seconds. The
|
||||
guess is very unreliable, and often the property will not be available
|
||||
at all, even if data is buffered.
|
||||
|
||||
``demuxer-cache-idle``
|
||||
Returns ``yes`` if the demuxer is idle, which means the demuxer cache is
|
||||
filled to the requested amount, and is currently not reading more data.
|
||||
|
||||
``paused-for-cache``
|
||||
Returns ``yes`` when playback is paused because of waiting for the cache.
|
||||
|
||||
@@ -1040,17 +1007,13 @@ Property list
|
||||
``dwidth``, ``dheight``
|
||||
Video display size. This is the video size after filters and aspect scaling
|
||||
have been applied. The actual video window size can still be different
|
||||
from this, e.g. if the user resized the video window manually.
|
||||
|
||||
These have the same values as ``video-out-params/dw`` and
|
||||
``video-out-params/dh``.
|
||||
from this.
|
||||
|
||||
``video-out-params``
|
||||
Same as ``video-params``, but after video filters have been applied. If
|
||||
there are no video filters in use, this will contain the same values as
|
||||
``video-params``. Note that this is still not necessarily what the video
|
||||
window uses, since the user can change the window size, and all real VOs
|
||||
do their own scaling independently from the filter chain.
|
||||
window uses, since all real VOs do their own scaling.
|
||||
|
||||
Has the same sub-properties as ``video-params``.
|
||||
|
||||
@@ -1070,7 +1033,7 @@ Property list
|
||||
Window size multiplier. Setting this will resize the video window to the
|
||||
values contained in ``dwidth`` and ``dheight`` multiplied with the value
|
||||
set with this property. Setting ``1`` will resize to original video size
|
||||
(or to be exact, the size the video filters output). ``2`` will set the
|
||||
(or to be exactly, the size the video filters output). ``2`` will set the
|
||||
double size, ``0.5`` halves the size.
|
||||
|
||||
``video-aspect`` (RW)
|
||||
@@ -1271,6 +1234,11 @@ Property list
|
||||
"title" MPV_FORMAT_STRING
|
||||
"time" MPV_FORMAT_DOUBLE
|
||||
|
||||
``quvi-format`` (RW)
|
||||
See ``--quvi-format``. Cycling this property (``cycle``) will attempt to
|
||||
cycle through known format, although currently this feature doesn't work
|
||||
well at all.
|
||||
|
||||
``af`` (RW)
|
||||
See ``--af`` and the ``af`` command.
|
||||
|
||||
@@ -1296,28 +1264,6 @@ Property list
|
||||
``seekable``
|
||||
Return whether it's generally possible to seek in the current file.
|
||||
|
||||
``osd-sym-cc``
|
||||
Inserts the current OSD symbol as opaque OSD control code (cc). This makes
|
||||
sense only with the ``show_text`` command or options which set OSD messages.
|
||||
The control code is implementation specific and is useless for anything else.
|
||||
|
||||
``osd-ass-cc``
|
||||
``${osd-ass-cc/0}`` disables escaping ASS sequences of text in OSD,
|
||||
``${osd-ass-cc/1}`` enables it again. By default, ASS sequences are
|
||||
escaped to avoid accidental formatting, and this property can disable
|
||||
this behavior. Note that the properties return an opaque OSD control
|
||||
code, which only makes sense for the ``show_text`` command or options
|
||||
which set OSD messages.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
--osd-status-msg='This is ${osd-ass-cc/0}{\\b1}bold text'
|
||||
|
||||
Any ASS override tags as understood by libass can be used.
|
||||
|
||||
Note that you need to escape the ``\`` character, because the string is
|
||||
processed for C escape sequences before passing it to the OSD code.
|
||||
|
||||
``options/<name>`` (RW)
|
||||
Read-only access to value of option ``--<name>``. Most options can be
|
||||
changed at runtime by writing to this property. Note that many options
|
||||
|
||||
+3
-21
@@ -2,14 +2,9 @@ LUA SCRIPTING
|
||||
=============
|
||||
|
||||
mpv can load Lua scripts. Scripts in ``~/.mpv/lua/`` will be loaded on program
|
||||
start, or if passed to ``--lua``. mpv appends ``~/.mpv/lua`` to the end of
|
||||
lua's path so you can import them too. Since it's added to the end, don't name
|
||||
scripts you want to import the same as lua libraries because they will be
|
||||
overshadowed by them.
|
||||
|
||||
mpv provides the built-in module ``mp``, which provides functions to send
|
||||
commands to the mpv core and to retrieve information about playback state, user
|
||||
settings, file information, and so on.
|
||||
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.
|
||||
@@ -52,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
|
||||
------------
|
||||
|
||||
@@ -500,10 +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.
|
||||
|
||||
``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).
|
||||
|
||||
+1
-53
@@ -73,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
|
||||
@@ -98,9 +98,6 @@ m
|
||||
f
|
||||
Toggle fullscreen (see also ``--fs``).
|
||||
|
||||
ESC
|
||||
Exit fullscreen mode.
|
||||
|
||||
T
|
||||
Toggle stay-on-top (see also ``--ontop``).
|
||||
|
||||
@@ -148,10 +145,6 @@ 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
|
||||
Activate/deactivate deinterlacer.
|
||||
|
||||
@@ -411,51 +404,6 @@ taking screenshots is handled by the VOs, and adding the screenshot filter will
|
||||
break hardware decoding. (The filter may still be useful for taking screenshots
|
||||
at a certain point within the video chain when using multiple video filters.)
|
||||
|
||||
TERMINAL STATUS LINE
|
||||
====================
|
||||
|
||||
During playback, mpv shows the playback status on the terminal. It looks like
|
||||
something like this:
|
||||
|
||||
``AV: 00:03:12 / 00:24:25 (13%) A-V: -0.000``
|
||||
|
||||
The status line can be overridden with the ``--term-status-msg`` option.
|
||||
|
||||
The following is a list of things that can show up in the status line. Input
|
||||
properties, that can be used to get the same information manually, are also
|
||||
listed.
|
||||
|
||||
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
|
||||
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
|
||||
- The total file duration (or ``00:00:00`` if unknown) (``length`` property)
|
||||
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
|
||||
is the user-requested speed, and not the actual speed (usually they should
|
||||
be the same, unless playback is too slow). (``speed`` property.)
|
||||
- Playback percentage, e.g. ``(13%)``. How much of the file has been played.
|
||||
Normally calculated out of playback position and duration, but can fallback
|
||||
to other methods (like byte position) if these are not available.
|
||||
(``percent-pos`` property.)
|
||||
- The audio/video sync as ``A-V: 0.000``. This is the difference between
|
||||
audio and video time. Normally it should be 0 or close to 0. If it's growing,
|
||||
it might indicate a playback problem. (``avsync`` property.)
|
||||
- Total A/V sync change, e.g. ``ct: -0.417``. Normally invisible. Can show up
|
||||
if there is audio "missing", or not enough frames can be dropped. Usually
|
||||
this will indicate a problem. (``total-avsync-change`` property.)
|
||||
- Encoding state in ``{...}``, only shown in encoding mode.
|
||||
- Decoder-dropped video frames, e.g. ``SD: 2``. Shows up only if the count is
|
||||
not 0. Normally should never show up, unless ``--framedrop`` is set to enable
|
||||
this mode, and your CPU is too slow. (``drop-frame-count`` property.)
|
||||
- VO-dropped video frames, e.g. ``D: 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.)
|
||||
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
|
||||
The first value shows the amount of video buffered in the demuxer in seconds,
|
||||
the second value shows *additional* data buffered in the stream cache in
|
||||
kilobytes. (``demuxer-cache-duration`` and ``cached-used`` properties.)
|
||||
|
||||
|
||||
PROTOCOLS
|
||||
=========
|
||||
|
||||
|
||||
+75
-221
@@ -198,22 +198,26 @@ Playback Control
|
||||
(i.e. not with stdin, pipe, etc).
|
||||
|
||||
``--load-unsafe-playlists``
|
||||
Load URLs from playlists which are considered unsafe (default: no). This
|
||||
includes special protocols and anything that doesn't refer to normal files.
|
||||
Local files and HTTP links on the other hand are always considered safe.
|
||||
Normally, something like ``mpv playlist.m3u`` won't load the playlist. This
|
||||
is because the playlist code is unsafe. (This is the same in all other
|
||||
variations of MPlayer.)
|
||||
|
||||
Note that ``--playlist`` always loads all entries, so you use that instead
|
||||
if you really have the need for this functionality.
|
||||
See ``--playlist`` for details.
|
||||
|
||||
``--loop-file=<N|inf|no>``
|
||||
Loop a single file N times. ``inf`` means forever, ``no`` means normal
|
||||
playback. For compatibility, ``--loop-file`` and ``--loop-file=yes`` are
|
||||
also accepted, and are the same as ``--loop-file=inf``.
|
||||
Note: this option will allow opening playlists using the ``playlist``
|
||||
special demuxer. The ``--playlist`` uses different code, and supports more
|
||||
playlist formats than the playlist demuxer. This means that for now, the
|
||||
``--playlist`` option should always be used if you intend to open playlists.
|
||||
Background: the special demuxer contains newly written code, while the
|
||||
``--playlist`` option uses the old MPlayer code. Adding support for more
|
||||
playlist formats to the special demuxer is work in progress, and eventually
|
||||
the old code should disappear.
|
||||
|
||||
The difference to ``--loop`` is that this doesn't loop the playlist, just
|
||||
the file itself. If the playlist contains only a single file, the difference
|
||||
between the two option is that this option performs a seek on loop, instead
|
||||
of reloading the file.
|
||||
``--loop-file``
|
||||
Loop a single file. The difference to ``--loop=inf`` is that this doesn't
|
||||
loop the playlist, just the file itself. If the playlist contains only a
|
||||
single file, the difference between the two option is that this option
|
||||
performs a seek on loop, instead of reloading the file.
|
||||
|
||||
``--ordered-chapters``, ``--no-ordered-chapters``
|
||||
Enabled by default.
|
||||
@@ -426,45 +430,16 @@ Video
|
||||
Do not sleep when outputting video frames. Useful for benchmarks when used
|
||||
with ``--no-audio.``
|
||||
|
||||
``--framedrop=<mode>``
|
||||
Skip displaying some frames to maintain A/V sync on slow systems, or
|
||||
playing high framerate video on video outputs that have an upper framerate
|
||||
limit.
|
||||
``--framedrop=<no|yes>``
|
||||
Skip displaying some frames to maintain A/V sync on slow systems. Video
|
||||
filters are not applied to such frames. For B-frames even decoding is
|
||||
skipped completely. May produce unwatchably choppy output.
|
||||
|
||||
The argument selects the drop methods, and can be one of the following:
|
||||
|
||||
<no>
|
||||
Disable any framedropping.
|
||||
<vo>
|
||||
Drop late frames on video output (default). This still decodes and
|
||||
filters all frames, but doesn't render them on the VO. It tries to query
|
||||
the display FPS (X11 only, not correct on multi-monitor systems), or
|
||||
assumes infinite display FPS if that fails. Drops are indicated in
|
||||
the terminal status line as ``D:`` field. If the decoder is too slow,
|
||||
in theory all frames would have to be dropped (because all frames are
|
||||
too late) - to avoid this, frame dropping stops if the effective
|
||||
framerate is below 10 FPS.
|
||||
<decoder>
|
||||
Old, decoder-based framedrop mode. (This is the same as ``--framedrop=yes``
|
||||
in mpv 0.5.x and before.) This tells the decoder to skip frames (unless
|
||||
they are needed to decode future frames). May help with slow systems,
|
||||
but can produce unwatchably choppy output, or even freeze the display
|
||||
complete. Not recommended.
|
||||
The ``--vd-lavc-framedrop`` option controls what frames to drop.
|
||||
<decoder+vo>
|
||||
Enable both modes. Not recommended.
|
||||
The ``--vd-lavc-framedrop`` option controls what frames to drop.
|
||||
|
||||
.. note::
|
||||
|
||||
``--vo=vdpau`` has its own code for the ``vo`` framedrop mode. Slight
|
||||
differences to other VOs are possible.
|
||||
|
||||
``--display-fps=<fps>``
|
||||
Set the maximum assumed display FPS used with ``--framedrop``. By default
|
||||
a detected value is used (X11 only, not correct on multi-monitor systems),
|
||||
or infinite display FPS if that fails. Infinite FPS means only frames too
|
||||
late are dropped. If a correct FPS is provided, frames that are predicted
|
||||
to be too late are dropped too.
|
||||
Practical use of this feature is questionable. Disabled by default.
|
||||
|
||||
``--hwdec=<api>``
|
||||
Specify the hardware video decoding API that should be used if possible.
|
||||
@@ -478,7 +453,7 @@ Video
|
||||
:vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
|
||||
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
|
||||
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
|
||||
:vda: requires ``--vo=opengl`` (OS X only)
|
||||
:vda: requires ``--vo=opengl`` or ``--vo=corevideo`` (OS X only)
|
||||
|
||||
``auto`` tries to automatically enable hardware decoding using the first
|
||||
available method. This still depends what VO you are using. For example,
|
||||
@@ -556,22 +531,6 @@ Video
|
||||
which means the value ``0`` would rotate the video according to the
|
||||
rotation metadata.)
|
||||
|
||||
``--video-stereo-mode=<mode>``
|
||||
Set the stereo 3D output mode (default: ``mono``). This is done by inserting
|
||||
the ``stereo3d`` conversion filter.
|
||||
|
||||
The mode ``mono`` is an alias to ``ml``, which refers to the left frame in
|
||||
2D. This is the default, which means mpv will try to show 3D movies in 2D,
|
||||
instead of the mangled 3D image not intended for consumption (such as
|
||||
showing the left and right frame side by side, etc.).
|
||||
|
||||
The pseudo-mode ``none`` disables automatic conversion completely.
|
||||
|
||||
Use ``--video-stereo-mode=help`` to list all available modes. Check with
|
||||
the ``stereo3d`` filter documentation to see what the names mean. Note that
|
||||
some names refer to modes not supported by ``stereo3d`` - these modes can
|
||||
appear in files, but can't be handled properly by mpv.
|
||||
|
||||
``--video-zoom=<value>``
|
||||
Adjust the video display scale factor by the given value. The unit is in
|
||||
fractions of the (scaled) window video size.
|
||||
@@ -682,6 +641,11 @@ Video
|
||||
The following explanations are relevant:
|
||||
`<http://quvi.sourceforge.net/r/api/0.9/glossary_termino.html#m_stream_id>`_
|
||||
|
||||
The ``quvi-format`` property can be used at runtime to cycle through the
|
||||
list of formats. Unfortunately, this is slow. On libquvi 0.4.x, this
|
||||
functionality is limited to switching between ``best`` and ``default`` if
|
||||
the ``cycle`` input command is used.
|
||||
|
||||
``--vd-lavc-check-hw-profile=<yes|no>``
|
||||
Check hardware decoder profile (default: yes). If ``no`` is set, the
|
||||
highest profile of the hardware decoder is unconditionally selected, and
|
||||
@@ -976,6 +940,10 @@ Audio
|
||||
maximum amplification, i.e. amplify by 200%. The default volume (no
|
||||
change in volume) will be ``50`` in this case.
|
||||
|
||||
``--volstep=<0-100>``
|
||||
Set the step size of mixer volume changes in percent of the full range
|
||||
(default: 3).
|
||||
|
||||
``--volume-restore-data=<string>``
|
||||
Used internally for use by playback resume (e.g. with ``quit_watch_later``).
|
||||
Restoring value has to be done carefully, because different AOs as well as
|
||||
@@ -988,20 +956,6 @@ Audio
|
||||
|
||||
Do not use.
|
||||
|
||||
``--audio-buffer=<seconds>``
|
||||
Set the audio output minimum buffer. The audio device might actually create
|
||||
a larger buffer if it pleases. If the device creates a smaller buffer,
|
||||
additional audio is buffered in an additional software buffer.
|
||||
|
||||
Making this larger will make soft-volume and other filters react slower,
|
||||
introduce additional issues on playback speed change, and block the
|
||||
player on audio format changes. A smaller buffer might lead to audio
|
||||
dropouts.
|
||||
|
||||
This option should be used for testing only. If a non-default value helps
|
||||
significantly, the mpv developers should be contacted.
|
||||
|
||||
Default: 0.2 (200 ms).
|
||||
|
||||
Subtitles
|
||||
---------
|
||||
@@ -1368,13 +1322,6 @@ Subtitles
|
||||
Can be used to disable display of subtitles, but still select and decode
|
||||
them.
|
||||
|
||||
``--sub-clear-on-seek``
|
||||
(Obscure, rarely useful.) Can be used to play broken mkv files with
|
||||
duplicate ReadOrder fields. ReadOrder is the first field in a
|
||||
Matroska-style ASS subtitle packets. It should be unique, and libass
|
||||
uses it for fast elimination of duplicates. This option disables caching
|
||||
of subtitles across seeks, so after a seek libass can't eliminate subtitle
|
||||
packets with the same ReadOrder as earlier packets.
|
||||
|
||||
Window
|
||||
------
|
||||
@@ -1425,8 +1372,7 @@ Window
|
||||
See also ``--screen``.
|
||||
|
||||
``--keep-open``
|
||||
Do not terminate when playing or seeking beyond the end of the file, and
|
||||
there is not next file to be played (and ``--loop`` is not used).
|
||||
Do not terminate when playing or seeking beyond the end of the file.
|
||||
Instead, pause the player. When trying to seek beyond end of the file, the
|
||||
player will pause at an arbitrary playback position (or, in corner cases,
|
||||
not redraw the window at all).
|
||||
@@ -1439,12 +1385,6 @@ Window
|
||||
chapter will terminate playback as well, even if ``--keep-open`` is
|
||||
given.
|
||||
|
||||
Since mpv 0.6.0, this doesn't pause if there is a next file in the playlist,
|
||||
or the playlist is looped. Approximately, this will pause when the player
|
||||
would normally exit, but in practice there are corner cases in which this
|
||||
is not the case (e.g. ``mpv --keep-open file.mkv /dev/null`` will play
|
||||
file.mkv normally, then fail to open ``/dev/null``, then exit).
|
||||
|
||||
``--force-window``
|
||||
Create a video output window even if there is no video. This can be useful
|
||||
when pretending that mpv is a GUI application. Currently, the window
|
||||
@@ -1651,7 +1591,7 @@ Window
|
||||
.. note::
|
||||
|
||||
This does not affect the normal screensaver operation in any way.
|
||||
|
||||
|
||||
``--no-keepaspect``, ``--keepaspect``
|
||||
``--no-keepaspect`` will always stretch the video to window size, and will
|
||||
disable the window manager hints that force the window aspect ratio.
|
||||
@@ -2000,7 +1940,6 @@ Demuxer
|
||||
|
||||
``--demuxer-rawaudio-format=<value>``
|
||||
Sample format for ``--demuxer=rawaudio`` (default: s16le).
|
||||
Use ``--demuxer-rawaudio-format=help`` to get a list of all formats.
|
||||
|
||||
``--demuxer-rawaudio-rate=<value>``
|
||||
Sample rate for ``--demuxer=rawaudio`` (default: 44 kHz).
|
||||
@@ -2036,25 +1975,13 @@ Demuxer
|
||||
|
||||
``--demuxer-thread=<yes|no>``
|
||||
Run the demuxer in a separate thread, and let it prefetch a certain amount
|
||||
of packets (default: yes). Having this enabled may lead to smoother
|
||||
playback, but on the other hand can add delays to seeking or track
|
||||
switching.
|
||||
|
||||
``--demuxer-readahead-secs=N``
|
||||
If ``--demuxer-thread`` is enabled, this controls how much the demuxer
|
||||
should buffer ahead in seconds (default: 0.2). As long as no packet has
|
||||
a timestamp difference higher than the readahead amount relative to the
|
||||
last packet returned to the decoder, the demuxer keeps reading.
|
||||
|
||||
(This value tends to be fuzzy, because many file formats don't store linear
|
||||
timestamps.)
|
||||
of packets (default: no).
|
||||
|
||||
``--demuxer-readahead-packets=N``
|
||||
If ``--demuxer-thread`` is enabled, this controls how much the demuxer
|
||||
should buffer ahead. As long as the number of packets in the packet queue
|
||||
doesn't exceed ``--demuxer-readahead-packets``, and the total number of
|
||||
bytes doesn't exceed ``--demuxer-readahead-bytes``, the thread keeps
|
||||
reading ahead.
|
||||
should buffer ahead. If the number of packets in the packet queue exceeds
|
||||
``--demuxer-readahead-packets``, or the total number of bytes exceeds
|
||||
``--demuxer-readahead-bytes``, the thread stops reading ahead.
|
||||
|
||||
Note that if you set these options near the maximum, you might get a
|
||||
packet queue overflow warning.
|
||||
@@ -2158,24 +2085,6 @@ Input
|
||||
Use the right Alt key as Alt Gr to produce special characters. If disabled,
|
||||
count the right Alt as an Alt modifier key. Enabled by default.
|
||||
|
||||
``--input-x11-keyboard=<yes|no>``
|
||||
Disable all keyboard input on the X11 VO window. Generally useful for
|
||||
embedding only.
|
||||
|
||||
On X11, a sub-window with input enabled grabs all keyboard input as long
|
||||
as it is 1. a child of a focused window, and 2. the mouse is inside of
|
||||
the sub-window. The can steal away all keyboard input from the
|
||||
application embedding the mpv window, and on the other hand, the mpv
|
||||
window will receive no input if the mouse is outside of the mpv window,
|
||||
even though mpv has focus. Modern toolkits work around this weird X11
|
||||
behavior, but naively embedding foreign windows breaks it.
|
||||
|
||||
The only way to handle this reasonably is using the XEmbed protocol, which
|
||||
was designed to solve these problems. But Qt has questionable support, and
|
||||
mpv doesn't implement it yet.
|
||||
|
||||
As a workaround, this option is disabled by default in libmpv. (Note that
|
||||
``input-default-bindings`` is disabled by default in libmpv as well.)
|
||||
|
||||
OSD
|
||||
---
|
||||
@@ -2217,26 +2126,6 @@ OSD
|
||||
|
||||
Default: 45.
|
||||
|
||||
``--osd-msg1=<string>``
|
||||
Show this string as message on OSD with OSD level 1 (visible by default).
|
||||
The message will be visible by default, and as long no other message
|
||||
covers it, and the OSD level isn't changed (see ``--osd-level``).
|
||||
Expands properties; see `Property Expansion`_.
|
||||
|
||||
``--osd-msg2=<string>``
|
||||
Similar as ``--osd-msg1``, but for OSD level 2. If this is an empty string
|
||||
(default), then the playback time is shown.
|
||||
|
||||
``--osd-msg3=<string>``
|
||||
Similar as ``--osd-msg1``, but for OSD level 3. If this is an empty string
|
||||
(default), then the playback time, duration, and some more information is
|
||||
shown.
|
||||
|
||||
This is also used for the ``show_progress`` command (by default mapped to
|
||||
``P``), or in some non-default cases when seeking.
|
||||
|
||||
``--osd-status-msg`` is a legacy equivalent (but with a minor difference).
|
||||
|
||||
``--osd-status-msg=<string>``
|
||||
Show a custom string during playback instead of the standard status text.
|
||||
This overrides the status text used for ``--osd-level=3``, when using the
|
||||
@@ -2244,17 +2133,6 @@ OSD
|
||||
non-default cases when seeking. Expands properties. See
|
||||
`Property Expansion`_.
|
||||
|
||||
This option has been replaced with ``--osd-msg3``. The only difference is
|
||||
that this option implicitly includes ``${osd-sym-cc}``. This option is
|
||||
ignored if ``--osd-msg3`` is not empty.
|
||||
|
||||
``--osd-playing-msg=<string>``
|
||||
Show a message on OSD when playback starts. The string is expanded for
|
||||
properties, e.g. ``--osd-playing-msg='file: ${filename}'`` will show the
|
||||
message ``file:`` followed by a space and the currently played filename.
|
||||
|
||||
See `Property Expansion`_.
|
||||
|
||||
``--osd-bar-align-x=<-1-1>``
|
||||
Position of the OSD bar. -1 is far left, 0 is centered, 1 is far right.
|
||||
Fractional values (like 0.5) are allowed.
|
||||
@@ -2819,6 +2697,20 @@ Cache
|
||||
will not automatically enable the cache e.g. when playing from a network
|
||||
stream. Note that using ``--cache`` will always override this option.
|
||||
|
||||
``--cache-pause-below=<kBytes|no>``
|
||||
If the cache size goes below the specified value (in KB), pause and wait
|
||||
until the size set by ``--cache-pause-restart`` is reached, then resume
|
||||
playback (default: 50). If ``no`` is specified, this behavior is disabled.
|
||||
|
||||
When the player is paused this way, the status line shows ``Buffering``
|
||||
instead of ``Paused``, and the OSD uses a clock symbol instead of the
|
||||
normal paused symbol.
|
||||
|
||||
``--cache-pause-restart=<kBytes>``
|
||||
If the cache is paused due to the ``--cache-pause-below`` functionality,
|
||||
then the player unpauses as soon as the cache has this much data (in KB).
|
||||
(Default: 100)
|
||||
|
||||
``--cache-initial=<kBytes>``
|
||||
Playback will start when the cache has been filled up with this many
|
||||
kilobytes of data (default: 0).
|
||||
@@ -2835,40 +2727,22 @@ Cache
|
||||
on the situation, either of these might be slower than the other method.
|
||||
This option allows control over this.
|
||||
|
||||
``--cache-file=<TMP|path>``
|
||||
Create a cache file on the filesystem.
|
||||
``--cache-file=<path>``
|
||||
Create a cache file on the filesystem with the given name. The file is
|
||||
always overwritten. When the general cache is enabled, this file cache
|
||||
will be used to store whatever is read from the source stream.
|
||||
|
||||
There are two ways of using this:
|
||||
This will always overwrite the cache file, and you can't use an existing
|
||||
cache file to resume playback of a stream. (Technically, mpv wouldn't
|
||||
even know which blocks in the file are valid and which not.)
|
||||
|
||||
1. Passing a path (a filename). The file will always be overwritten. When
|
||||
the general cache is enabled, this file cache will be used to store
|
||||
whatever is read from the source stream.
|
||||
The resulting file will not necessarily contain all data of the source
|
||||
stream. For example, if you seek, the parts that were skipped over are
|
||||
never read and consequently are not written to the cache. The skipped over
|
||||
parts are filled with zeros. This means that the cache file doesn't
|
||||
necessarily correspond to a full download of the source stream.
|
||||
|
||||
This will always overwrite the cache file, and you can't use an existing
|
||||
cache file to resume playback of a stream. (Technically, mpv wouldn't
|
||||
even know which blocks in the file are valid and which not.)
|
||||
|
||||
The resulting file will not necessarily contain all data of the source
|
||||
stream. For example, if you seek, the parts that were skipped over are
|
||||
never read and consequently are not written to the cache. The skipped over
|
||||
parts are filled with zeros. This means that the cache file doesn't
|
||||
necessarily correspond to a full download of the source stream.
|
||||
|
||||
Both of these issues could be improved if there is any user interest.
|
||||
|
||||
.. warning:: Causes random corruption when used with ordered chapters or
|
||||
with ``--audio-file``.
|
||||
|
||||
2. Passing the string ``TMP``. This will not be interpreted as filename.
|
||||
Instead, an invisible temporary file is created. It depends on your
|
||||
C library where this file is created (usually ``/tmp/``), and whether
|
||||
filename is visible (the ``tmpfile()`` function is used). On some
|
||||
systems, automatic deletion of the cache file might not be guaranteed.
|
||||
|
||||
If you want to use a file cache, this mode is recommended, because it
|
||||
doesn't break ordered chapters or ``--audio-file``. These modes open
|
||||
multiple cache streams, and using the same file for them obviously
|
||||
clashes.
|
||||
Both of these issues could be improved if there is any user interest.
|
||||
|
||||
Also see ``--cache-file-size``.
|
||||
|
||||
@@ -2876,24 +2750,11 @@ Cache
|
||||
Maximum size of the file created with ``--cache-file``. For read accesses
|
||||
above this size, the cache is simply not used.
|
||||
|
||||
Keep in mind that some use-cases, like playing ordered chapters with cache
|
||||
enabled, will actually create multiple cache files, each of which will
|
||||
use up to this much disk space.
|
||||
|
||||
(Default: 1048576, 1 GB.)
|
||||
|
||||
``--no-cache``
|
||||
Turn off input stream caching. See ``--cache``.
|
||||
|
||||
``--cache-secs=<seconds>``
|
||||
How many seconds of audio/video to prefetch if the cache is active. This
|
||||
overrides the ``--demuxer-readahead-secs`` option if and only if the cache
|
||||
is enabled. (Default: 2.)
|
||||
|
||||
``--cache-pause``, ``--no-cache-pause``
|
||||
Whether the player should automatically pause when the cache runs low,
|
||||
and unpause once more data is available ("buffering").
|
||||
|
||||
|
||||
Network
|
||||
-------
|
||||
@@ -2902,9 +2763,11 @@ Network
|
||||
Use ``<string>`` as user agent for HTTP streaming.
|
||||
|
||||
``--cookies``, ``--no-cookies``
|
||||
(network only)
|
||||
Support cookies when making HTTP requests. Disabled by default.
|
||||
|
||||
``--cookies-file=<filename>``
|
||||
(network only)
|
||||
Read HTTP cookies from <filename>. The file is assumed to be in Netscape
|
||||
format.
|
||||
|
||||
@@ -2944,17 +2807,6 @@ Network
|
||||
network transport when playing ``rtsp://...`` URLs. The value ``lavf``
|
||||
leaves the decision to libavformat.
|
||||
|
||||
``--hls-bitrate=<no|min|max>``
|
||||
If HLS streams are played, this option controls what streams are selected
|
||||
by default. The option allows the following parameters:
|
||||
|
||||
:no: Don't do anything special. Typically, this will simply pick the
|
||||
first audio/video streams it can find. (Default.)
|
||||
:min: Pick the streams with the lowest bitrate.
|
||||
:max: Same, but highest bitrate.
|
||||
|
||||
The bitrate as used is sent by the server, and there's no guarantee it's
|
||||
actually meaningful.
|
||||
|
||||
DVB
|
||||
---
|
||||
@@ -3089,11 +2941,6 @@ Miscellaneous
|
||||
You can also try to use ``--no-correct-pts`` for files with completely
|
||||
broken timestamps.
|
||||
|
||||
``--media-title=<string>``
|
||||
Force the contents of the ``media-title`` property to this value. Useful
|
||||
for scripts which want to set a title, without overriding the user's
|
||||
setting in ``--title``.
|
||||
|
||||
``--slave-broken``
|
||||
Switches on the old slave mode. This is for testing only, and incompatible
|
||||
to the removed ``--slave`` switch.
|
||||
@@ -3111,3 +2958,10 @@ Miscellaneous
|
||||
if there is enough interest).
|
||||
|
||||
This affects most third-party GUI frontends.
|
||||
|
||||
``--softsleep``
|
||||
Time frames by repeatedly checking the current time instead of asking
|
||||
the kernel to wake up mpv at the correct time. Useful if your kernel
|
||||
timing is imprecise and you cannot use the RTC either. Comes at the
|
||||
price of higher CPU consumption.
|
||||
|
||||
+8
-14
@@ -231,23 +231,12 @@ Available filters are:
|
||||
``<fmt>``
|
||||
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
|
||||
|
||||
``pp=[filter string]``
|
||||
``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.
|
||||
|
||||
.. note::
|
||||
|
||||
Unlike in MPlayer or in earlier versions, you must quote the pp string
|
||||
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
This filter is most likely useless on modern HD video. It might be
|
||||
helpful with old low-resolution files.
|
||||
|
||||
All subfilters share common options to determine their scope:
|
||||
interchangeably, i.e. ``dr``/``dering`` are the same. All subfilters share
|
||||
common options to determine their scope:
|
||||
|
||||
``a/autoq``
|
||||
Automatically switch the subfilter off if the CPU is too slow.
|
||||
@@ -262,6 +251,11 @@ Available filters are:
|
||||
|
||||
``--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]]``
|
||||
|
||||
+8
-36
@@ -259,6 +259,13 @@ Available video output drivers are:
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``corevideo`` (Mac OS X 10.6 and later)
|
||||
Mac OS X CoreVideo video output driver. Uses the CoreVideo APIs to fill
|
||||
PixelBuffers and generate OpenGL textures from them (useful as a fallback
|
||||
for ``opengl``).
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``opengl``
|
||||
OpenGL video output driver. It supports extended scaling methods, dithering
|
||||
and color management.
|
||||
@@ -290,9 +297,6 @@ Available video output drivers are:
|
||||
``lanczos3``
|
||||
Lanczos with radius=3.
|
||||
|
||||
``lanczos``
|
||||
Generic Lanczos scaling filter. Set radius with ``lradius``.
|
||||
|
||||
``spline36``
|
||||
This is the default when using ``opengl-hq``.
|
||||
|
||||
@@ -312,14 +316,6 @@ Available video output drivers are:
|
||||
Mitchell-Netravali. The ``b`` and ``c`` parameters can be set with
|
||||
``lparam1`` and ``lparam2``. Both are set to 1/3 by default.
|
||||
|
||||
``gaussian``
|
||||
Gaussian filter with a parameter ``p`` for sharpness control.
|
||||
``p`` can be set to float number between 1(blurry) and 100(sharp)
|
||||
and has a default value of about 28.8 (see ``lparam1``).
|
||||
|
||||
Note that for extremely small value of ``p``, a large filter radius
|
||||
might be required to avoid unintended artifacts (see ``lradius``).
|
||||
|
||||
|
||||
There are some more filters. For a complete list, pass ``help`` as
|
||||
value, e.g.::
|
||||
@@ -333,16 +329,6 @@ Available video output drivers are:
|
||||
``lparam2=<value>``
|
||||
See ``lparam1``.
|
||||
|
||||
``lradius=<r>``
|
||||
Set radius for filters listed below, must be a float number between 1.0
|
||||
and 8.0. Defaults to be 2.0 if not specified.
|
||||
|
||||
``sinc``, ``lanczos``, ``blackman``, ``gaussian``
|
||||
|
||||
Note that depending on filter implementation details and video scaling
|
||||
ratio, the radius that actually being used might be different
|
||||
(most likely being increased a bit).
|
||||
|
||||
``scaler-resizes-only``
|
||||
Disable the scaler if the video image is not resized. In that case,
|
||||
``bilinear`` is used instead whatever is set with ``lscale``. Bilinear
|
||||
@@ -444,11 +430,6 @@ Available video output drivers are:
|
||||
RGB. If chroma is not subsampled, this option is ignored, and the
|
||||
luma scaler is used instead. Setting this option is often useless.
|
||||
|
||||
``cparam1``, ``cparam2``, ``cradius``
|
||||
Set filter parameters and radius for ``cscale``.
|
||||
|
||||
See ``lparam1``, ``lparam2`` and ``lradius``.
|
||||
|
||||
``fancy-downscaling``
|
||||
When using convolution based filters, extend the filter size
|
||||
when downscaling. Trades quality for reduced downscaling performance.
|
||||
@@ -458,16 +439,7 @@ Available video output drivers are:
|
||||
Borders will be distorted due to filtering.
|
||||
|
||||
``glfinish``
|
||||
Call ``glFinish()`` before and after swapping buffers (default: disabled).
|
||||
Slower, but might help getting better results when doing framedropping.
|
||||
The details depend entirely on the OpenGL driver.
|
||||
|
||||
``waitvsync``
|
||||
Call ``glXWaitVideoSyncSGI`` after each buffer swap (default: disabled).
|
||||
This may or may not help with video timing accuracy and frame drop. It's
|
||||
possible that this makes video output slower, or has no effect at all.
|
||||
|
||||
X11 only.
|
||||
Call ``glFinish()`` before swapping buffers
|
||||
|
||||
``sw``
|
||||
Continue even if a software renderer is detected.
|
||||
|
||||
+17
-35
@@ -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".
|
||||
|
||||
@@ -116,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.
|
||||
|
||||
@@ -124,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
|
||||
@@ -135,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
|
||||
@@ -194,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.
|
||||
@@ -216,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.
|
||||
@@ -234,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
|
||||
|
||||
+134
-56
@@ -1,68 +1,146 @@
|
||||
Release 0.6.0
|
||||
Release 0.5.2
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Map ESC to exiting fullscreen.
|
||||
- Run the vo inside its own thread.
|
||||
- Enable the demuxer thread by default.
|
||||
- Prefer the opengl vo over the vdpau one.
|
||||
- Complete profile names in the zsh completion script.
|
||||
- Raise dsound ao buffer size to 200ms and make it configurable.
|
||||
- Bind ctrl+c to quit, so that it works both in the terminal and the player
|
||||
window.
|
||||
- Automatically load playlists without needing --load-unsafe-playlists or
|
||||
--playlist. Unsafe URLs are still rejected (e.g. remote playlists can't load
|
||||
local files).
|
||||
- Remove quvi-format property (which never really worked).
|
||||
- Wait until all Lua scripts are loaded on start.
|
||||
- Use separate threads for input sources (terminal, lirc, joystick) instead of
|
||||
using a central I/O loop.
|
||||
- Complete values for options that take file names in the zsh completion script.
|
||||
- Use libwaio for pipe input on Windows (if you want --input-file to work on
|
||||
Windows you need libwaio as an additional dependency).
|
||||
- Remove the --volstep option (users should change the argument to the "add"
|
||||
function in input.conf instead).
|
||||
- Always show the OSD message when changing volume, gamma, panscan, etc along
|
||||
with the OSD bar.
|
||||
- When changing a property always show it on the OSD (except for "pause" and
|
||||
"fullscreen").
|
||||
- Prefer the libavcodec decoder over libmpg123 for mp3 decoding (ffmpeg/libav
|
||||
support for gapless audio works better for mpv's use-case).
|
||||
- Allow passing number of loops to --loop-file.
|
||||
- Change --keep-open option semantics to not terminate the player when playing
|
||||
or seeking past the end of the file and there's no next file to be played.
|
||||
- Disable the sndio ao by default and prefer other outputs even if it's enabled.
|
||||
- Inherit event flags from parent X11 window when using --wid.
|
||||
- Properly resume playlists loaded at runtime (this makes mpv try to resume a
|
||||
playlist when loaded like "mpv playlist.m3u").
|
||||
- Don't honor $XDG_CONFIG_DIRS anymore for global paths (this fixes a regression
|
||||
on systems that set the variable by default to somewhere else then /etc,
|
||||
making mpv skip previously created config files).
|
||||
- Allow up to 256 MB of extradata to make broken files work.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Restore old speed change behavior (this fixes a temporary lack of sound after
|
||||
restoring normal speed e.g. when pressing Backspace).
|
||||
- Fix quit handling when seeking past EOF while paused.
|
||||
- Prevent burning CPU when seeking while paused.
|
||||
- Don't add the playlist base path twice when using --playlist.
|
||||
- Fix a crash when the initialization of the EGL context fails on Wayland.
|
||||
- Make EOF handling properly event-based (this fixes a bug that made mpv hang
|
||||
when changing from one file to the next with --gapless-audio=no).
|
||||
- Assume icy title data is terminated with ';' (this fixes handling of streams
|
||||
with metadata containing apostrophes).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 0f1d6c0..e3d3a47`` in
|
||||
the git repository.
|
||||
|
||||
Release 0.5.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Add audio/flac to the MIME type list in mpv.desktop.
|
||||
- The --slave-broken option has been reintroduced and is equivalent to using
|
||||
--input-file=/dev/stdin and --input-terminal=no.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fix screenshots with anamorphic videos in the vdpau vo.
|
||||
- Fix a crash happening when mpv fails to create a vdpau mixer in the vdpau vo.
|
||||
- Don't quit when the --loop-file option is used.
|
||||
- Always load the cursor theme in the wayland vo (this fixes a crash caused by
|
||||
moving the mouse cursor over the video).
|
||||
- Properly return non-zero from the zsh completion script when no matches are
|
||||
found (this fixes the use of user-defined zsh completion matchers).
|
||||
- Fix the description of the opengl-hq vo default options in the manpage.
|
||||
- Remove duplicated content from the manpage that was accidentally added in the
|
||||
manpage reorganization from the previous release.
|
||||
- Avoid clobbering playback position on video end (when both audio and video
|
||||
streams reached the end, the playback poisition was briefly set at 0).
|
||||
- Avoid overwriting user input in the coreaudio_device ao.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 6729e39..fdd648b`` in
|
||||
the git repository.
|
||||
|
||||
Release 0.5.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Support setting the start time relative to the start PTS with --start.
|
||||
- Use /dev/sr0 as default cdrom/dvd device on Linux.
|
||||
- Change "cache" property representation to float.
|
||||
- Split the ao_coreaudio audio output into ao_coreaudio (which uses the
|
||||
AudioUnits and the CoreAudio sound server) and ao_coreaudio_device (which uses
|
||||
direct device access).
|
||||
- Complete supported URL schemes in the zsh completion script.
|
||||
- Enable compiler optimizations (-O2) during build.
|
||||
- Allow precise seeking with percent-based seeks too.
|
||||
- Do precise seeks when clicking on the OSC seekbar.
|
||||
- Remove support for older VDA hwaccel versions on Mac OS X (requires recent
|
||||
versions of ffmpeg/libav).
|
||||
- Remove the last remains of slave mode (--slave-broken option). Support for
|
||||
reading input commands from stdin can be emulated using: --input-terminal=no
|
||||
--input-file=/dev/stdin.
|
||||
- Some key binding were remapped or removed:
|
||||
+ "LEFT" and "RIGHT" have been remapped to seek 5 seconds instead of 10.
|
||||
+ "c" for changing the YUV color space was unmapped.
|
||||
+ OSD level cycling has been remapped from "o" to "O".
|
||||
+ "o" is now mapped to the show_progress command (like "P").
|
||||
+ "PGUP" and "PGDWN" have been remapped to chapter-based seeks (the old
|
||||
bindings "@" and "!" are still available). The old behvaiour has been
|
||||
remapped to "Shift+PGUP" and "Shift+PGDWM".
|
||||
+ Increasing/decreasing audio delay have been remapped from "+" and "-" to
|
||||
"Ctrl++" and "Ctrl+-".
|
||||
+ "(" and ")" for adjusting audio balance have been unmapped.
|
||||
+ "F" for toggling the "sub-forced-only" option has been unmapped.
|
||||
+ "TAB" for cycling TS programs has been unmapped.
|
||||
+ "A" for cycling DVD angle has been unmapped.
|
||||
+ "U" for stopping playback has been unmapped.
|
||||
+ "l" and "L" for cycling quvi format have been unmapped.
|
||||
- Make it possible to build the client API (libmpv) on Mac OS X.
|
||||
- Do necessary checks for nested Wayland compositors which only have limited
|
||||
interfaces.
|
||||
- The options in the manpage have been re-organized into sections and ordered by
|
||||
usage to make it easier to navigate.
|
||||
- The "hard" framedrop mode has been removed (this behaviour is still available
|
||||
via the --vd-lavc-framedrop option).
|
||||
- Show the basename of a subtitle file only (without the directory part).
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Use a default color space for files with no video (this fixes color managed
|
||||
OSD messages when no video channel is present).
|
||||
- Fix key equivalent dispatching for Cocoa on Mac OS X.
|
||||
- Disable non-interleaved audio formats by default with ao_alsa, since some
|
||||
ALSA plugins don't cope well with them (non-interleaved formats can still be
|
||||
enabled using the "non-interleaved" ao_alsa option).
|
||||
- Fix deadlock in the client API when calling mpv_terminate_destroy() before
|
||||
mpv_initialize().
|
||||
- Fix flushing of filter chain (this fixes a bug where the last frames would get
|
||||
dropped on EOF, if buffered by filters).
|
||||
- Actually use the channels.conf configuration file for DVB.
|
||||
- Restore the --dvbin-file option.
|
||||
- Fix off by one error in the command parser which caused a segfault when using
|
||||
the mp.commandv() Lua function with 10 or more arguments.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Add new framedrop mode that limits the frequency of new video frames displayed
|
||||
based on the display framerate (this is enabled by default, the old
|
||||
decoder-based mode can be enabled with --framedrop=decoder).
|
||||
- Add --demuxer-readahead-secs option to control the readahead buffer using a
|
||||
duration value.
|
||||
- Add estimated-frame-count and estimated-frame-number properties.
|
||||
- Add --cache-secs option which overrides the value of --demuxer-readahead-secs
|
||||
if the stream cache is active.
|
||||
- Add demuxer-cache-duration and demuxer-cache-idle properties.
|
||||
- Make --cache-file create a temporary file if the string "TMP" is passed
|
||||
instead of a file name.
|
||||
- Add support for Matroska 3D (if a file is marked as 3D using the StereoMode
|
||||
element, an automatic conversion filter is added).
|
||||
- Add --osd-playing-msg option to show a message on the OSD when the playback
|
||||
starts.
|
||||
- Add --media-title option to force the content of the media-title property.
|
||||
- Add --osd-msg1, --osd-msg2 and --osd-msg3 options to override the OSD message
|
||||
for the corresponding OSD level (before only level 3 could be overridden with
|
||||
--osd-status-message).
|
||||
- Add support for digital passthrough in the pulse ao.
|
||||
- Add support for opening DVD and Bluray directories directly.
|
||||
- Add --input-x11-keyboard option to control whether keyboard input should be
|
||||
enabled on the X11 window ot not (this is disabled by default when using the
|
||||
libmpv library).
|
||||
- Search for config files in $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS in addition
|
||||
to ~/.mpv. The config file has also been renamed to mpv.conf (although the
|
||||
old file still works and will be loaded even if the new one exists).
|
||||
- Add "playback-time" property that returns the current playback time always
|
||||
starting at 0. This is now used to show the playback time in the OSD and the
|
||||
status line, fixing the shown playback time for transport streams that start
|
||||
at a completely random timestamp.
|
||||
- Add cache-related "cache-free", "cache-used" and "cache-idle" properties.
|
||||
- Add option to list supported protocols (--list-protocols).
|
||||
- Allow setting libavformat stream options with the --stream-lavf-o option.
|
||||
- Support running the demuxer in a separate thread and prefetch packets (this
|
||||
can be enabled using the --demuxer-thread option).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log a1be3cf..61ebe61`` in
|
||||
complete change log can be viewed by running ``git log cb250d4..52f2f67`` in
|
||||
the git repository.
|
||||
@@ -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" ;
|
||||
|
||||
+53
-54
@@ -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,66 +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 IFS= read -r out; do
|
||||
tff1=${out##* TFF:}; tff1=${tff1%% *}
|
||||
bff1=${out##* BFF:}; bff1=${bff1%% *}
|
||||
progressive1=${out##* Progressive:}; progressive1=${progressive1%% *}
|
||||
undetermined1=${out##* Undetermined:}; undetermined1=${undetermined1%% *}
|
||||
case "$tff1$bff1$progressive1$undetermined1" in
|
||||
*[!0-9]*)
|
||||
printf >&2 'ERROR: Unrecognized idet output: %s\n' "$out"
|
||||
exit 16
|
||||
;;
|
||||
esac
|
||||
tff=$((tff + tff1))
|
||||
bff=$((bff + bff1))
|
||||
progressive=$((progressive + progressive1))
|
||||
undetermined=$((undetermined + undetermined1))
|
||||
done <<EOF
|
||||
$(testfun "$@")
|
||||
EOF
|
||||
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
|
||||
@@ -95,7 +94,7 @@ 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)
|
||||
@@ -105,14 +104,14 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$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=yadif --field-dominance=top
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -124,14 +123,14 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$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=yadif --field-dominance=bottom
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -143,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
|
||||
|
||||
@@ -265,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'),
|
||||
|
||||
+150
-127
@@ -17,134 +17,157 @@
|
||||
# When multiple files were specified, their info will be put into FOO_* for the
|
||||
# first file, FOO_1_* for the second file, FOO_2_* for the third file, etc.
|
||||
|
||||
__midentify__main() {
|
||||
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
[ -n "$1" ] && set -- '' "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
cat >&2 <<EOF
|
||||
Usage 1 (for humans only): $0 filename.mkv
|
||||
will print all property values.
|
||||
Note that this output really shouldn't be parsed, as the
|
||||
format is subject to change.
|
||||
|
||||
Usage 2 (for use by scripts): see top of this file
|
||||
|
||||
NOTE: for mkv with ordered chapters, this may
|
||||
not always identify the specified file, but the
|
||||
file providing the first chapter. Specify
|
||||
--no-ordered-chapters to prevent this.
|
||||
EOF
|
||||
return 2
|
||||
fi
|
||||
|
||||
local LF="
|
||||
"
|
||||
|
||||
local nextprefix="$1"
|
||||
shift
|
||||
|
||||
if [ -n "$nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
unset "${nextprefix}path"
|
||||
fi
|
||||
|
||||
local allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
video-aspect
|
||||
fps
|
||||
width
|
||||
height
|
||||
dwidth
|
||||
dheight
|
||||
|
||||
sub
|
||||
"
|
||||
# TODO add metadata support once mpv can do it
|
||||
|
||||
local propstr="X-MIDENTIFY-START:$LF"
|
||||
local key
|
||||
for key in $allprops; do
|
||||
propstr="${propstr}X-MIDENTIFY: $key \${=$key}$LF"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
|
||||
local fileindex=0
|
||||
local prefix=
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
X-MIDENTIFY-START:)
|
||||
if [ -n "$nextprefix" ]; then
|
||||
prefix="$nextprefix"
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
nextprefix="${prefix%${fileindex}_}"
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
nextprefix="${nextprefix}${fileindex}_"
|
||||
for key in $allprops; do
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
else
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
printf '\n'
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
fi
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
case "$1" in
|
||||
'')
|
||||
;;
|
||||
X-MIDENTIFY:\ *)
|
||||
local key="${line#X-MIDENTIFY: }"
|
||||
local value="${key#* }"
|
||||
key="${key%% *}"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
if [ -n "$nextprefix" ]; then
|
||||
if [ -z "$prefix" ]; then
|
||||
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
|
||||
elif [ -n "$value" ]; then
|
||||
eval "$prefix$key"='"$value"'
|
||||
fi
|
||||
else
|
||||
if [ -n "$value" ]; then
|
||||
printf '%s=%s\n' "$key" "$value"
|
||||
fi
|
||||
fi
|
||||
*)
|
||||
set -- '' "$@"
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$(${MPV:-mpv} --term-playing-msg="$propstr" --vo=null --ao=null \
|
||||
--frames=1 --quiet --no-cache --no-config -- "$@")
|
||||
EOF
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
__midentify__main "$@"
|
||||
if [ $# -lt 2 ]; then
|
||||
echo >&2 "Usage 1 (for humans only): $0 filename.mkv"
|
||||
echo >&2 "will print all property values."
|
||||
echo >&2 "Note that this output really shouldn't be parsed, as the"
|
||||
echo >&2 "format is subject to change."
|
||||
echo >&2
|
||||
echo >&2 "Usage 2 (for use by scripts): see top of this file"
|
||||
echo >&2
|
||||
echo >&2 "NOTE: for mkv with ordered chapters, this may"
|
||||
echo >&2 "not always identify the specified file, but the"
|
||||
echo >&2 "file providing the first chapter. Specify"
|
||||
echo >&2 "--no-ordered-chapters to prevent this."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MPV" ]; then
|
||||
MPV="mpv"
|
||||
fi
|
||||
|
||||
__midentify__LF="
|
||||
"
|
||||
|
||||
__midentify__nextprefix=$1
|
||||
shift
|
||||
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
eval unset $__midentify__nextprefix'path'
|
||||
fi
|
||||
|
||||
__midentify__allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
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
|
||||
+18
-31
@@ -18,11 +18,10 @@ e.g.:
|
||||
|
||||
Currently, the following event types are supported:
|
||||
|
||||
'start' <name> start of the named event
|
||||
'end' <name> end of the named event
|
||||
'value' <float> <name> a normal value (as opposed to event)
|
||||
'event-timed' <ts> <name> singular event at the given timestamp
|
||||
<name> singular event
|
||||
'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
|
||||
|
||||
"""
|
||||
|
||||
@@ -30,32 +29,19 @@ 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"
|
||||
e.numid = len(G.events)
|
||||
e.type = "unknown"
|
||||
G.sevents = list(G.events.values())
|
||||
G.sevents.sort(key=lambda x: x.name)
|
||||
if e.type == "event-signal":
|
||||
e.marker = find_marker()
|
||||
return G.events[event]
|
||||
|
||||
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
||||
@@ -68,29 +54,30 @@ for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
||||
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) / 1000 - G.start
|
||||
e = get_event(name, "event-signal")
|
||||
e.vals.append((val, 1))
|
||||
else:
|
||||
e = get_event(event, "event-signal")
|
||||
e = get_event(event)
|
||||
e.type = "event-signal"
|
||||
e.vals.append((ts, 1))
|
||||
|
||||
ao_events = ["ao fill", "audio wait"]
|
||||
for e in G.sevents:
|
||||
e.vals = [(x, y * e.numid / len(G.events)) for (x, y) in e.vals]
|
||||
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)
|
||||
@@ -102,7 +89,7 @@ for e in G.sevents:
|
||||
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")
|
||||
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])
|
||||
|
||||
+20
-15
@@ -23,11 +23,10 @@ Note that you can control the mpv instance by writing to the command fifo:
|
||||
|
||||
echo "cycle fullscreen" > /tmp/umpv-fifo-*
|
||||
|
||||
Note: you can supply custom 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:
|
||||
|
||||
@@ -46,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):
|
||||
@@ -65,6 +68,9 @@ def make_abs(filename):
|
||||
return filename
|
||||
files = [make_abs(f) for f in files]
|
||||
|
||||
opts_env = os.getenv("UMPV_OPTIONS")
|
||||
opts_env = opts_env.split() if opts_env else []
|
||||
|
||||
FIFO = "/tmp/umpv-fifo-" + os.getenv("USER")
|
||||
|
||||
fifo_fd = -1
|
||||
@@ -83,8 +89,7 @@ if fifo_fd >= 0:
|
||||
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())):
|
||||
sys.stderr.write("error: command FIFO is not a FIFO or has bogus "
|
||||
"permissions\n")
|
||||
print("error: command FIFO is not a FIFO or has bogus permissions")
|
||||
sys.exit(1)
|
||||
|
||||
if fifo_fd >= 0:
|
||||
@@ -107,11 +112,11 @@ else:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
os.mkfifo(FIFO, 0o600)
|
||||
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)
|
||||
@@ -90,7 +90,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (argc != 1 && argc != 2)
|
||||
return -1;
|
||||
|
||||
framestep_data_t *framestep = calloc(1,sizeof(framestep_data_t));
|
||||
framestep_data_t *framestep = malloc(sizeof(framestep_data_t));
|
||||
memset(framestep, 0, sizeof(*framestep));
|
||||
|
||||
framestep->step = atoi(argv[0]);
|
||||
framestep->pos = (argc >= 2) ? atoi(argv[1]) : 0;
|
||||
|
||||
@@ -267,7 +267,8 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
ildetect_data_t *il = calloc(1,sizeof(ildetect_data_t));
|
||||
ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
|
||||
memset(il, 0, sizeof(*il));
|
||||
|
||||
#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
|
||||
il->method = A(0, 0);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -10,13 +10,14 @@ video_url="$(youtube-dl \
|
||||
--user-agent="$user_agent" \
|
||||
--cookies="$cookies_file" \
|
||||
--get-url \
|
||||
"$@")"
|
||||
"$1")"
|
||||
|
||||
set -f
|
||||
${MPV:-mpv} \
|
||||
shift
|
||||
|
||||
mpv \
|
||||
--cookies \
|
||||
--cookies-file="$cookies_file" \
|
||||
--user-agent="$user_agent" \
|
||||
-- $video_url
|
||||
"$@" -- $video_url
|
||||
|
||||
rm -rf "$cookies_dir"
|
||||
@@ -37,36 +37,6 @@ chomp $vf_str;
|
||||
$protos_str .= qq{$_ } foreach (@protos);
|
||||
chomp $protos_str;
|
||||
|
||||
my $profile_comp = <<'EOS';
|
||||
local -a profiles
|
||||
local current
|
||||
for current in "${(@f)$($words[1] --profile=help)}"; do
|
||||
current=${current//\*/\\\*}
|
||||
current=${current//\:/\\\:}
|
||||
current=${current//\[/\\\[}
|
||||
current=${current//\]/\\\]}
|
||||
if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
|
||||
if [[ -n $match[2] ]]; then
|
||||
current="$match[1][$match[2]]"
|
||||
else
|
||||
current="$match[1]"
|
||||
fi
|
||||
profiles=($profiles $current)
|
||||
fi
|
||||
done
|
||||
if [[ $state == profile ]]; then
|
||||
# For --show-profile, only one allowed
|
||||
if (( ${#profiles} > 0 )); then
|
||||
_values 'profile' $profiles && rc=0
|
||||
fi
|
||||
else
|
||||
# For --profile, multiple allowed
|
||||
profiles=($profiles 'help[list profiles]')
|
||||
_values -s , 'profile(s)' $profiles && rc=0
|
||||
fi
|
||||
EOS
|
||||
chomp $profile_comp;
|
||||
|
||||
my $tmpl = <<"EOS";
|
||||
#compdef mpv
|
||||
|
||||
@@ -118,16 +88,6 @@ $vf_str
|
||||
_describe -t values 'video filters' values && rc=0
|
||||
;;
|
||||
|
||||
profile|profiles)
|
||||
$profile_comp
|
||||
;;
|
||||
|
||||
files)
|
||||
compset -P '*,'
|
||||
compset -S ',*'
|
||||
_files -r ',/ \\t\\n\\-' && rc=0
|
||||
;;
|
||||
|
||||
mfiles)
|
||||
local expl
|
||||
_tags files urls
|
||||
@@ -181,9 +141,6 @@ sub parse_opts {
|
||||
$entry .= '->vo' if ($1 eq '--vo');
|
||||
$entry .= '->af' if ($1 eq '--af');
|
||||
$entry .= '->vf' if ($1 eq '--vf');
|
||||
$entry .= '->profiles' if ($1 eq '--profile');
|
||||
$entry .= '->profile' if ($1 eq '--show-profile');
|
||||
$entry .= '->files' if ($line =~ /\[file\]/);
|
||||
}
|
||||
|
||||
push @list, $entry
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
#define MP_NUM_CHANNELS 8
|
||||
|
||||
|
||||
+98
-7
@@ -37,6 +37,8 @@
|
||||
#include "ad.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
#include "compat/libav.h"
|
||||
|
||||
struct priv {
|
||||
AVCodecContext *avctx;
|
||||
AVFrame *avframe;
|
||||
@@ -71,6 +73,74 @@ const struct m_sub_options ad_lavc_conf = {
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -102,6 +172,18 @@ static void set_data_from_avframe(struct dec_audio *da)
|
||||
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;
|
||||
@@ -114,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) {
|
||||
@@ -144,13 +232,12 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
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
|
||||
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,
|
||||
@@ -171,6 +258,8 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -273,6 +362,8 @@ static int decode_packet(struct dec_audio *da)
|
||||
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 = {
|
||||
|
||||
@@ -117,13 +117,13 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
switch (stream->codec->codec_id) {
|
||||
case AV_CODEC_ID_AAC:
|
||||
spdif_ctx->iec61937_packet_size = 16384;
|
||||
sample_format = AF_FORMAT_S_AAC;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_AC3:
|
||||
spdif_ctx->iec61937_packet_size = 6144;
|
||||
sample_format = AF_FORMAT_S_AC3;
|
||||
sample_format = AF_FORMAT_AC3_LE;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
@@ -131,31 +131,31 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
if (da->opts->dtshd) {
|
||||
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_S_DTSHD;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
samplerate = 192000;
|
||||
num_channels = 2*4;
|
||||
} else {
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_S_DTS;
|
||||
sample_format = AF_FORMAT_AC3_LE;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
}
|
||||
break;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
spdif_ctx->iec61937_packet_size = 24576;
|
||||
sample_format = AF_FORMAT_S_EAC3;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
samplerate = 192000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_MP3:
|
||||
spdif_ctx->iec61937_packet_size = 4608;
|
||||
sample_format = AF_FORMAT_S_MP3;
|
||||
sample_format = AF_FORMAT_MPEG2;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_TRUEHD:
|
||||
spdif_ctx->iec61937_packet_size = 61440;
|
||||
sample_format = AF_FORMAT_S_TRUEHD;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
samplerate = 192000;
|
||||
num_channels = 8;
|
||||
break;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#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"
|
||||
@@ -48,10 +48,10 @@ extern const struct ad_functions ad_lavc;
|
||||
extern const struct ad_functions ad_spdif;
|
||||
|
||||
static const struct ad_functions * const ad_drivers[] = {
|
||||
&ad_lavc,
|
||||
#if HAVE_MPG123
|
||||
&ad_mpg123,
|
||||
#endif
|
||||
&ad_lavc,
|
||||
&ad_spdif,
|
||||
NULL
|
||||
};
|
||||
|
||||
+49
-20
@@ -48,12 +48,20 @@ static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
|
||||
}
|
||||
|
||||
FILTER(f, float)
|
||||
FILTER(s32, int32_t)
|
||||
FILTER(u32, uint32_t)
|
||||
FILTER(s24, bs2b_int24_t)
|
||||
FILTER(u24, bs2b_uint24_t)
|
||||
FILTER(s16, int16_t)
|
||||
FILTER(u16, uint16_t)
|
||||
FILTER(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)
|
||||
|
||||
@@ -77,26 +85,47 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
/* check for formats supported by libbs2b
|
||||
and assign corresponding handlers */
|
||||
switch (format) {
|
||||
case AF_FORMAT_FLOAT:
|
||||
af->filter = filter_f;
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
af->filter = filter_fbe;
|
||||
break;
|
||||
case AF_FORMAT_S32:
|
||||
af->filter = filter_s32;
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
af->filter = filter_fle;
|
||||
break;
|
||||
case AF_FORMAT_U32:
|
||||
af->filter = filter_u32;
|
||||
case AF_FORMAT_S32_BE:
|
||||
af->filter = filter_s32be;
|
||||
break;
|
||||
case AF_FORMAT_S24:
|
||||
af->filter = filter_s24;
|
||||
case AF_FORMAT_U32_BE:
|
||||
af->filter = filter_u32be;
|
||||
break;
|
||||
case AF_FORMAT_U24:
|
||||
af->filter = filter_u24;
|
||||
case AF_FORMAT_S32_LE:
|
||||
af->filter = filter_s32le;
|
||||
break;
|
||||
case AF_FORMAT_S16:
|
||||
af->filter = filter_s16;
|
||||
case AF_FORMAT_U32_LE:
|
||||
af->filter = filter_u32le;
|
||||
break;
|
||||
case AF_FORMAT_U16:
|
||||
af->filter = filter_u16;
|
||||
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;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "af.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
|
||||
@@ -20,16 +20,21 @@
|
||||
|
||||
#include "af.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/endian.h"
|
||||
#include "compat/mpbswap.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
if ((src_format & AF_FORMAT_PLANAR) ||
|
||||
(dst_format & AF_FORMAT_PLANAR))
|
||||
return false;
|
||||
if (((src_format & ~AF_FORMAT_SIGN_MASK) ==
|
||||
(dst_format & ~AF_FORMAT_SIGN_MASK)) &&
|
||||
((src_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_I))
|
||||
int src_noend = src_format & ~AF_FORMAT_END_MASK;
|
||||
int dst_noend = dst_format & ~AF_FORMAT_END_MASK;
|
||||
// We can swap endian for all formats, but sign only for integer formats.
|
||||
if (src_noend == dst_noend)
|
||||
return true;
|
||||
if (((src_noend & ~AF_FORMAT_SIGN_MASK) ==
|
||||
(dst_noend & ~AF_FORMAT_SIGN_MASK)) &&
|
||||
((src_noend & AF_FORMAT_POINT_MASK) == AF_FORMAT_I))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -58,11 +63,34 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static void si2us(void *data, int len, int bps)
|
||||
static void endian(void *data, int len, int bps)
|
||||
{
|
||||
switch (bps) {
|
||||
case 2:
|
||||
for (int i = 0; i < len; i++) {
|
||||
((uint16_t*)data)[i] = bswap_16(((uint16_t *)data)[i]);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for(int i = 0; i < len; i++) {
|
||||
uint8_t s = ((uint8_t *)data)[3 * i];
|
||||
((uint8_t *)data)[3 * i] = ((uint8_t *)data)[3 * i + 2];
|
||||
((uint8_t *)data)[3 * i + 2] = s;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for(int i = 0; i < len; i++) {
|
||||
((uint32_t*)data)[i] = bswap_32(((uint32_t *)data)[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void si2us(void *data, int len, int bps, bool le)
|
||||
{
|
||||
ptrdiff_t i = -(len * bps);
|
||||
uint8_t *p = &((uint8_t *)data)[len * bps];
|
||||
if (BYTE_ORDER == LITTLE_ENDIAN && bps > 1)
|
||||
if (le && bps > 1)
|
||||
p += bps - 1;
|
||||
if (len <= 0)
|
||||
return;
|
||||
@@ -77,8 +105,12 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
int outfmt = af->data->format;
|
||||
size_t len = data->samples * data->nch;
|
||||
|
||||
if ((infmt & AF_FORMAT_END_MASK) != (outfmt & AF_FORMAT_END_MASK))
|
||||
endian(data->planes[0], len, data->bps);
|
||||
|
||||
if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK))
|
||||
si2us(data->planes[0], len, data->bps);
|
||||
si2us(data->planes[0], len, data->bps,
|
||||
(outfmt & AF_FORMAT_END_MASK) == AF_FORMAT_LE);
|
||||
|
||||
mp_audio_set_format(data, outfmt);
|
||||
return 0;
|
||||
@@ -92,8 +124,8 @@ static int af_open(struct af_instance *af)
|
||||
}
|
||||
|
||||
const struct af_info af_info_convertsignendian = {
|
||||
.info = "Convert between sample format sign",
|
||||
.name = "convertsign",
|
||||
.info = "Convert between sample format sign/endian",
|
||||
.name = "convertsignendian",
|
||||
.open = af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
+17
-35
@@ -284,27 +284,6 @@ static inline void update_ch(af_hrtf_t *s, short *in, const int k)
|
||||
s->ba_r[k] = in[4] + in[1] + in[3];
|
||||
}
|
||||
|
||||
static void clear_coeff(af_hrtf_t *s, float *c)
|
||||
{
|
||||
memset(c, 0, s->dlbuflen * sizeof(float));
|
||||
}
|
||||
|
||||
static void reset(af_hrtf_t *s)
|
||||
{
|
||||
clear_coeff(s, s->lf);
|
||||
clear_coeff(s, s->rf);
|
||||
clear_coeff(s, s->lr);
|
||||
clear_coeff(s, s->rr);
|
||||
clear_coeff(s, s->cf);
|
||||
clear_coeff(s, s->cr);
|
||||
clear_coeff(s, s->ba_l);
|
||||
clear_coeff(s, s->ba_r);
|
||||
clear_coeff(s, s->fwrbuf_l);
|
||||
clear_coeff(s, s->fwrbuf_r);
|
||||
clear_coeff(s, s->fwrbuf_lr);
|
||||
clear_coeff(s, s->fwrbuf_rr);
|
||||
}
|
||||
|
||||
/* Initialization and runtime control */
|
||||
static int control(struct af_instance *af, int cmd, void* arg)
|
||||
{
|
||||
@@ -313,27 +292,30 @@ static int control(struct af_instance *af, int cmd, void* arg)
|
||||
|
||||
switch(cmd) {
|
||||
case AF_CONTROL_REINIT:
|
||||
reset(s);
|
||||
af->data->rate = 48000;
|
||||
mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch);
|
||||
if(af->data->nch == 2) {
|
||||
/* 2 channel input */
|
||||
if(s->decode_mode != HRTF_MIX_MATRIX2CH) {
|
||||
/* Default behavior is stereo mixing. */
|
||||
s->decode_mode = HRTF_MIX_STEREO;
|
||||
}
|
||||
} else if (af->data->nch < 5) {
|
||||
mp_audio_set_channels_old(af->data, 5);
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
if(af->data->rate != 48000) {
|
||||
// automatic samplerate adjustment in the filter chain
|
||||
// is not yet supported.
|
||||
MP_ERR(af, "ERROR: Sampling rate is not 48000 Hz (%d)!\n",
|
||||
af->data->rate);
|
||||
return AF_ERROR;
|
||||
}
|
||||
mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch);
|
||||
if(af->data->nch == 2) {
|
||||
/* 2 channel input */
|
||||
if(s->decode_mode != HRTF_MIX_MATRIX2CH) {
|
||||
/* Default behavior is stereo mixing. */
|
||||
s->decode_mode = HRTF_MIX_STEREO;
|
||||
}
|
||||
}
|
||||
else if (af->data->nch < 5)
|
||||
mp_audio_set_channels_old(af->data, 5);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
test_output_res = af_test_output(af, (struct mp_audio*)arg);
|
||||
// after testing input set the real output format
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
s->print_flag = 1;
|
||||
return test_output_res;
|
||||
case AF_CONTROL_RESET:
|
||||
reset(s);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
return AF_UNKNOWN;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <libavutil/audioconvert.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/bswap.h>
|
||||
#include <libavutil/mem.h>
|
||||
|
||||
#include "common/common.h"
|
||||
@@ -88,7 +87,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
if (in->nch > AC3_MAX_CHANNELS)
|
||||
mp_audio_set_num_channels(in, AC3_MAX_CHANNELS);
|
||||
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S_AC3);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
|
||||
if (!mp_audio_config_equals(in, &orig_in))
|
||||
@@ -151,12 +150,6 @@ static void uninit(struct af_instance* af)
|
||||
}
|
||||
}
|
||||
|
||||
static void swap_16(uint16_t *ptr, size_t size)
|
||||
{
|
||||
for (size_t n = 0; n < size; n++)
|
||||
ptr[n] = av_bswap16(ptr[n]);
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
{
|
||||
@@ -230,11 +223,11 @@ static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
frame_size = AC3_FRAME_SIZE * 2 * 2;
|
||||
header_len = 8;
|
||||
|
||||
AV_WL16(hdr, 0xF872); // iec 61937 syncword 1
|
||||
AV_WL16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
AV_WB16(hdr, 0xF872); // iec 61937 syncword 1
|
||||
AV_WB16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
hdr[4] = bsmod; // bsmod
|
||||
hdr[5] = 0x01; // data-type ac3
|
||||
AV_WL16(hdr + 6, len << 3); // number of bits in payload
|
||||
AV_WB16(hdr + 6, len << 3); // number of bits in payload
|
||||
}
|
||||
|
||||
size_t max_size = (max_out_samples - out->samples) * out->sstride;
|
||||
@@ -246,7 +239,6 @@ static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
memcpy(buf + header_len, s->pkt.data, s->pkt.size);
|
||||
memset(buf + header_len + s->pkt.size, 0,
|
||||
frame_size - (header_len + s->pkt.size));
|
||||
swap_16((uint16_t *)(buf + header_len), s->pkt.size / 2);
|
||||
out->samples += frame_size / out->sstride;
|
||||
}
|
||||
|
||||
@@ -320,13 +312,12 @@ const struct af_info af_info_lavcac3enc = {
|
||||
.priv_size = sizeof(struct af_ac3enc_s),
|
||||
.priv_defaults = &(const struct af_ac3enc_s){
|
||||
.cfg_add_iec61937_header = 1,
|
||||
.cfg_bit_rate = 640,
|
||||
.cfg_min_channel_num = 3,
|
||||
.cfg_min_channel_num = 5,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLAG("tospdif", cfg_add_iec61937_header, 0),
|
||||
OPT_CHOICE_OR_INT("bitrate", cfg_bit_rate, 0, 32, 640,
|
||||
({"auto", 0}, {"default", 0})),
|
||||
({"default", 0})),
|
||||
OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6),
|
||||
{0}
|
||||
},
|
||||
|
||||
@@ -214,7 +214,8 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
if (avresample_open(s->avrctx) < 0 ||
|
||||
avresample_open(s->avrctx_out) < 0)
|
||||
{
|
||||
MP_ERR(af, "Cannot open Libavresample Context. \n");
|
||||
MP_ERR(af, "Cannot open "
|
||||
"Libavresample Context. \n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
return AF_OK;
|
||||
@@ -338,7 +339,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
if (needs_reorder(s->reorder_out, out->nch)) {
|
||||
if (af_fmt_is_planar(out->format)) {
|
||||
reorder_planes(data, s->reorder_out);
|
||||
} else if (out->samples) {
|
||||
} else {
|
||||
int out_size = out->samples * out->sstride;
|
||||
if (talloc_get_size(s->reorder_buffer) < out_size)
|
||||
s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
|
||||
@@ -346,8 +347,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
int out_samples = avresample_convert(s->avrctx_out,
|
||||
(uint8_t **) data->planes, out_size, out->samples,
|
||||
(uint8_t **) out->planes, out_size, out->samples);
|
||||
if (out_samples < 0)
|
||||
MP_ERR(af, "Reordering failed.\n");
|
||||
assert(out_samples == data->samples);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +371,8 @@ static int af_open(struct af_instance *af)
|
||||
if (s->avrctx && s->avrctx_out) {
|
||||
return AF_OK;
|
||||
} else {
|
||||
MP_ERR(af, "Cannot initialize Libavresample Context. \n");
|
||||
MP_ERR(af, "Cannot initialize "
|
||||
"Libavresample Context. \n");
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
+38
-26
@@ -30,6 +30,9 @@
|
||||
|
||||
int af_fmt2bps(int format)
|
||||
{
|
||||
if (AF_FORMAT_IS_AC3(format)) return 2;
|
||||
if (format == AF_FORMAT_UNKNOWN)
|
||||
return 0;
|
||||
switch (format & AF_FORMAT_BITS_MASK) {
|
||||
case AF_FORMAT_8BIT: return 1;
|
||||
case AF_FORMAT_16BIT: return 2;
|
||||
@@ -59,7 +62,7 @@ static int bits_to_mask(int bits)
|
||||
|
||||
int af_fmt_change_bits(int format, int bits)
|
||||
{
|
||||
if (!af_fmt_is_valid(format))
|
||||
if (!af_fmt_is_valid(format) || (format & AF_FORMAT_SPECIAL_MASK))
|
||||
return 0;
|
||||
int mask = bits_to_mask(bits);
|
||||
format = (format & ~AF_FORMAT_BITS_MASK) | mask;
|
||||
@@ -106,31 +109,35 @@ bool af_fmt_is_planar(int format)
|
||||
return !!(format & AF_FORMAT_PLANAR);
|
||||
}
|
||||
|
||||
#define FMT(string, id) \
|
||||
{string, id},
|
||||
|
||||
#define FMT_ENDIAN(string, id) \
|
||||
{string, id}, \
|
||||
{string "le", MP_CONCAT(id, _LE)}, \
|
||||
{string "be", MP_CONCAT(id, _BE)}, \
|
||||
|
||||
const struct af_fmt_entry af_fmtstr_table[] = {
|
||||
{"u8", AF_FORMAT_U8},
|
||||
{"s8", AF_FORMAT_S8},
|
||||
{"u16", AF_FORMAT_U16},
|
||||
{"s16", AF_FORMAT_S16},
|
||||
{"u24", AF_FORMAT_U24},
|
||||
{"s24", AF_FORMAT_S24},
|
||||
{"u32", AF_FORMAT_U32},
|
||||
{"s32", AF_FORMAT_S32},
|
||||
{"float", AF_FORMAT_FLOAT},
|
||||
{"double", AF_FORMAT_DOUBLE},
|
||||
FMT("mpeg2", AF_FORMAT_MPEG2)
|
||||
FMT_ENDIAN("ac3", AF_FORMAT_AC3)
|
||||
FMT_ENDIAN("iec61937", AF_FORMAT_IEC61937)
|
||||
|
||||
{"u8p", AF_FORMAT_U8P},
|
||||
{"s16p", AF_FORMAT_S16P},
|
||||
{"s32p", AF_FORMAT_S32P},
|
||||
{"floatp", AF_FORMAT_FLOATP},
|
||||
{"doublep", AF_FORMAT_DOUBLEP},
|
||||
FMT("u8", AF_FORMAT_U8)
|
||||
FMT("s8", AF_FORMAT_S8)
|
||||
FMT_ENDIAN("u16", AF_FORMAT_U16)
|
||||
FMT_ENDIAN("s16", AF_FORMAT_S16)
|
||||
FMT_ENDIAN("u24", AF_FORMAT_U24)
|
||||
FMT_ENDIAN("s24", AF_FORMAT_S24)
|
||||
FMT_ENDIAN("u32", AF_FORMAT_U32)
|
||||
FMT_ENDIAN("s32", AF_FORMAT_S32)
|
||||
FMT_ENDIAN("float", AF_FORMAT_FLOAT)
|
||||
FMT_ENDIAN("double", AF_FORMAT_DOUBLE)
|
||||
|
||||
{"spdif-aac", AF_FORMAT_S_AAC},
|
||||
{"spdif-ac3", AF_FORMAT_S_AC3},
|
||||
{"spdif-dts", AF_FORMAT_S_DTS},
|
||||
{"spdif-dtshd", AF_FORMAT_S_DTSHD},
|
||||
{"spdif-eac3", AF_FORMAT_S_EAC3},
|
||||
{"spdif-mp3", AF_FORMAT_S_MP3},
|
||||
{"spdif-truehd",AF_FORMAT_S_TRUEHD},
|
||||
FMT("u8p", AF_FORMAT_U8P)
|
||||
FMT("s16p", AF_FORMAT_S16P)
|
||||
FMT("s32p", AF_FORMAT_S32P)
|
||||
FMT("floatp", AF_FORMAT_FLOATP)
|
||||
FMT("doublep", AF_FORMAT_DOUBLEP)
|
||||
|
||||
{0}
|
||||
};
|
||||
@@ -192,17 +199,22 @@ int af_format_conversion_score(int dst_format, int src_format)
|
||||
return INT_MIN;
|
||||
if (dst_format == src_format)
|
||||
return 1024;
|
||||
// Just endian swapping (separate because it works for special formats)
|
||||
if ((dst_format & ~AF_FORMAT_END_MASK) == (src_format & ~AF_FORMAT_END_MASK))
|
||||
return 1024 - 2;
|
||||
// Can't be normally converted
|
||||
if (AF_FORMAT_IS_SPECIAL(dst_format) || AF_FORMAT_IS_SPECIAL(src_format))
|
||||
return INT_MIN;
|
||||
int score = 1024;
|
||||
if (FMT_DIFF(AF_FORMAT_INTERLEAVING_MASK, dst_format, src_format))
|
||||
score -= 1; // has to (de-)planarize
|
||||
if (FMT_DIFF(AF_FORMAT_END_MASK, dst_format, src_format))
|
||||
score -= 2; // has to swap endian
|
||||
if (FMT_DIFF(AF_FORMAT_SIGN_MASK, dst_format, src_format))
|
||||
score -= 4; // has to swap sign
|
||||
if (FMT_DIFF(AF_FORMAT_TYPE_MASK, dst_format, src_format)) {
|
||||
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format)) {
|
||||
int dst_bits = dst_format & AF_FORMAT_BITS_MASK;
|
||||
if ((dst_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F) {
|
||||
if ((dst_format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
|
||||
// For int->float, always prefer 32 bit float.
|
||||
score -= dst_bits == AF_FORMAT_32BIT ? 8 : 0;
|
||||
} else {
|
||||
@@ -218,7 +230,7 @@ int af_format_conversion_score(int dst_format, int src_format)
|
||||
}
|
||||
}
|
||||
// Consider this the worst case.
|
||||
if (FMT_DIFF(AF_FORMAT_TYPE_MASK, dst_format, src_format))
|
||||
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format))
|
||||
score -= 2048; // has to convert float<->int
|
||||
return score;
|
||||
}
|
||||
+91
-47
@@ -25,77 +25,121 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "osdep/endian.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define AF_SELECT_LE_BE(LE, BE) BE
|
||||
#else
|
||||
#define AF_SELECT_LE_BE(LE, BE) LE
|
||||
#endif
|
||||
|
||||
// Endianness
|
||||
#define AF_FORMAT_BE (0<<0) // Big Endian
|
||||
#define AF_FORMAT_LE (1<<0) // Little Endian
|
||||
#define AF_FORMAT_END_MASK (1<<0)
|
||||
|
||||
#define AF_FORMAT_NE AF_SELECT_LE_BE(AF_FORMAT_LE, AF_FORMAT_BE)
|
||||
|
||||
// Signed/unsigned
|
||||
#define AF_FORMAT_SI (0<<0) // Signed
|
||||
#define AF_FORMAT_US (1<<0) // Unsigned
|
||||
#define AF_FORMAT_SIGN_MASK (1<<0)
|
||||
#define AF_FORMAT_SI (0<<1) // Signed
|
||||
#define AF_FORMAT_US (1<<1) // Unsigned
|
||||
#define AF_FORMAT_SIGN_MASK (1<<1)
|
||||
|
||||
// Bits used
|
||||
// Some code assumes they're sorted by size.
|
||||
#define AF_FORMAT_8BIT (0<<1)
|
||||
#define AF_FORMAT_16BIT (1<<1)
|
||||
#define AF_FORMAT_24BIT (2<<1)
|
||||
#define AF_FORMAT_32BIT (3<<1)
|
||||
#define AF_FORMAT_64BIT (4<<1)
|
||||
#define AF_FORMAT_BITS_MASK (7<<1)
|
||||
#define AF_FORMAT_8BIT (0<<3)
|
||||
#define AF_FORMAT_16BIT (1<<3)
|
||||
#define AF_FORMAT_24BIT (2<<3)
|
||||
#define AF_FORMAT_32BIT (3<<3)
|
||||
#define AF_FORMAT_64BIT (4<<3)
|
||||
#define AF_FORMAT_BITS_MASK (7<<3)
|
||||
|
||||
// Fixed/floating point/special
|
||||
#define AF_FORMAT_I (1<<4) // Int
|
||||
#define AF_FORMAT_F (2<<4) // Foating point
|
||||
#define AF_FORMAT_S (4<<4) // special (IEC61937)
|
||||
#define AF_FORMAT_TYPE_MASK (7<<4)
|
||||
// Special flags refering to non pcm data (note: 1<<6, 2<<6, 5<<6 unused)
|
||||
#define AF_FORMAT_S_MPEG2 (3<<6) // MPEG(2) audio
|
||||
#define AF_FORMAT_S_AC3 (4<<6) // Dolby Digital AC3
|
||||
#define AF_FORMAT_S_IEC61937 (6<<6)
|
||||
#define AF_FORMAT_SPECIAL_MASK (7<<6)
|
||||
|
||||
// Fixed or floating point
|
||||
#define AF_FORMAT_I (1<<9) // Int
|
||||
#define AF_FORMAT_F (2<<9) // Foating point
|
||||
#define AF_FORMAT_POINT_MASK (3<<9)
|
||||
|
||||
// Interleaving (planar formats have data for each channel in separate planes)
|
||||
#define AF_FORMAT_INTERLEAVED (0<<7) // must be 0
|
||||
#define AF_FORMAT_PLANAR (1<<7)
|
||||
#define AF_FORMAT_INTERLEAVING_MASK (1<<7)
|
||||
|
||||
#define AF_FORMAT_S_CODEC(n) ((n)<<8)
|
||||
#define AF_FORMAT_S_CODEC_MASK (15 <<8) // 16 codecs max.
|
||||
#define AF_FORMAT_INTERLEAVED (0<<11) // must be 0
|
||||
#define AF_FORMAT_PLANAR (1<<11)
|
||||
#define AF_FORMAT_INTERLEAVING_MASK (1<<11)
|
||||
|
||||
#define AF_FORMAT_MASK ((1<<12)-1)
|
||||
|
||||
#define AF_INTP (AF_FORMAT_I|AF_FORMAT_PLANAR)
|
||||
#define AF_FLTP (AF_FORMAT_F|AF_FORMAT_PLANAR)
|
||||
#define AF_FORMAT_S_(n) (AF_FORMAT_S_CODEC(n)|AF_FORMAT_S|AF_FORMAT_16BIT)
|
||||
|
||||
// actual sample formats
|
||||
enum af_format {
|
||||
AF_FORMAT_UNKNOWN = 0,
|
||||
|
||||
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_U16 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S16 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_U24 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_S24 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_U32 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_S32 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_U16_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U16_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S16_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S16_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_U24_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U24_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S24_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S24_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_U32_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U32_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S32_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S32_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_FLOAT = (AF_FORMAT_F|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLE = (AF_FORMAT_F|AF_FORMAT_64BIT),
|
||||
AF_FORMAT_FLOAT_LE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_FLOAT_BE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_DOUBLE_LE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_DOUBLE_BE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_AC3_LE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_AC3_BE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_IEC61937_LE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_IEC61937_BE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_MPEG2 = (AF_FORMAT_S_MPEG2),
|
||||
|
||||
// Planar variants
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT),
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT|AF_FORMAT_NE),
|
||||
|
||||
// All of these use IEC61937 framing, and otherwise pretend to be like PCM.
|
||||
AF_FORMAT_S_AAC = AF_FORMAT_S_(0),
|
||||
AF_FORMAT_S_AC3 = AF_FORMAT_S_(1),
|
||||
AF_FORMAT_S_DTS = AF_FORMAT_S_(2),
|
||||
AF_FORMAT_S_DTSHD = AF_FORMAT_S_(3),
|
||||
AF_FORMAT_S_EAC3 = AF_FORMAT_S_(4),
|
||||
AF_FORMAT_S_MP3 = AF_FORMAT_S_(5),
|
||||
AF_FORMAT_S_TRUEHD = AF_FORMAT_S_(6),
|
||||
// Native endian variants
|
||||
AF_FORMAT_U16 = AF_SELECT_LE_BE(AF_FORMAT_U16_LE, AF_FORMAT_U16_BE),
|
||||
AF_FORMAT_S16 = AF_SELECT_LE_BE(AF_FORMAT_S16_LE, AF_FORMAT_S16_BE),
|
||||
AF_FORMAT_U24 = AF_SELECT_LE_BE(AF_FORMAT_U24_LE, AF_FORMAT_U24_BE),
|
||||
AF_FORMAT_S24 = AF_SELECT_LE_BE(AF_FORMAT_S24_LE, AF_FORMAT_S24_BE),
|
||||
AF_FORMAT_U32 = AF_SELECT_LE_BE(AF_FORMAT_U32_LE, AF_FORMAT_U32_BE),
|
||||
AF_FORMAT_S32 = AF_SELECT_LE_BE(AF_FORMAT_S32_LE, AF_FORMAT_S32_BE),
|
||||
|
||||
AF_FORMAT_FLOAT = AF_SELECT_LE_BE(AF_FORMAT_FLOAT_LE, AF_FORMAT_FLOAT_BE),
|
||||
AF_FORMAT_DOUBLE = AF_SELECT_LE_BE(AF_FORMAT_DOUBLE_LE, AF_FORMAT_DOUBLE_BE),
|
||||
|
||||
AF_FORMAT_AC3 = AF_SELECT_LE_BE(AF_FORMAT_AC3_LE, AF_FORMAT_AC3_BE),
|
||||
AF_FORMAT_IEC61937 = AF_SELECT_LE_BE(AF_FORMAT_IEC61937_LE, AF_FORMAT_IEC61937_BE),
|
||||
};
|
||||
|
||||
#define AF_FORMAT_IS_IEC61937(f) (((f) & AF_FORMAT_TYPE_MASK) == AF_FORMAT_S)
|
||||
#define AF_FORMAT_IS_SPECIAL(f) AF_FORMAT_IS_IEC61937(f)
|
||||
#define AF_FORMAT_IS_AC3(fmt) \
|
||||
(((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_AC3)
|
||||
|
||||
#define AF_FORMAT_IS_IEC61937(fmt) \
|
||||
(AF_FORMAT_IS_AC3(fmt) || \
|
||||
((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_IEC61937)
|
||||
|
||||
#define AF_FORMAT_IS_SPECIAL(fmt) \
|
||||
((fmt & AF_FORMAT_SPECIAL_MASK) != 0)
|
||||
|
||||
struct af_fmt_entry {
|
||||
const char *name;
|
||||
|
||||
+6
-8
@@ -37,7 +37,6 @@ struct mixer {
|
||||
struct af_stream *af;
|
||||
// Static, dependent on ao/softvol settings
|
||||
bool softvol; // use AO (true) or af_volume (false)
|
||||
bool ao_softvol; // AO has private or per-app volume
|
||||
bool emulate_mute; // if true, emulate mute with volume=0
|
||||
// Last known values (possibly out of sync with reality)
|
||||
float vol_l, vol_r;
|
||||
@@ -246,13 +245,11 @@ char *mixer_get_volume_restore_data(struct mixer *mixer)
|
||||
|
||||
static void probe_softvol(struct mixer *mixer)
|
||||
{
|
||||
mixer->ao_softvol =
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_SOFT_VOLUME, 0) == 1 ||
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) == 1;
|
||||
|
||||
if (mixer->opts->softvol == SOFTVOL_AUTO) {
|
||||
// No system-wide volume => fine with AO volume control.
|
||||
mixer->softvol = !mixer->ao_softvol;
|
||||
mixer->softvol =
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_TEMP_VOLUME, 0) != 1 ||
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) != 1;
|
||||
} else {
|
||||
mixer->softvol = mixer->opts->softvol == SOFTVOL_YES;
|
||||
}
|
||||
@@ -285,7 +282,8 @@ static void restore_volume(struct mixer *mixer)
|
||||
const char *prev_driver = mixer->driver;
|
||||
mixer->driver = mixer->softvol ? "softvol" : ao_get_name(ao);
|
||||
|
||||
bool restore = mixer->softvol || mixer->ao_softvol;
|
||||
bool restore =
|
||||
mixer->softvol || ao_control(ao, AOCONTROL_HAS_TEMP_VOLUME, 0) == 1;
|
||||
|
||||
// Restore old parameters if volume won't survive reinitialization.
|
||||
// But not if volume scale is possibly different.
|
||||
@@ -363,7 +361,7 @@ void mixer_uninit_audio(struct mixer *mixer)
|
||||
return;
|
||||
|
||||
checkvolume(mixer);
|
||||
if (mixer->muted_by_us && !mixer->softvol && !mixer->ao_softvol) {
|
||||
if (mixer->muted_by_us) {
|
||||
/* Current audio output API combines playing the remaining buffered
|
||||
* audio and uninitializing the AO into one operation, even though
|
||||
* ideally unmute would happen between those two steps. We can't do
|
||||
|
||||
+14
-6
@@ -60,6 +60,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#if HAVE_PULSE
|
||||
&audio_out_pulse,
|
||||
#endif
|
||||
#if HAVE_SNDIO
|
||||
&audio_out_sndio,
|
||||
#endif
|
||||
#if HAVE_ALSA
|
||||
&audio_out_alsa,
|
||||
#endif
|
||||
@@ -84,9 +87,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#endif
|
||||
#if HAVE_SDL1 || HAVE_SDL2
|
||||
&audio_out_sdl,
|
||||
#endif
|
||||
#if HAVE_SNDIO
|
||||
&audio_out_sndio,
|
||||
#endif
|
||||
&audio_out_null,
|
||||
// should not be auto-selected:
|
||||
@@ -152,7 +152,6 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
|
||||
.channels = channels,
|
||||
.format = format,
|
||||
.log = mp_log_new(ao, log, name),
|
||||
.def_buffer = global->opts->audio_buffer,
|
||||
};
|
||||
if (ao->driver->encode != !!ao->encode_lavc_ctx)
|
||||
goto error;
|
||||
@@ -188,7 +187,7 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
|
||||
ao->device_buffer = ao->driver->get_space(ao);
|
||||
MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer);
|
||||
}
|
||||
ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate);
|
||||
ao->buffer = MPMAX(ao->device_buffer, MIN_BUFFER * ao->samplerate);
|
||||
MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer);
|
||||
|
||||
if (ao->api->init(ao) < 0)
|
||||
@@ -259,7 +258,16 @@ int ao_play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
{
|
||||
return ao->api->control ? ao->api->control(ao, cmd, arg) : CONTROL_UNKNOWN;
|
||||
switch (cmd) {
|
||||
case AOCONTROL_HAS_TEMP_VOLUME:
|
||||
return !ao->no_persistent_volume;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return !!ao->per_application_mixer;
|
||||
default:
|
||||
if (ao->api->control)
|
||||
return ao->api->control(ao, cmd, arg);
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
// Return size of the buffered data in seconds. Can include the device latency.
|
||||
|
||||
+2
-4
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "common/common.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
@@ -36,9 +36,7 @@ enum aocontrol {
|
||||
AOCONTROL_SET_MUTE,
|
||||
// Has char* as argument, which contains the desired stream title.
|
||||
AOCONTROL_UPDATE_STREAM_TITLE,
|
||||
// the AO does the equivalent of af_volume (return CONTROL_TRUE if yes)
|
||||
AOCONTROL_HAS_SOFT_VOLUME,
|
||||
// like above, but volume persists (per app), mpv won't restore volume
|
||||
AOCONTROL_HAS_TEMP_VOLUME,
|
||||
AOCONTROL_HAS_PER_APP_VOLUME,
|
||||
};
|
||||
|
||||
|
||||
+29
-25
@@ -38,7 +38,6 @@
|
||||
#include "options/options.h"
|
||||
#include "options/m_option.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
#define ALSA_PCM_NEW_SW_PARAMS_API
|
||||
@@ -100,7 +99,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
long get_vol, set_vol;
|
||||
float f_multi;
|
||||
|
||||
if (AF_FORMAT_IS_SPECIAL(ao->format))
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
return CONTROL_FALSE;
|
||||
|
||||
//allocate simple id
|
||||
@@ -209,15 +208,25 @@ alsa_error:
|
||||
static const int mp_to_alsa_format[][2] = {
|
||||
{AF_FORMAT_S8, SND_PCM_FORMAT_S8},
|
||||
{AF_FORMAT_U8, SND_PCM_FORMAT_U8},
|
||||
{AF_FORMAT_U16, SND_PCM_FORMAT_U16},
|
||||
{AF_FORMAT_S16, SND_PCM_FORMAT_S16},
|
||||
{AF_FORMAT_U32, SND_PCM_FORMAT_U32},
|
||||
{AF_FORMAT_S32, SND_PCM_FORMAT_S32},
|
||||
{AF_FORMAT_U24,
|
||||
MP_SELECT_LE_BE(SND_PCM_FORMAT_U24_3LE, SND_PCM_FORMAT_U24_3BE)},
|
||||
{AF_FORMAT_S24,
|
||||
MP_SELECT_LE_BE(SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE)},
|
||||
{AF_FORMAT_FLOAT, SND_PCM_FORMAT_FLOAT},
|
||||
{AF_FORMAT_U16_LE, SND_PCM_FORMAT_U16_LE},
|
||||
{AF_FORMAT_U16_BE, SND_PCM_FORMAT_U16_BE},
|
||||
{AF_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_S16_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_U32_LE, SND_PCM_FORMAT_U32_LE},
|
||||
{AF_FORMAT_U32_BE, SND_PCM_FORMAT_U32_BE},
|
||||
{AF_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE},
|
||||
{AF_FORMAT_S32_BE, SND_PCM_FORMAT_S32_BE},
|
||||
{AF_FORMAT_U24_LE, SND_PCM_FORMAT_U24_3LE},
|
||||
{AF_FORMAT_U24_BE, SND_PCM_FORMAT_U24_3BE},
|
||||
{AF_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE},
|
||||
{AF_FORMAT_S24_BE, SND_PCM_FORMAT_S24_3BE},
|
||||
{AF_FORMAT_FLOAT_LE, SND_PCM_FORMAT_FLOAT_LE},
|
||||
{AF_FORMAT_FLOAT_BE, SND_PCM_FORMAT_FLOAT_BE},
|
||||
{AF_FORMAT_AC3_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_AC3_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_IEC61937_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_IEC61937_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_MPEG2, SND_PCM_FORMAT_MPEG},
|
||||
{AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN},
|
||||
};
|
||||
|
||||
@@ -400,15 +409,7 @@ static int init(struct ao *ao)
|
||||
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
|
||||
CHECK_ALSA_ERROR("Unable to get initial parameters");
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (ao->format == AF_FORMAT_S_MP3) {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_MPEG;
|
||||
} else {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
}
|
||||
} else {
|
||||
p->alsa_fmt = find_alsa_format(ao->format);
|
||||
}
|
||||
p->alsa_fmt = find_alsa_format(ao->format);
|
||||
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
@@ -416,12 +417,15 @@ static int init(struct ao *ao)
|
||||
|
||||
err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
if (err < 0) {
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
CHECK_ALSA_ERROR("Unable to set IEC61937 format");
|
||||
MP_INFO(ao, "Format %s is not supported by hardware, trying default.\n",
|
||||
af_fmt_to_str(ao->format));
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16_LE;
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ao->format = AF_FORMAT_AC3_LE;
|
||||
else if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
ao->format = AF_FORMAT_IEC61937_LE;
|
||||
else
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
@@ -671,7 +675,7 @@ static int get_space(struct ao *ao)
|
||||
unsigned space = snd_pcm_status_get_avail(status);
|
||||
if (space > p->buffersize) // Buffer underrun?
|
||||
space = p->buffersize;
|
||||
return space / p->outburst * p->outburst;
|
||||
return space;
|
||||
|
||||
alsa_error:
|
||||
return 0;
|
||||
|
||||
@@ -136,10 +136,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return get_volume(ao, arg);
|
||||
case AOCONTROL_SET_VOLUME:
|
||||
return set_volume(ao, arg);
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -153,6 +149,9 @@ static int init(struct ao *ao)
|
||||
|
||||
if (p->opt_list) ca_print_device_list(ao);
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
ao->no_persistent_volume = true;
|
||||
|
||||
OSStatus err = ca_select_device(ao, p->opt_device_id, &p->device);
|
||||
CHECK_CA_ERROR("failed to select device");
|
||||
|
||||
|
||||
@@ -410,9 +410,8 @@ static int init(struct ao *ao)
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
bool supports_digital = false;
|
||||
/* Probe whether device support S/PDIF stream output if input is AC3 stream,
|
||||
* or anything else IEC61937-framed. */
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
|
||||
if (AF_FORMAT_IS_AC3(ao->format)) {
|
||||
if (ca_device_supports_digital(ao, p->device))
|
||||
supports_digital = true;
|
||||
}
|
||||
@@ -522,7 +521,14 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
err = ca_enable_device_listener(p->device, changed);
|
||||
CHECK_CA_ERROR("cannot install format change listener during init");
|
||||
|
||||
if (p->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
if (!(p->stream_asdb.mFormatFlags & kAudioFormatFlagIsBigEndian))
|
||||
#else
|
||||
/* tell mplayer that we need a byteswap on AC3 streams, */
|
||||
if (p->stream_asbd.mFormatID & kAudioFormat60958AC3)
|
||||
ao->format = AF_FORMAT_AC3_LE;
|
||||
else if (p->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
|
||||
#endif
|
||||
MP_WARN(ao, "stream has non-native byte order, output may fail\n");
|
||||
|
||||
ao->samplerate = p->stream_asbd.mSampleRate;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "audio/out/ao_coreaudio_utils.h"
|
||||
#include "audio/out/ao_coreaudio_properties.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/endian.h"
|
||||
#include "audio/format.h"
|
||||
|
||||
void ca_print_device_list(struct ao *ao)
|
||||
@@ -130,21 +129,20 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
|
||||
{
|
||||
asbd->mSampleRate = ao->samplerate;
|
||||
// Set "AC3" for other spdif formats too - unknown if that works.
|
||||
asbd->mFormatID = AF_FORMAT_IS_IEC61937(ao->format) ?
|
||||
asbd->mFormatID = AF_FORMAT_IS_AC3(ao->format) ?
|
||||
kAudioFormat60958AC3 :
|
||||
kAudioFormatLinearPCM;
|
||||
asbd->mChannelsPerFrame = ao->channels.num;
|
||||
asbd->mBitsPerChannel = af_fmt2bits(ao->format);
|
||||
asbd->mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
|
||||
if ((ao->format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F)
|
||||
if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsFloat;
|
||||
|
||||
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
if (BYTE_ORDER == BIG_ENDIAN)
|
||||
if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
|
||||
asbd->mFramesPerPacket = 1;
|
||||
|
||||
+13
-16
@@ -98,7 +98,6 @@ struct priv {
|
||||
int outburst; ///play in multiple of chunks of this size
|
||||
|
||||
int cfg_device;
|
||||
int cfg_buffersize;
|
||||
};
|
||||
|
||||
static float get_delay(struct ao *ao);
|
||||
@@ -351,8 +350,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
IDirectSoundBuffer_SetVolume(p->hdsbuf, volume);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -373,6 +370,7 @@ static int init(struct ao *ao)
|
||||
if (!InitDirectSound(ao))
|
||||
return -1;
|
||||
|
||||
ao->no_persistent_volume = true;
|
||||
p->audio_volume = 100;
|
||||
|
||||
// ok, now create the buffers
|
||||
@@ -382,33 +380,34 @@ static int init(struct ao *ao)
|
||||
int format = af_fmt_from_planar(ao->format);
|
||||
int rate = ao->samplerate;
|
||||
|
||||
if (!AF_FORMAT_IS_IEC61937(format)) {
|
||||
if (AF_FORMAT_IS_AC3(format))
|
||||
format = AF_FORMAT_AC3;
|
||||
else {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
}
|
||||
switch (format) {
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_S16:
|
||||
case AF_FORMAT_AC3:
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_U8:
|
||||
break;
|
||||
default:
|
||||
if (AF_FORMAT_IS_IEC61937(format))
|
||||
break;
|
||||
MP_VERBOSE(ao, "format %s not supported defaulting to Signed 16-bit Little-Endian\n",
|
||||
af_fmt_to_str(format));
|
||||
format = AF_FORMAT_S16;
|
||||
format = AF_FORMAT_S16_LE;
|
||||
}
|
||||
//set our audio parameters
|
||||
ao->samplerate = rate;
|
||||
ao->format = format;
|
||||
ao->bps = ao->channels.num * rate * af_fmt2bps(format);
|
||||
int buffersize = ao->bps * p->cfg_buffersize / 1000;
|
||||
int buffersize = ao->bps * 0.100; // space for 100ms
|
||||
MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate,
|
||||
ao->channels.num, af_fmt_to_str(format));
|
||||
MP_VERBOSE(ao, "Buffersize:%d bytes (%f msec)\n",
|
||||
buffersize, buffersize * 1000.0 / ao->bps);
|
||||
MP_VERBOSE(ao, "Buffersize:%d bytes (%d msec)\n",
|
||||
buffersize, buffersize / ao->bps * 1000);
|
||||
|
||||
//fill waveformatex
|
||||
ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
@@ -416,8 +415,7 @@ static int init(struct ao *ao)
|
||||
? sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) : 0;
|
||||
wformat.Format.nChannels = ao->channels.num;
|
||||
wformat.Format.nSamplesPerSec = rate;
|
||||
if (AF_FORMAT_IS_IEC61937(format)) {
|
||||
// Whether it also works with e.g. DTS is unknown, but probably does.
|
||||
if (AF_FORMAT_IS_AC3(format)) {
|
||||
wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
|
||||
wformat.Format.wBitsPerSample = 16;
|
||||
wformat.Format.nBlockAlign = 4;
|
||||
@@ -585,7 +583,7 @@ static int get_space(struct ao *ao)
|
||||
int space = check_free_buffer_size(ao);
|
||||
if (space < p->min_free_space)
|
||||
return 0;
|
||||
return (space - p->min_free_space) / p->outburst * p->outburst / ao->sstride;
|
||||
return (space - p->min_free_space) / ao->sstride;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,7 +636,6 @@ const struct ao_driver audio_out_dsound = {
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INT("device", cfg_device, 0),
|
||||
OPT_INTRANGE("buffersize", cfg_buffersize, 0, 1, 10000, OPTDEF_INT(200)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/audioconvert.h>
|
||||
|
||||
#include "compat/libav.h"
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "common/common.h"
|
||||
|
||||
+1
-2
@@ -148,8 +148,7 @@ static int get_space(struct ao *ao)
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
drain(ao);
|
||||
int samples = priv->buffersize - priv->latency - priv->buffered;
|
||||
return samples / priv->outburst * priv->outburst;
|
||||
return priv->buffersize - priv->latency - priv->buffered;
|
||||
}
|
||||
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
@@ -76,8 +76,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
vol->left = vol->right = volume * 100;
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -134,6 +132,7 @@ static int init(struct ao *ao)
|
||||
return -1;
|
||||
}
|
||||
ao_data = ao;
|
||||
ao->no_persistent_volume = true;
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (i = 0; speaker_pos[i].id != -1; i++)
|
||||
mp_chmap_sel_add_speaker(&sel, speaker_pos[i].id);
|
||||
|
||||
+297
-321
@@ -31,7 +31,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
@@ -39,8 +38,6 @@
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#if HAVE_SYS_SOUNDCARD_H
|
||||
#include <sys/soundcard.h>
|
||||
@@ -55,20 +52,14 @@
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
|
||||
// Define to 0 if the device must be reopened to reset it (stop all playback,
|
||||
// clear the buffer), and the device should be closed when unused.
|
||||
// Define to 1 if SNDCTL_DSP_RESET should be used to reset without close.
|
||||
#define KEEP_DEVICE (defined(SNDCTL_DSP_RESET) && !defined(__NetBSD__))
|
||||
|
||||
struct priv {
|
||||
int audio_fd;
|
||||
int prepause_samples;
|
||||
int prepause_space;
|
||||
int oss_mixer_channel;
|
||||
audio_buf_info zz;
|
||||
int audio_delay_method;
|
||||
int buffersize;
|
||||
int outburst;
|
||||
bool device_failed;
|
||||
double audio_end;
|
||||
|
||||
char *dsp;
|
||||
char *oss_mixer_device;
|
||||
@@ -90,60 +81,53 @@ static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), // 7.1
|
||||
};
|
||||
|
||||
#if !defined(AFMT_S16_NE) && defined(AFMT_S16_LE) && defined(AFMT_S16_BE)
|
||||
#define AFMT_S16_NE MP_SELECT_LE_BE(AFMT_S16_LE, AFMT_S16_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U16_NE) && defined(AFMT_U16_LE) && defined(AFMT_U16_BE)
|
||||
#define AFMT_U16_NE MP_SELECT_LE_BE(AFMT_U16_LE, AFMT_U16_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U24_NE) && defined(AFMT_U24_LE) && defined(AFMT_U24_BE)
|
||||
#define AFMT_U24_NE MP_SELECT_LE_BE(AFMT_U24_LE, AFMT_U24_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_S24_NE) && defined(AFMT_S24_LE) && defined(AFMT_S24_BE)
|
||||
#define AFMT_S24_NE MP_SELECT_LE_BE(AFMT_S24_LE, AFMT_S24_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U32_NE) && defined(AFMT_U32_LE) && defined(AFMT_U32_BE)
|
||||
#define AFMT_U32MP_SELECT_LE_BE(AFMT_U32_LE, AFMT_U32_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_S32_NE) && defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
|
||||
#define AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
|
||||
#endif
|
||||
|
||||
static const int format_table[][2] = {
|
||||
{AFMT_U8, AF_FORMAT_U8},
|
||||
{AFMT_S8, AF_FORMAT_S8},
|
||||
{AFMT_U16_NE, AF_FORMAT_U16},
|
||||
{AFMT_S16_NE, AF_FORMAT_S16},
|
||||
#ifdef AFMT_U24_NE
|
||||
{AFMT_U24_NE, AF_FORMAT_U24},
|
||||
{AFMT_U16_LE, AF_FORMAT_U16_LE},
|
||||
{AFMT_U16_BE, AF_FORMAT_U16_BE},
|
||||
{AFMT_S16_LE, AF_FORMAT_S16_LE},
|
||||
{AFMT_S16_BE, AF_FORMAT_S16_BE},
|
||||
#ifdef AFMT_S24_PACKED
|
||||
{AFMT_S24_PACKED, AF_FORMAT_S24_LE},
|
||||
#endif
|
||||
#ifdef AFMT_S24_NE
|
||||
{AFMT_S24_NE, AF_FORMAT_S24},
|
||||
#ifdef AFMT_U24_LE
|
||||
{AFMT_U24_LE, AF_FORMAT_U24_LE},
|
||||
#endif
|
||||
#ifdef AFMT_U32_NE
|
||||
{AFMT_U32_NE, AF_FORMAT_U32},
|
||||
#ifdef AFMT_U24_BE
|
||||
{AFMT_U24_BE, AF_FORMAT_U24_BE},
|
||||
#endif
|
||||
#ifdef AFMT_S32_NE
|
||||
{AFMT_S32_NE, AF_FORMAT_S32},
|
||||
#ifdef AFMT_S24_LE
|
||||
{AFMT_S24_LE, AF_FORMAT_S24_LE},
|
||||
#endif
|
||||
#ifdef AFMT_S24_BE
|
||||
{AFMT_S24_BE, AF_FORMAT_S24_BE},
|
||||
#endif
|
||||
#ifdef AFMT_U32_LE
|
||||
{AFMT_U32_LE, AF_FORMAT_U32_LE},
|
||||
#endif
|
||||
#ifdef AFMT_U32_BE
|
||||
{AFMT_U32_BE, AF_FORMAT_U32_BE},
|
||||
#endif
|
||||
#ifdef AFMT_S32_LE
|
||||
{AFMT_S32_LE, AF_FORMAT_S32_LE},
|
||||
#endif
|
||||
#ifdef AFMT_S32_BE
|
||||
{AFMT_S32_BE, AF_FORMAT_S32_BE},
|
||||
#endif
|
||||
#ifdef AFMT_FLOAT
|
||||
{AFMT_FLOAT, AF_FORMAT_FLOAT},
|
||||
#endif
|
||||
// SPECIALS
|
||||
#ifdef AFMT_MPEG
|
||||
{AFMT_MPEG, AF_FORMAT_S_MP3},
|
||||
{AFMT_MPEG, AF_FORMAT_MPEG2},
|
||||
#endif
|
||||
#ifdef AFMT_AC3
|
||||
{AFMT_AC3, AF_FORMAT_AC3},
|
||||
#endif
|
||||
{-1, -1}
|
||||
};
|
||||
|
||||
#ifndef AFMT_AC3
|
||||
#define AFMT_AC3 -1
|
||||
#endif
|
||||
|
||||
static int format2oss(int format)
|
||||
{
|
||||
for (int n = 0; format_table[n][0] != -1; n++) {
|
||||
@@ -159,7 +143,7 @@ static int oss2format(int format)
|
||||
if (format_table[n][0] == format)
|
||||
return format_table[n][1];
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +178,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
struct priv *p = ao->priv;
|
||||
switch (cmd) {
|
||||
case AOCONTROL_GET_VOLUME:
|
||||
case AOCONTROL_SET_VOLUME: {
|
||||
case AOCONTROL_SET_VOLUME:
|
||||
{
|
||||
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||
int fd, v, devs;
|
||||
|
||||
@@ -204,7 +189,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_OK;
|
||||
#endif
|
||||
|
||||
if (AF_FORMAT_IS_SPECIAL(ao->format))
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
return CONTROL_TRUE;
|
||||
|
||||
if ((fd = open(p->oss_mixer_device, O_RDONLY)) != -1) {
|
||||
@@ -225,203 +210,22 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
close(fd);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
}
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
#ifdef SNDCTL_DSP_GETPLAYVOL
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
#endif
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
// 1: ok, 0: not writable, -1: error
|
||||
static int device_writable(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
struct pollfd fd = {.fd = p->audio_fd, .events = POLLOUT};
|
||||
return poll(&fd, 1, 0);
|
||||
}
|
||||
|
||||
static void close_device(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->device_failed = false;
|
||||
if (p->audio_fd == -1)
|
||||
return;
|
||||
#if defined(SNDCTL_DSP_RESET)
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#endif
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
close_device(ao);
|
||||
}
|
||||
|
||||
static bool try_format(struct ao *ao, int *format)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
int oss_format = format2oss(*format);
|
||||
if (oss_format == -1 && AF_FORMAT_IS_IEC61937(*format))
|
||||
oss_format = AFMT_AC3;
|
||||
|
||||
if (oss_format == -1) {
|
||||
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
|
||||
af_fmt_to_str(*format));
|
||||
*format = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int actual_format = oss_format;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &actual_format) < 0)
|
||||
actual_format = -1;
|
||||
|
||||
if (actual_format == oss_format)
|
||||
return true;
|
||||
|
||||
MP_WARN(ao, "Can't set audio device to %s output.\n", af_fmt_to_str(*format));
|
||||
*format = oss2format(actual_format);
|
||||
if (actual_format != -1 && !*format)
|
||||
MP_ERR(ao, "Unknown/Unsupported OSS format: 0x%x.\n", actual_format);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int reopen_device(struct ao *ao, bool allow_format_changes)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
int samplerate = ao->samplerate;
|
||||
int format = ao->format;
|
||||
struct mp_chmap channels = ao->channels;
|
||||
|
||||
#ifdef __linux__
|
||||
p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK);
|
||||
#else
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
#endif
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Can't open audio device %s: %s\n", p->dsp, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Remove the non-blocking flag */
|
||||
if (fcntl(p->audio_fd, F_SETFL, 0) < 0) {
|
||||
MP_ERR(ao, "Can't make file descriptor blocking: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(format)) {
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate);
|
||||
// Probably could be fixed by setting number of channels; needs testing.
|
||||
if (channels.num != 2) {
|
||||
MP_ERR(ao, "Format %s not implemented.\n", af_fmt_to_str(format));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
int try_formats[] = {format, AF_FORMAT_S32, AF_FORMAT_S24, AF_FORMAT_S16, 0};
|
||||
for (int n = 0; try_formats[n]; n++) {
|
||||
format = try_formats[n];
|
||||
if (try_format(ao, &format))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!format) {
|
||||
MP_ERR(ao, "Can't set sample format.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(format));
|
||||
|
||||
if (!AF_FORMAT_IS_IEC61937(format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
|
||||
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &channels))
|
||||
goto fail;
|
||||
int reqchannels = channels.num;
|
||||
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
|
||||
if (reqchannels > 2) {
|
||||
int nchannels = reqchannels;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
|
||||
nchannels != reqchannels)
|
||||
{
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
int c = reqchannels - 1;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
goto fail;
|
||||
}
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &channels, c + 1))
|
||||
goto fail;
|
||||
}
|
||||
MP_VERBOSE(ao, "using %d channels (requested: %d)\n",
|
||||
channels.num, reqchannels);
|
||||
// set rate
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate);
|
||||
MP_VERBOSE(ao, "using %d Hz samplerate\n", samplerate);
|
||||
}
|
||||
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) == -1) {
|
||||
int r = 0;
|
||||
MP_WARN(ao, "driver doesn't support SNDCTL_DSP_GETOSPACE\n");
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETBLKSIZE, &r) == -1)
|
||||
MP_VERBOSE(ao, "%d bytes/frag (config.h)\n", p->outburst);
|
||||
else {
|
||||
p->outburst = r;
|
||||
MP_VERBOSE(ao, "%d bytes/frag (GETBLKSIZE)\n", p->outburst);
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "frags: %3d/%d (%d bytes/frag) free: %6d\n",
|
||||
zz.fragments, zz.fragstotal, zz.fragsize, zz.bytes);
|
||||
p->buffersize = zz.bytes;
|
||||
p->outburst = zz.fragsize;
|
||||
}
|
||||
|
||||
if (allow_format_changes) {
|
||||
ao->format = format;
|
||||
ao->samplerate = samplerate;
|
||||
ao->channels = channels;
|
||||
} else {
|
||||
if (format != ao->format || samplerate != ao->samplerate ||
|
||||
!mp_chmap_equals(&channels, &ao->channels))
|
||||
{
|
||||
MP_ERR(ao, "Could not reselect previous audio format.\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
p->outburst -= p->outburst % (channels.num * af_fmt2bps(format)); // round down
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close_device(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// open & setup audio device
|
||||
// return: 0=success -1=fail
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int oss_format;
|
||||
|
||||
#ifdef SNDCTL_DSP_GETPLAYVOL
|
||||
ao->no_persistent_volume = true;
|
||||
#endif
|
||||
|
||||
const char *mchan = NULL;
|
||||
if (p->cfg_oss_mixer_channel && p->cfg_oss_mixer_channel[0])
|
||||
@@ -462,36 +266,178 @@ static int init(struct ao *ao)
|
||||
MP_VERBOSE(ao, "using '%s' mixer device\n", p->oss_mixer_device);
|
||||
MP_VERBOSE(ao, "using '%s' mixer device\n", mixer_channels[p->oss_mixer_channel]);
|
||||
|
||||
#ifdef __linux__
|
||||
p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK);
|
||||
#else
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
#endif
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Can't open audio device %s: %s\n", p->dsp, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Remove the non-blocking flag */
|
||||
if (fcntl(p->audio_fd, F_SETFL, 0) < 0) {
|
||||
MP_ERR(ao, "Can't make file descriptor blocking: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
if (reopen_device(ao, true) < 0)
|
||||
goto fail;
|
||||
if (AF_FORMAT_IS_AC3(ao->format)) {
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
}
|
||||
|
||||
ac3_retry:
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ao->format = AF_FORMAT_AC3;
|
||||
oss_format = format2oss(ao->format);
|
||||
if (oss_format == -1) {
|
||||
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
|
||||
af_fmt_to_str(ao->format));
|
||||
#if defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S32_BE;
|
||||
#else
|
||||
oss_format = AFMT_S32_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S32;
|
||||
#elif defined(AFMT_S24_LE) && defined(AFMT_S24_BE)
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S24_BE;
|
||||
#else
|
||||
oss_format = AFMT_S24_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S24;
|
||||
#else
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S16_BE;
|
||||
#else
|
||||
oss_format = AFMT_S16_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S16;
|
||||
#endif
|
||||
}
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format) < 0 ||
|
||||
oss_format != format2oss(ao->format))
|
||||
{
|
||||
MP_WARN(ao, "Can't set audio device %s to %s output, trying %s...\n",
|
||||
p->dsp, af_fmt_to_str(ao->format),
|
||||
af_fmt_to_str(AF_FORMAT_S16));
|
||||
ao->format = AF_FORMAT_S16;
|
||||
goto ac3_retry;
|
||||
}
|
||||
|
||||
ao->format = oss2format(oss_format);
|
||||
if (ao->format == -1) {
|
||||
MP_ERR(ao, "Unknown/Unsupported OSS format: %x.\n", oss_format);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(ao->format));
|
||||
|
||||
if (!AF_FORMAT_IS_AC3(ao->format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
|
||||
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
int reqchannels = ao->channels.num;
|
||||
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
|
||||
if (reqchannels > 2) {
|
||||
int nchannels = reqchannels;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
|
||||
nchannels != reqchannels)
|
||||
{
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
int c = reqchannels - 1;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
return -1;
|
||||
}
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, c + 1))
|
||||
return -1;
|
||||
}
|
||||
MP_VERBOSE(ao, "using %d channels (requested: %d)\n",
|
||||
ao->channels.num, reqchannels);
|
||||
// set rate
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
MP_VERBOSE(ao, "using %d Hz samplerate\n", ao->samplerate);
|
||||
}
|
||||
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) == -1) {
|
||||
int r = 0;
|
||||
MP_WARN(ao, "driver doesn't support SNDCTL_DSP_GETOSPACE\n");
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETBLKSIZE, &r) == -1)
|
||||
MP_VERBOSE(ao, "%d bytes/frag (config.h)\n", p->outburst);
|
||||
else {
|
||||
p->outburst = r;
|
||||
MP_VERBOSE(ao, "%d bytes/frag (GETBLKSIZE)\n", p->outburst);
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "frags: %3d/%d (%d bytes/frag) free: %6d\n",
|
||||
p->zz.fragments, p->zz.fragstotal, p->zz.fragsize, p->zz.bytes);
|
||||
p->buffersize = p->zz.bytes;
|
||||
p->outburst = p->zz.fragsize;
|
||||
}
|
||||
|
||||
if (p->buffersize == -1) {
|
||||
// Measuring buffer size:
|
||||
void *data = malloc(p->outburst);
|
||||
if (!data) {
|
||||
MP_ERR(ao, "Out of memory, or broken outburst size.\n");
|
||||
goto fail;
|
||||
}
|
||||
void *data;
|
||||
p->buffersize = 0;
|
||||
#if HAVE_AUDIO_SELECT
|
||||
data = malloc(p->outburst);
|
||||
memset(data, 0, p->outburst);
|
||||
while (p->buffersize < 0x40000 && device_writable(ao) > 0) {
|
||||
while (p->buffersize < 0x40000) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(p->audio_fd, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
if (!select(p->audio_fd + 1, NULL, &rfds, NULL, &tv))
|
||||
break;
|
||||
write(p->audio_fd, data, p->outburst);
|
||||
p->buffersize += p->outburst;
|
||||
}
|
||||
free(data);
|
||||
if (p->buffersize == 0) {
|
||||
MP_ERR(ao, "Your OSS audio driver DOES NOT support poll().\n");
|
||||
goto fail;
|
||||
MP_ERR(ao, "*** Your audio driver DOES NOT support select() ***\n");
|
||||
MP_ERR(ao, "Recompile mpv with #define HAVE_AUDIO_SELECT 0 in config.h!\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
ao->bps = ao->channels.num * af_fmt2bps(ao->format);
|
||||
p->outburst -= p->outburst % ao->bps; // round down
|
||||
ao->bps *= ao->samplerate;
|
||||
|
||||
fail:
|
||||
uninit(ao);
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (p->audio_fd == -1)
|
||||
return;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#endif
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
}
|
||||
|
||||
static void drain(struct ao *ao)
|
||||
@@ -504,39 +450,104 @@ static void drain(struct ao *ao)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SNDCTL_DSP_RESET
|
||||
static void close_device(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// stop playing and empty buffers (for seeking/pause)
|
||||
static void reset(struct ao *ao)
|
||||
{
|
||||
#if KEEP_DEVICE
|
||||
struct priv *p = ao->priv;
|
||||
int oss_format;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Fatal error: *** CANNOT "
|
||||
"RE-OPEN / RESET AUDIO DEVICE *** %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
oss_format = format2oss(ao->format);
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
|
||||
if (!AF_FORMAT_IS_AC3(ao->format)) {
|
||||
int c = ao->channels.num;
|
||||
if (ao->channels.num > 2)
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &c);
|
||||
else {
|
||||
c--;
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c);
|
||||
}
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
}
|
||||
}
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int playsize = p->outburst;
|
||||
|
||||
#ifdef SNDCTL_DSP_GETOSPACE
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
|
||||
// calculate exact buffer space:
|
||||
playsize = p->zz.fragments * p->zz.fragsize;
|
||||
return playsize / ao->sstride;
|
||||
}
|
||||
#endif
|
||||
|
||||
// check buffer
|
||||
#if HAVE_AUDIO_SELECT
|
||||
{
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(p->audio_fd, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
if (!select(p->audio_fd + 1, NULL, &rfds, NULL, &tv))
|
||||
return 0; // not block!
|
||||
}
|
||||
#endif
|
||||
|
||||
return p->outburst / ao->sstride;
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->prepause_space = get_space(ao) * ao->sstride;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
#endif
|
||||
}
|
||||
|
||||
// plays 'len' samples of 'data'
|
||||
// plays 'len' bytes of 'data'
|
||||
// it should round it down to outburst*n
|
||||
// return: number of samples played
|
||||
// return: number of bytes played
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int len = samples * ao->sstride;
|
||||
if (len == 0)
|
||||
return len;
|
||||
|
||||
if (p->audio_fd < 0 && !p->device_failed && reopen_device(ao, false) < 0)
|
||||
MP_ERR(ao, "Fatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE ***\n");
|
||||
if (p->audio_fd < 0) {
|
||||
// Let playback continue normally, even with a closed device.
|
||||
p->device_failed = true;
|
||||
double now = mp_time_sec();
|
||||
if (p->audio_end < now)
|
||||
p->audio_end = now;
|
||||
p->audio_end += samples / (double)ao->samplerate;
|
||||
return samples;
|
||||
}
|
||||
|
||||
if (len > p->outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
|
||||
len /= p->outburst;
|
||||
len *= p->outburst;
|
||||
@@ -545,16 +556,22 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
return len / ao->sstride;
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
#ifndef SNDCTL_DSP_RESET
|
||||
reset(ao);
|
||||
#endif
|
||||
int fillframes = get_space(ao) - p->prepause_space / ao->sstride;
|
||||
if (fillframes > 0)
|
||||
ao_play_silence(ao, fillframes);
|
||||
}
|
||||
|
||||
// return: delay in seconds between first and last sample in buffer
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (p->audio_fd < 0) {
|
||||
double rest = p->audio_end - mp_time_sec();
|
||||
if (rest > 0)
|
||||
return rest;
|
||||
return 0;
|
||||
}
|
||||
/* Calculate how many bytes/second is sent out */
|
||||
if (p->audio_delay_method == 2) {
|
||||
#ifdef SNDCTL_DSP_GETODELAY
|
||||
@@ -565,57 +582,16 @@ static float get_delay(struct ao *ao)
|
||||
p->audio_delay_method = 1; // fallback if not supported
|
||||
}
|
||||
if (p->audio_delay_method == 1) {
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
|
||||
return ((float)(p->buffersize - zz.bytes)) / (float)ao->bps;
|
||||
// SNDCTL_DSP_GETOSPACE
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
|
||||
return ((float)(p->buffersize -
|
||||
p->zz.bytes)) / (float)ao->bps;
|
||||
}
|
||||
p->audio_delay_method = 0; // fallback if not supported
|
||||
}
|
||||
return ((float)p->buffersize) / (float)ao->bps;
|
||||
}
|
||||
|
||||
|
||||
// return: how many samples can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
|
||||
// calculate exact buffer space:
|
||||
return zz.fragments * zz.fragsize / ao->sstride;
|
||||
}
|
||||
|
||||
if (p->audio_fd < 0 && p->device_failed && get_delay(ao) > 0.2)
|
||||
return 0;
|
||||
|
||||
if (p->audio_fd < 0 || device_writable(ao) > 0)
|
||||
return p->outburst / ao->sstride;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->prepause_samples = get_delay(ao) * ao->samplerate;
|
||||
#if KEEP_DEVICE
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
#endif
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->audio_end = 0;
|
||||
if (p->prepause_samples > 0)
|
||||
ao_play_silence(ao, p->prepause_samples);
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
const struct ao_driver audio_out_oss = {
|
||||
|
||||
+9
-16
@@ -35,7 +35,6 @@
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// for GetFileType to detect pipes
|
||||
@@ -73,7 +72,8 @@ static void fput32le(uint32_t val, FILE *fp)
|
||||
static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
|
||||
{
|
||||
bool use_waveex = true;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT ? WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
|
||||
WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
|
||||
int bits = af_fmt2bits(ao->format);
|
||||
|
||||
@@ -124,23 +124,16 @@ static int init(struct ao *ao)
|
||||
if (priv->waveheader) {
|
||||
// WAV files must have one of the following formats
|
||||
|
||||
// And they don't work in big endian; fixing it would be simple, but
|
||||
// nobody cares.
|
||||
if (BYTE_ORDER == BIG_ENDIAN) {
|
||||
MP_FATAL(ao, "Not supported on big endian.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ao->format) {
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S16:
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_S32:
|
||||
case AF_FORMAT_FLOAT:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
case AF_FORMAT_AC3_LE:
|
||||
break;
|
||||
default:
|
||||
if (!AF_FORMAT_IS_IEC61937(ao->format))
|
||||
ao->format = AF_FORMAT_S16;
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -210,7 +203,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
fwrite(data[0], len, 1, priv->fp);
|
||||
priv->data_length += len;
|
||||
return samples;
|
||||
return len / ao->sstride;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
+32
-74
@@ -169,31 +169,16 @@ static const struct format_map {
|
||||
int mp_format;
|
||||
pa_sample_format_t pa_format;
|
||||
} format_maps[] = {
|
||||
{AF_FORMAT_S16, PA_SAMPLE_S16NE},
|
||||
{AF_FORMAT_S32, PA_SAMPLE_S32NE},
|
||||
{AF_FORMAT_FLOAT, PA_SAMPLE_FLOAT32NE},
|
||||
{AF_FORMAT_S16_LE, PA_SAMPLE_S16LE},
|
||||
{AF_FORMAT_S16_BE, PA_SAMPLE_S16BE},
|
||||
{AF_FORMAT_S32_LE, PA_SAMPLE_S32LE},
|
||||
{AF_FORMAT_S32_BE, PA_SAMPLE_S32BE},
|
||||
{AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE},
|
||||
{AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE},
|
||||
{AF_FORMAT_U8, PA_SAMPLE_U8},
|
||||
{AF_FORMAT_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
static pa_encoding_t map_digital_format(int format)
|
||||
{
|
||||
switch (format) {
|
||||
case AF_FORMAT_S_AC3: return PA_ENCODING_AC3_IEC61937;
|
||||
case AF_FORMAT_S_EAC3: return PA_ENCODING_EAC3_IEC61937;
|
||||
case AF_FORMAT_S_MP3: return PA_ENCODING_MPEG_IEC61937;
|
||||
case AF_FORMAT_S_DTS:
|
||||
case AF_FORMAT_S_DTSHD: return PA_ENCODING_DTS_IEC61937;
|
||||
#ifdef PA_ENCODING_MPEG2_AAC_IEC61937
|
||||
case AF_FORMAT_S_AAC: return PA_ENCODING_MPEG2_AAC_IEC61937;
|
||||
#endif
|
||||
default:
|
||||
if (AF_FORMAT_IS_IEC61937(format))
|
||||
return PA_ENCODING_ANY;
|
||||
return PA_ENCODING_PCM;
|
||||
}
|
||||
}
|
||||
|
||||
static const int speaker_map[][2] = {
|
||||
{PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL},
|
||||
{PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR},
|
||||
@@ -289,9 +274,9 @@ static void uninit(struct ao *ao)
|
||||
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct pa_sample_spec ss;
|
||||
struct pa_channel_map map;
|
||||
pa_proplist *proplist = NULL;
|
||||
pa_format_info *format = NULL;
|
||||
struct priv *priv = ao->priv;
|
||||
char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL;
|
||||
char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL;
|
||||
@@ -299,6 +284,8 @@ static int init(struct ao *ao)
|
||||
pthread_mutex_init(&priv->wakeup_lock, NULL);
|
||||
pthread_cond_init(&priv->wakeup, NULL);
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
|
||||
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
|
||||
MP_ERR(ao, "Failed to allocate main loop\n");
|
||||
goto fail;
|
||||
@@ -310,12 +297,6 @@ static int init(struct ao *ao)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "Library version: %s\n", pa_get_library_version());
|
||||
MP_VERBOSE(ao, "Proto: %lu\n",
|
||||
(long)pa_context_get_protocol_version(priv->context));
|
||||
MP_VERBOSE(ao, "Server proto: %lu\n",
|
||||
(long)pa_context_get_server_protocol_version(priv->context));
|
||||
|
||||
pa_context_set_state_callback(priv->context, context_state_cb, ao);
|
||||
|
||||
if (pa_context_connect(priv->context, host, 0, NULL) < 0)
|
||||
@@ -332,26 +313,26 @@ static int init(struct ao *ao)
|
||||
if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
if (!(format = pa_format_info_new()))
|
||||
goto unlock_and_fail;
|
||||
ss.channels = ao->channels.num;
|
||||
ss.rate = ao->samplerate;
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
format->encoding = map_digital_format(ao->format);
|
||||
if (format->encoding == PA_ENCODING_PCM) {
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
MP_VERBOSE(ao, "Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
MP_VERBOSE(ao, "Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
fmt_map++;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
ss.format = fmt_map->pa_format;
|
||||
|
||||
pa_format_info_set_sample_format(format, fmt_map->pa_format);
|
||||
if (!pa_sample_spec_valid(&ss)) {
|
||||
MP_ERR(ao, "Invalid sample spec\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
if (!select_chmap(ao, &map))
|
||||
@@ -365,28 +346,14 @@ static int init(struct ao *ao)
|
||||
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ICON_NAME,
|
||||
PULSE_CLIENT_NAME);
|
||||
|
||||
pa_format_info_set_rate(format, ao->samplerate);
|
||||
pa_format_info_set_channels(format, ao->channels.num);
|
||||
pa_format_info_set_channel_map(format, &map);
|
||||
|
||||
if (!pa_format_info_valid(format)) {
|
||||
MP_ERR(ao, "Invalid audio format\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
if (!(priv->stream = pa_stream_new_extended(priv->context, "audio stream",
|
||||
&format, 1, proplist)))
|
||||
if (!(priv->stream = pa_stream_new_with_proplist(priv->context,
|
||||
"audio stream", &ss,
|
||||
&map, proplist)))
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_proplist_free(proplist);
|
||||
proplist = NULL;
|
||||
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map map2;
|
||||
|
||||
if (pa_format_info_to_sample_spec(format, &ss, &map2) < 0)
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_stream_set_state_callback(priv->stream, stream_state_cb, ao);
|
||||
pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
|
||||
pa_stream_set_latency_update_callback(priv->stream,
|
||||
@@ -409,14 +376,10 @@ static int init(struct ao *ao)
|
||||
goto unlock_and_fail;
|
||||
|
||||
/* Wait until the stream is ready */
|
||||
while (1) {
|
||||
int state = pa_stream_get_state(priv->stream);
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
goto unlock_and_fail;
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
|
||||
if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
@@ -428,8 +391,6 @@ unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
fail:
|
||||
if (format)
|
||||
pa_format_info_free(format);
|
||||
if (priv->context) {
|
||||
if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED
|
||||
&& ao->probing))
|
||||
@@ -620,7 +581,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx,
|
||||
info_func, ao))) {
|
||||
GENERIC_ERR_MSG("pa_context_get_sink_input_info() failed");
|
||||
GENERIC_ERR_MSG("pa_stream_get_sink_input_info() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
// Warning: some information in pi might be unaccessible, because
|
||||
@@ -683,9 +644,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
|
||||
case AOCONTROL_UPDATE_STREAM_TITLE: {
|
||||
char *title = (char *)arg;
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
|
||||
+28
-14
@@ -52,26 +52,40 @@ static int set_format(struct ao *ao)
|
||||
case AF_FORMAT_S8:
|
||||
rsd_format = RSD_S8;
|
||||
break;
|
||||
case AF_FORMAT_S16:
|
||||
rsd_format = RSD_S16_NE;
|
||||
case AF_FORMAT_S16_LE:
|
||||
rsd_format = RSD_S16_LE;
|
||||
break;
|
||||
case AF_FORMAT_U16:
|
||||
rsd_format = RSD_U16_NE;
|
||||
case AF_FORMAT_S16_BE:
|
||||
rsd_format = RSD_S16_BE;
|
||||
break;
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_U24:
|
||||
rsd_format = RSD_S32_NE;
|
||||
ao->format = AF_FORMAT_S32;
|
||||
case AF_FORMAT_U16_LE:
|
||||
rsd_format = RSD_U16_LE;
|
||||
break;
|
||||
case AF_FORMAT_S32:
|
||||
rsd_format = RSD_S32_NE;
|
||||
case AF_FORMAT_U16_BE:
|
||||
rsd_format = RSD_U16_BE;
|
||||
break;
|
||||
case AF_FORMAT_U32:
|
||||
rsd_format = RSD_U32_NE;
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S24_BE:
|
||||
case AF_FORMAT_U24_LE:
|
||||
case AF_FORMAT_U24_BE:
|
||||
rsd_format = RSD_S32_LE;
|
||||
ao->format = AF_FORMAT_S32_LE;
|
||||
break;
|
||||
case AF_FORMAT_S32_LE:
|
||||
rsd_format = RSD_S32_LE;
|
||||
break;
|
||||
case AF_FORMAT_S32_BE:
|
||||
rsd_format = RSD_S32_BE;
|
||||
break;
|
||||
case AF_FORMAT_U32_LE:
|
||||
rsd_format = RSD_U32_LE;
|
||||
break;
|
||||
case AF_FORMAT_U32_BE:
|
||||
rsd_format = RSD_U32_BE;
|
||||
break;
|
||||
default:
|
||||
rsd_format = RSD_S16_NE;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
rsd_format = RSD_S16_LE;
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
return rsd_format;
|
||||
|
||||
+44
-32
@@ -38,20 +38,6 @@ struct priv
|
||||
float buflen;
|
||||
};
|
||||
|
||||
static int fmtmap[][2] = {
|
||||
{AF_FORMAT_U8, AUDIO_U8},
|
||||
{AF_FORMAT_S8, AUDIO_S8},
|
||||
{AF_FORMAT_U16, AUDIO_U16SYS},
|
||||
{AF_FORMAT_S16, AUDIO_S16SYS},
|
||||
#ifdef AUDIO_S32SYS
|
||||
{AF_FORMAT_S32, AUDIO_S32SYS},
|
||||
#endif
|
||||
#ifdef AUDIO_F32SYS
|
||||
{AF_FORMAT_FLOAT, AUDIO_F32SYS},
|
||||
#endif
|
||||
{0}
|
||||
};
|
||||
|
||||
static void audio_callback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
@@ -118,12 +104,26 @@ static int init(struct ao *ao)
|
||||
|
||||
SDL_AudioSpec desired, obtained;
|
||||
|
||||
desired.format = AUDIO_S16SYS;
|
||||
for (int n = 0; fmtmap[n][0]; n++) {
|
||||
if (ao->format == fmtmap[n][0]) {
|
||||
desired.format = fmtmap[n][1];
|
||||
break;
|
||||
}
|
||||
switch (ao->format) {
|
||||
case AF_FORMAT_U8: desired.format = AUDIO_U8; break;
|
||||
case AF_FORMAT_S8: desired.format = AUDIO_S8; break;
|
||||
case AF_FORMAT_U16_LE: desired.format = AUDIO_U16LSB; break;
|
||||
case AF_FORMAT_U16_BE: desired.format = AUDIO_U16MSB; break;
|
||||
default:
|
||||
case AF_FORMAT_S16_LE: desired.format = AUDIO_S16LSB; break;
|
||||
case AF_FORMAT_S16_BE: desired.format = AUDIO_S16MSB; break;
|
||||
#ifdef AUDIO_S32LSB
|
||||
case AF_FORMAT_S32_LE: desired.format = AUDIO_S32LSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_S32MSB
|
||||
case AF_FORMAT_S32_BE: desired.format = AUDIO_S32MSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32LSB
|
||||
case AF_FORMAT_FLOAT_LE: desired.format = AUDIO_F32LSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32MSB
|
||||
case AF_FORMAT_FLOAT_BE: desired.format = AUDIO_F32MSB; break;
|
||||
#endif
|
||||
}
|
||||
desired.freq = ao->samplerate;
|
||||
desired.channels = ao->channels.num;
|
||||
@@ -156,18 +156,30 @@ static int init(struct ao *ao)
|
||||
// large, this will help.
|
||||
ao->device_buffer = 3 * obtained.samples;
|
||||
|
||||
ao->format = 0;
|
||||
for (int n = 0; fmtmap[n][0]; n++) {
|
||||
if (obtained.format == fmtmap[n][1]) {
|
||||
ao->format = fmtmap[n][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ao->format) {
|
||||
if (!ao->probing)
|
||||
MP_ERR(ao, "could not find matching format\n");
|
||||
uninit(ao);
|
||||
return -1;
|
||||
switch (obtained.format) {
|
||||
case AUDIO_U8: ao->format = AF_FORMAT_U8; break;
|
||||
case AUDIO_S8: ao->format = AF_FORMAT_S8; break;
|
||||
case AUDIO_S16LSB: ao->format = AF_FORMAT_S16_LE; break;
|
||||
case AUDIO_S16MSB: ao->format = AF_FORMAT_S16_BE; break;
|
||||
case AUDIO_U16LSB: ao->format = AF_FORMAT_U16_LE; break;
|
||||
case AUDIO_U16MSB: ao->format = AF_FORMAT_U16_BE; break;
|
||||
#ifdef AUDIO_S32LSB
|
||||
case AUDIO_S32LSB: ao->format = AF_FORMAT_S32_LE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_S32MSB
|
||||
case AUDIO_S32MSB: ao->format = AF_FORMAT_S32_BE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32LSB
|
||||
case AUDIO_F32LSB: ao->format = AF_FORMAT_FLOAT_LE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32MSB
|
||||
case AUDIO_F32MSB: ao->format = AF_FORMAT_FLOAT_BE; break;
|
||||
#endif
|
||||
default:
|
||||
if (!ao->probing)
|
||||
MP_ERR(ao, "could not find matching format\n");
|
||||
uninit(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
|
||||
|
||||
+54
-59
@@ -33,7 +33,6 @@ struct priv {
|
||||
struct sio_hdl *hdl;
|
||||
struct sio_par par;
|
||||
int delay;
|
||||
bool playing;
|
||||
int vol;
|
||||
int havevol;
|
||||
#define SILENCE_NMAX 0x1000
|
||||
@@ -61,8 +60,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_FALSE;
|
||||
sio_setvol(p->hdl, vol->left * SIO_MAXVOL / 100);
|
||||
break;
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -75,7 +72,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
static void movecb(void *addr, int delta)
|
||||
{
|
||||
struct priv *p = addr;
|
||||
p->delay -= delta;
|
||||
p->delay -= delta * (int)(p->par.bps * p->par.pchan);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -110,20 +107,26 @@ static int init(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
struct af_to_par {
|
||||
int format, bits, sig;
|
||||
int format, bits, sig, le;
|
||||
} static const af_to_par[] = {
|
||||
{AF_FORMAT_U8, 8, 0},
|
||||
{AF_FORMAT_S8, 8, 1},
|
||||
{AF_FORMAT_U16, 16, 0},
|
||||
{AF_FORMAT_S16, 16, 1},
|
||||
{AF_FORMAT_U24, 24, 0},
|
||||
{AF_FORMAT_S24, 24, 1},
|
||||
{AF_FORMAT_U32, 32, 0},
|
||||
{AF_FORMAT_S32, 32, 1},
|
||||
{AF_FORMAT_U8, 8, 0, 0},
|
||||
{AF_FORMAT_S8, 8, 1, 0},
|
||||
{AF_FORMAT_U16_LE, 16, 0, 1},
|
||||
{AF_FORMAT_U16_BE, 16, 0, 0},
|
||||
{AF_FORMAT_S16_LE, 16, 1, 1},
|
||||
{AF_FORMAT_S16_BE, 16, 1, 0},
|
||||
{AF_FORMAT_U24_LE, 16, 0, 1},
|
||||
{AF_FORMAT_U24_BE, 24, 0, 0},
|
||||
{AF_FORMAT_S24_LE, 24, 1, 1},
|
||||
{AF_FORMAT_S24_BE, 24, 1, 0},
|
||||
{AF_FORMAT_U32_LE, 32, 0, 1},
|
||||
{AF_FORMAT_U32_BE, 32, 0, 0},
|
||||
{AF_FORMAT_S32_LE, 32, 1, 1},
|
||||
{AF_FORMAT_S32_BE, 32, 1, 0}
|
||||
}, *ap;
|
||||
int i;
|
||||
|
||||
p->hdl = sio_open(p->dev, SIO_PLAY, 1);
|
||||
p->hdl = sio_open(p->dev, SIO_PLAY, 0);
|
||||
if (p->hdl == NULL) {
|
||||
MP_ERR(ao, "can't open sndio %s\n", p->dev);
|
||||
goto error;
|
||||
@@ -144,7 +147,7 @@ static int init(struct ao *ao)
|
||||
p->par.bits = ap->bits;
|
||||
p->par.sig = ap->sig;
|
||||
if (ap->bits > 8)
|
||||
p->par.le = SIO_LE_NATIVE;
|
||||
p->par.le = ap->le;
|
||||
if (ap->bits != SIO_BPS(ap->bits))
|
||||
p->par.bps = ap->bits / 8;
|
||||
break;
|
||||
@@ -170,25 +173,30 @@ static int init(struct ao *ao)
|
||||
MP_ERR(ao, "couldn't get params\n");
|
||||
goto error;
|
||||
}
|
||||
if (p->par.bps > 1 && p->par.le != SIO_LE_NATIVE) {
|
||||
MP_ERR(ao, "swapped endian output not supported\n");
|
||||
goto error;
|
||||
}
|
||||
if (p->par.bits == 8 && p->par.bps == 1) {
|
||||
ao->format = p->par.sig ? AF_FORMAT_S8 : AF_FORMAT_U8;
|
||||
} else if (p->par.bits == 16 && p->par.bps == 2) {
|
||||
ao->format = p->par.sig ? AF_FORMAT_S16 : AF_FORMAT_U16;
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S16_LE : AF_FORMAT_S16_BE) :
|
||||
(p->par.le ? AF_FORMAT_U16_LE : AF_FORMAT_U16_BE);
|
||||
} else if ((p->par.bits == 24 || p->par.msb) && p->par.bps == 3) {
|
||||
ao->format = p->par.sig ? AF_FORMAT_S24 : AF_FORMAT_U24;
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S24_LE : AF_FORMAT_S24_BE) :
|
||||
(p->par.le ? AF_FORMAT_U24_LE : AF_FORMAT_U24_BE);
|
||||
} else if ((p->par.bits == 32 || p->par.msb) && p->par.bps == 4) {
|
||||
ao->format = p->par.sig ? AF_FORMAT_S32 : AF_FORMAT_U32;
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S32_LE : AF_FORMAT_S32_BE) :
|
||||
(p->par.le ? AF_FORMAT_U32_LE : AF_FORMAT_U32_BE);
|
||||
} else {
|
||||
MP_ERR(ao, "couldn't set format\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ao->bps = p->par.bps * p->par.pchan * p->par.rate;
|
||||
ao->no_persistent_volume = true;
|
||||
p->havevol = sio_onvol(p->hdl, volcb, p);
|
||||
sio_onmove(p->hdl, movecb, p);
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "init: couldn't start\n");
|
||||
|
||||
@@ -225,56 +233,46 @@ static void reset(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
if (p->playing) {
|
||||
MP_WARN(ao, "Blocking until remaining audio is played... (sndio design bug).\n");
|
||||
|
||||
p->playing = false;
|
||||
|
||||
if (!sio_stop(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't stop\n");
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't start\n");
|
||||
}
|
||||
if (!sio_stop(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't stop\n");
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't start\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* play given number of samples until sio_write() blocks
|
||||
* play given number of bytes until sio_write() blocks
|
||||
*/
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n;
|
||||
|
||||
n = sio_write(p->hdl, data[0], samples * ao->sstride) / ao->sstride;
|
||||
n = sio_write(p->hdl, data[0], samples * ao->sstride);
|
||||
p->delay += n;
|
||||
p->playing = true;
|
||||
/* on AOPLAY_FINAL_CHUNK, just let it underrun */
|
||||
return n;
|
||||
if (flags & AOPLAY_FINAL_CHUNK)
|
||||
reset(ao);
|
||||
return n / ao->sstride;
|
||||
}
|
||||
|
||||
/*
|
||||
* make libsndio call movecb()
|
||||
*/
|
||||
static void update(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
|
||||
while (poll(p->pfd, n, 0) < 0 && errno == EINTR) {}
|
||||
sio_revents(p->hdl, p->pfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* how many samples can be played without blocking
|
||||
* how many bytes can be played without blocking
|
||||
*/
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n;
|
||||
|
||||
update(ao);
|
||||
/*
|
||||
* call poll() and sio_revents(), so the
|
||||
* delay counter is updated
|
||||
*/
|
||||
n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
|
||||
while (poll(p->pfd, n, 0) < 0 && errno == EINTR)
|
||||
; /* nothing */
|
||||
sio_revents(p->hdl, p->pfd);
|
||||
|
||||
int samples = p->par.bufsz - p->delay;
|
||||
return samples / p->par.round * p->par.round;
|
||||
return (p->par.bufsz * p->par.pchan * p->par.bps - p->delay) / ao->sstride;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -283,10 +281,7 @@ static int get_space(struct ao *ao)
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
update(ao);
|
||||
|
||||
return p->delay / (double)p->par.rate;
|
||||
return (float)p->delay / (p->par.bps * p->par.pchan * p->par.rate);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -306,8 +301,8 @@ static void audio_resume(struct ao *ao)
|
||||
int n, count, todo;
|
||||
|
||||
/*
|
||||
* we want to start with buffers full, because mpv uses
|
||||
* get_delay() as clock, which would cause video to
|
||||
* we want to start with buffers full, because mplayer uses
|
||||
* get_space() pointer as clock, which would cause video to
|
||||
* accelerate while buffers are filled.
|
||||
*/
|
||||
todo = p->par.bufsz * p->par.pchan * p->par.bps;
|
||||
|
||||
@@ -194,6 +194,7 @@ static int init(struct ao *ao)
|
||||
wasapi_enumerate_devices(state->log);
|
||||
}
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
if (state->opt_exclusive) {
|
||||
state->share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
} else {
|
||||
@@ -283,8 +284,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL);
|
||||
|
||||
return CONTROL_OK;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
case AOCONTROL_UPDATE_STREAM_TITLE: {
|
||||
MP_VERBOSE(state, "Updating stream title to \"%s\"\n", (char*)arg);
|
||||
wchar_t *title = mp_from_utf8(NULL, (char*)arg);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <mmdeviceapi.h>
|
||||
#include <avrt.h>
|
||||
|
||||
#include "osdep/atomics.h"
|
||||
#include "compat/atomics.h"
|
||||
|
||||
typedef struct wasapi_state {
|
||||
struct mp_log *log;
|
||||
|
||||
+33
-23
@@ -142,36 +142,47 @@ static void set_format(WAVEFORMATEXTENSIBLE *wformat, WORD bytepersample,
|
||||
wformat->dwChannelMask = chanmask;
|
||||
}
|
||||
|
||||
static int format_set_bits(int old_format, int bits, int fp)
|
||||
{
|
||||
if (fp) {
|
||||
switch (bits) {
|
||||
case 64: return AF_FORMAT_DOUBLE;
|
||||
case 32: return AF_FORMAT_FLOAT;
|
||||
default: return 0;
|
||||
}
|
||||
static int format_set_bits(int old_format, int bits, int fp) {
|
||||
int format = old_format;
|
||||
format &= (~AF_FORMAT_BITS_MASK) & (~AF_FORMAT_POINT_MASK) & (~AF_FORMAT_SIGN_MASK);
|
||||
format |= AF_FORMAT_SI;
|
||||
|
||||
switch (bits) {
|
||||
case 32:
|
||||
format |= AF_FORMAT_32BIT;
|
||||
break;
|
||||
case 24:
|
||||
format |= AF_FORMAT_24BIT;
|
||||
break;
|
||||
case 16:
|
||||
format |= AF_FORMAT_16BIT;
|
||||
break;
|
||||
default:
|
||||
abort(); // (should be) unreachable
|
||||
}
|
||||
|
||||
return af_fmt_change_bits(old_format, bits);
|
||||
if (fp) {
|
||||
format |= AF_FORMAT_F;
|
||||
} else {
|
||||
format |= AF_FORMAT_I;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
static int set_ao_format(struct wasapi_state *state,
|
||||
struct ao *const ao,
|
||||
WAVEFORMATEXTENSIBLE wformat)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wformat) {
|
||||
// .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT
|
||||
int format = format_set_bits(ao->format,
|
||||
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
|
||||
|
||||
if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) {
|
||||
MP_ERR(ao, "unknown SubFormat %"PRIu32"\n",
|
||||
(uint32_t)wformat.SubFormat.Data1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT
|
||||
int format = format_set_bits(ao->format,
|
||||
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
|
||||
|
||||
if (!format)
|
||||
return 0;
|
||||
|
||||
ao->samplerate = wformat.Format.nSamplesPerSec;
|
||||
ao->bps = wformat.Format.nAvgBytesPerSec;
|
||||
ao->format = format;
|
||||
@@ -193,8 +204,6 @@ static int try_format(struct wasapi_state *state,
|
||||
set_format(&wformat, bits / 8, samplerate, channels.num, mp_chmap_to_waveext(&channels));
|
||||
|
||||
int af_format = format_set_bits(ao->format, bits, bits == 32);
|
||||
if (!af_format)
|
||||
return 0;
|
||||
|
||||
MP_VERBOSE(ao, "trying %dch %s @ %dhz\n",
|
||||
channels.num, af_fmt_to_str(af_format), samplerate);
|
||||
@@ -283,13 +292,14 @@ static int try_passthrough(struct wasapi_state *state,
|
||||
union WAVEFMT u;
|
||||
u.extensible = &wformat;
|
||||
|
||||
MP_VERBOSE(ao, "trying passthrough for %s...\n", af_fmt_to_str(ao->format));
|
||||
MP_VERBOSE(ao, "trying passthrough for %s...\n",
|
||||
af_fmt_to_str((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE));
|
||||
|
||||
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
|
||||
state->share_mode,
|
||||
u.ex, NULL);
|
||||
if (!FAILED(hr)) {
|
||||
ao->format = ao->format;
|
||||
ao->format = (ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE;
|
||||
state->format = wformat;
|
||||
return 1;
|
||||
}
|
||||
@@ -300,7 +310,7 @@ static int find_formats(struct ao *const ao)
|
||||
{
|
||||
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format) || ao->format == AF_FORMAT_MPEG2) {
|
||||
if (try_passthrough(state, ao))
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
|
||||
// Minimum buffer size in seconds.
|
||||
#define MIN_BUFFER 0.2
|
||||
|
||||
// If ao_get_delay() reaches this value after ao_play() was called with the
|
||||
// AOPLAY_FINAL_CHUNK flag set, the playback core expects that the audio has
|
||||
// all been played.
|
||||
@@ -41,6 +44,8 @@ struct ao {
|
||||
int num_planes;
|
||||
bool probing; // if true, don't fail loudly on init
|
||||
bool untimed; // don't assume realtime playback
|
||||
bool no_persistent_volume; // the AO does the equivalent of af_volume
|
||||
bool per_application_mixer; // like above, but volume persists (per app)
|
||||
int device_buffer; // device buffer in samples (guessed by
|
||||
// common init code if not set by driver)
|
||||
const struct ao_driver *api; // entrypoints to the wrapper (push.c/pull.c)
|
||||
@@ -51,7 +56,6 @@ struct ao {
|
||||
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
|
||||
|
||||
int buffer;
|
||||
double def_buffer;
|
||||
void *api_priv;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "compat/atomics.h"
|
||||
#include "misc/ring.h"
|
||||
|
||||
/*
|
||||
|
||||
+56
-61
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "compat/atomics.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
@@ -52,9 +52,11 @@ struct ao_push_state {
|
||||
|
||||
bool terminate;
|
||||
bool drain;
|
||||
bool wait_on_ao;
|
||||
bool buffers_full;
|
||||
bool avoid_ao_wait;
|
||||
bool still_playing;
|
||||
bool need_wakeup;
|
||||
bool requested_data;
|
||||
bool paused;
|
||||
|
||||
// Whether the current buffer contains the complete audio.
|
||||
@@ -120,9 +122,8 @@ static void reset(struct ao *ao)
|
||||
ao->driver->reset(ao);
|
||||
mp_audio_buffer_clear(p->buffer);
|
||||
p->paused = false;
|
||||
if (p->still_playing)
|
||||
wakeup_playthread(ao);
|
||||
p->still_playing = false;
|
||||
wakeup_playthread(ao);
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
}
|
||||
|
||||
@@ -176,17 +177,13 @@ static int unlocked_get_space(struct ao *ao)
|
||||
int space = mp_audio_buffer_get_write_available(p->buffer);
|
||||
if (ao->driver->get_space) {
|
||||
// The following code attempts to keep the total buffered audio to
|
||||
// ao->buffer in order to improve latency.
|
||||
// MIN_BUFFER/2+device_buffer in order to improve latency.
|
||||
int device_space = ao->driver->get_space(ao);
|
||||
int device_buffered = ao->device_buffer - device_space;
|
||||
int soft_buffered = mp_audio_buffer_samples(p->buffer);
|
||||
// The extra margin helps avoiding too many wakeups if the AO is fully
|
||||
// byte based and doesn't do proper chunked processing.
|
||||
int min_buffer = ao->buffer + 64;
|
||||
int missing = min_buffer - device_buffered - soft_buffered;
|
||||
// But always keep the device's buffer filled as much as we can.
|
||||
int device_missing = device_space - soft_buffered;
|
||||
missing = MPMAX(missing, device_missing);
|
||||
int min_buffer = MIN_BUFFER / 2 * ao->samplerate + ao->device_buffer;
|
||||
int total_buffer = device_buffered + soft_buffered;
|
||||
int missing = min_buffer - total_buffer;
|
||||
space = MPMIN(space, missing);
|
||||
space = MPMAX(0, space);
|
||||
}
|
||||
@@ -220,8 +217,6 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
int write_samples = mp_audio_buffer_get_write_available(p->buffer);
|
||||
write_samples = MPMIN(write_samples, samples);
|
||||
|
||||
MP_TRACE(ao, "samples=%d flags=%d r=%d\n", samples, flags, write_samples);
|
||||
|
||||
if (write_samples < samples)
|
||||
flags = flags & ~AOPLAY_FINAL_CHUNK;
|
||||
bool is_final = flags & AOPLAY_FINAL_CHUNK;
|
||||
@@ -242,8 +237,10 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
// If we don't have new data, the decoder thread basically promises it
|
||||
// will send new data as soon as it's available.
|
||||
if (got_data)
|
||||
if (got_data) {
|
||||
p->requested_data = false;
|
||||
wakeup_playthread(ao);
|
||||
}
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
return write_samples;
|
||||
}
|
||||
@@ -273,8 +270,8 @@ static void ao_play_data(struct ao *ao)
|
||||
}
|
||||
r = MPMAX(r, 0);
|
||||
// Probably can't copy the rest of the buffer due to period alignment.
|
||||
bool stuck_eof = r <= 0 && space >= max && data.samples > 0;
|
||||
if ((flags & AOPLAY_FINAL_CHUNK) && stuck_eof) {
|
||||
bool stuck = r <= 0 && space >= max && data.samples > 0;
|
||||
if ((flags & AOPLAY_FINAL_CHUNK) && stuck) {
|
||||
MP_ERR(ao, "Audio output driver seems to ignore AOPLAY_FINAL_CHUNK.\n");
|
||||
r = max;
|
||||
}
|
||||
@@ -284,23 +281,27 @@ static void ao_play_data(struct ao *ao)
|
||||
if (ao->driver->get_delay)
|
||||
p->expected_end_time += ao->driver->get_delay(ao);
|
||||
}
|
||||
// Nothing written, but more input data than space - this must mean the
|
||||
// AO's get_space() doesn't do period alignment correctly.
|
||||
bool stuck = r == 0 && max >= space && space > 0;
|
||||
if (stuck)
|
||||
MP_ERR(ao, "Audio output is reporting incorrect buffer status.\n");
|
||||
// Wait until space becomes available. Also wait if we actually wrote data,
|
||||
// so the AO wakes us up properly if it needs more data.
|
||||
p->wait_on_ao = space == 0 || r > 0 || stuck;
|
||||
// In both cases, we have to account for space!=0, but the AO not accepting
|
||||
// any new data (due to rounding to period boundaries).
|
||||
p->buffers_full = max >= space && r <= 0;
|
||||
p->avoid_ao_wait = (max == 0 && space > 0) || p->paused || stuck;
|
||||
p->still_playing |= r > 0;
|
||||
// If we just filled the AO completely (r == space), don't refill for a
|
||||
// while. Prevents wakeup feedback with byte-granular AOs.
|
||||
int needed = unlocked_get_space(ao);
|
||||
bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck;
|
||||
if (more)
|
||||
mp_input_wakeup(ao->input_ctx); // request more data
|
||||
MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa=%d needed=%d more=%d\n",
|
||||
max, flags, space, r, p->wait_on_ao, needed, more);
|
||||
MP_TRACE(ao, "in=%d, space=%d r=%d flags=%d aw=%d full=%d f=%d\n", max,
|
||||
space, r, flags, p->avoid_ao_wait, p->buffers_full, p->final_chunk);
|
||||
}
|
||||
|
||||
// Estimate when the AO needs data again.
|
||||
static double ao_estimate_timeout(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
double timeout = 0;
|
||||
if (p->buffers_full && ao->driver->get_delay) {
|
||||
timeout = ao->driver->get_delay(ao) - 0.050;
|
||||
// Keep extra safety margin if the buffers are large
|
||||
if (timeout > 0.100)
|
||||
timeout = MPMAX(timeout - 0.200, 0.100);
|
||||
}
|
||||
return MPMAX(timeout, ao->device_buffer * 0.75 / ao->samplerate);
|
||||
}
|
||||
|
||||
static void *playthread(void *arg)
|
||||
@@ -312,7 +313,16 @@ static void *playthread(void *arg)
|
||||
if (!p->paused)
|
||||
ao_play_data(ao);
|
||||
|
||||
if (p->drain && (!p->wait_on_ao || p->paused)) {
|
||||
// Request new data from decoder if buffer goes below "full".
|
||||
// Allow a small margin of missing data for AOs that use timeouts.
|
||||
double margin = ao->driver->wait ? 0 : ao->device_buffer / 8;
|
||||
if (!p->buffers_full && unlocked_get_space(ao) > margin) {
|
||||
if (!p->requested_data)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
p->requested_data = true;
|
||||
}
|
||||
|
||||
if (p->drain && (p->avoid_ao_wait || p->paused)) {
|
||||
if (ao->driver->drain)
|
||||
ao->driver->drain(ao);
|
||||
p->drain = false;
|
||||
@@ -321,7 +331,7 @@ static void *playthread(void *arg)
|
||||
|
||||
if (!p->need_wakeup) {
|
||||
MP_STATS(ao, "start audio wait");
|
||||
if (!p->wait_on_ao || p->paused) {
|
||||
if (p->avoid_ao_wait || p->paused) {
|
||||
// Avoid busy waiting, because the audio API will still report
|
||||
// that it needs new data, even if we're not ready yet, or if
|
||||
// get_space() decides that the amount of audio buffered in the
|
||||
@@ -338,7 +348,7 @@ static void *playthread(void *arg)
|
||||
p->still_playing = false;
|
||||
}
|
||||
|
||||
if (was_playing && !p->still_playing)
|
||||
if (!p->requested_data || (was_playing && !p->still_playing))
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
|
||||
if (p->still_playing && timeout > 0) {
|
||||
@@ -349,10 +359,7 @@ static void *playthread(void *arg)
|
||||
} else {
|
||||
if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) {
|
||||
// Fallback to guessing.
|
||||
double timeout = 0;
|
||||
if (ao->driver->get_delay)
|
||||
timeout = ao->driver->get_delay(ao);
|
||||
timeout *= 0.25; // wake up if 25% played
|
||||
double timeout = ao_estimate_timeout(ao);
|
||||
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
||||
}
|
||||
}
|
||||
@@ -364,20 +371,6 @@ static void *playthread(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void destroy_no_thread(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
|
||||
ao->driver->uninit(ao);
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
close(p->wakeup_pipe[n]);
|
||||
|
||||
pthread_cond_destroy(&p->wakeup);
|
||||
pthread_cond_destroy(&p->wakeup_drain);
|
||||
pthread_mutex_destroy(&p->lock);
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
@@ -389,7 +382,14 @@ static void uninit(struct ao *ao)
|
||||
|
||||
pthread_join(p->thread, NULL);
|
||||
|
||||
destroy_no_thread(ao);
|
||||
ao->driver->uninit(ao);
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
close(p->wakeup_pipe[n]);
|
||||
|
||||
pthread_cond_destroy(&p->wakeup);
|
||||
pthread_cond_destroy(&p->wakeup_drain);
|
||||
pthread_mutex_destroy(&p->lock);
|
||||
}
|
||||
|
||||
static int init(struct ao *ao)
|
||||
@@ -401,11 +401,6 @@ static int init(struct ao *ao)
|
||||
pthread_cond_init(&p->wakeup_drain, NULL);
|
||||
mp_make_wakeup_pipe(p->wakeup_pipe);
|
||||
|
||||
if (ao->device_buffer <= 0) {
|
||||
MP_FATAL(ao, "Couldn't probe device buffer size.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->buffer = mp_audio_buffer_create(ao);
|
||||
mp_audio_buffer_reinit_fmt(p->buffer, ao->format,
|
||||
&ao->channels, ao->samplerate);
|
||||
@@ -414,7 +409,7 @@ static int init(struct ao *ao)
|
||||
goto err;
|
||||
return 0;
|
||||
err:
|
||||
destroy_no_thread(ao);
|
||||
ao->driver->uninit(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "misc/ctype.h"
|
||||
#include "bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
int bstrcmp(struct bstr str1, struct bstr str2)
|
||||
{
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "osdep/compiler.h"
|
||||
#include "compat/compiler.h"
|
||||
|
||||
/* NOTE: 'len' is size_t, but most string-handling functions below assume
|
||||
* that input size has been sanity checked and len fits in an int.
|
||||
@@ -117,8 +117,6 @@ void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
|
||||
if (mpkt && mpkt->avpacket) {
|
||||
dst->side_data = mpkt->avpacket->side_data;
|
||||
dst->side_data_elems = mpkt->avpacket->side_data_elems;
|
||||
if (dst->data == mpkt->avpacket->data)
|
||||
dst->buf = mpkt->avpacket->buf;
|
||||
}
|
||||
if (mpkt && tb && tb->num > 0 && tb->den > 0)
|
||||
dst->duration = mpkt->duration / av_q2d(*tb);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include "talloc.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "common/msg.h"
|
||||
#include "codecs.h"
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#define appendf(ptr, ...) \
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "osdep/compiler.h"
|
||||
#include "compat/compiler.h"
|
||||
#include "talloc.h"
|
||||
|
||||
// double should be able to represent this exactly
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
struct mpv_global {
|
||||
struct MPOpts *opts;
|
||||
struct mp_log *log;
|
||||
|
||||
int (*stream_interrupt_cb)(void *ctx);
|
||||
void *stream_interrupt_cb_ctx;
|
||||
};
|
||||
|
||||
#endif
|
||||
+31
-40
@@ -27,8 +27,8 @@
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "compat/atomics.h"
|
||||
#include "common/common.h"
|
||||
#include "common/global.h"
|
||||
#include "misc/ring.h"
|
||||
@@ -158,10 +158,16 @@ static void prepare_status_line(struct mp_log_root *root, char *new_status)
|
||||
size_t clear_lines = MPMIN(MPMAX(new_lines, old_lines), root->blank_lines);
|
||||
|
||||
// clear the status line itself
|
||||
fprintf(f, "\r\033[K");
|
||||
if (terminal_erase_to_end_of_line[0]) {
|
||||
fprintf(f, "\r%s", terminal_erase_to_end_of_line);
|
||||
} else {
|
||||
// This code is for MS windows (no ANSI control sequences)
|
||||
get_screen_size();
|
||||
fprintf(f, "\r%*s\r", screen_width - 1, "");
|
||||
}
|
||||
// and clear all previous old lines
|
||||
for (size_t n = 1; n < clear_lines; n++)
|
||||
fprintf(f, "\033[A\r\033[K");
|
||||
fprintf(f, "%s\r%s", terminal_cursor_up, terminal_erase_to_end_of_line);
|
||||
// skip "unused" blank lines, so that status is aligned to term bottom
|
||||
for (size_t n = new_lines; n < clear_lines; n++)
|
||||
fprintf(f, "\n");
|
||||
@@ -194,20 +200,10 @@ bool mp_msg_has_status_line(struct mpv_global *global)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void set_term_color(FILE *stream, int c)
|
||||
{
|
||||
if (c == -1) {
|
||||
fprintf(stream, "\033[0m");
|
||||
} else {
|
||||
fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void set_msg_color(FILE* stream, int lev)
|
||||
{
|
||||
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
|
||||
set_term_color(stream, v_colors[lev]);
|
||||
terminal_set_foreground_color(stream, v_colors[lev]);
|
||||
}
|
||||
|
||||
static void pretty_print_module(FILE* stream, const char *prefix, bool use_color, int lev)
|
||||
@@ -218,12 +214,12 @@ static void pretty_print_module(FILE* stream, const char *prefix, bool use_color
|
||||
unsigned int mod = 0;
|
||||
for (int i = 0; i < prefix_len; ++i)
|
||||
mod = mod * 33 + prefix[i];
|
||||
set_term_color(stream, (mod + 1) % 15 + 1);
|
||||
terminal_set_foreground_color(stream, (mod + 1) % 15 + 1);
|
||||
}
|
||||
|
||||
fprintf(stream, "%10s", prefix);
|
||||
if (use_color)
|
||||
set_term_color(stream, -1);
|
||||
terminal_set_foreground_color(stream, -1);
|
||||
fprintf(stream, ": ");
|
||||
if (use_color)
|
||||
set_msg_color(stream, lev);
|
||||
@@ -295,7 +291,7 @@ static void print_msg_on_terminal(struct mp_log *log, int lev, char *text)
|
||||
fprintf(stream, "%s", terminate);
|
||||
|
||||
if (root->color)
|
||||
set_term_color(stream, -1);
|
||||
terminal_set_foreground_color(stream, -1);
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
@@ -367,39 +363,34 @@ void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va)
|
||||
// parent's name. If the name starts with "/", the parent's name is not
|
||||
// prefixed (except in verbose mode), and if it starts with "!", the name is
|
||||
// not printed at all (except in verbose mode).
|
||||
// If name is NULL, the parent's name/prefix is used.
|
||||
// Thread-safety: fully thread-safe, but keep in mind that talloc is not (so
|
||||
// talloc_ctx must be owned by the current thread).
|
||||
struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent,
|
||||
const char *name)
|
||||
{
|
||||
assert(parent);
|
||||
assert(name);
|
||||
struct mp_log *log = talloc_zero(talloc_ctx, struct mp_log);
|
||||
if (!parent->root)
|
||||
return log; // same as null_log
|
||||
log->root = parent->root;
|
||||
if (name) {
|
||||
if (name[0] == '!') {
|
||||
name = &name[1];
|
||||
} else if (name[0] == '/') {
|
||||
name = &name[1];
|
||||
log->prefix = talloc_strdup(log, name);
|
||||
} else {
|
||||
log->prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
}
|
||||
log->verbose_prefix = parent->prefix
|
||||
if (name[0] == '!') {
|
||||
name = &name[1];
|
||||
} else if (name[0] == '/') {
|
||||
name = &name[1];
|
||||
log->prefix = talloc_strdup(log, name);
|
||||
} else {
|
||||
log->prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
if (log->prefix && !log->prefix[0])
|
||||
log->prefix = NULL;
|
||||
if (!log->verbose_prefix[0])
|
||||
log->verbose_prefix = "global";
|
||||
} else {
|
||||
log->prefix = talloc_strdup(log, parent->prefix);
|
||||
log->verbose_prefix = talloc_strdup(log, parent->verbose_prefix);
|
||||
}
|
||||
log->verbose_prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
if (log->prefix && !log->prefix[0])
|
||||
log->prefix = NULL;
|
||||
if (!log->verbose_prefix[0])
|
||||
log->verbose_prefix = "global";
|
||||
return log;
|
||||
}
|
||||
|
||||
@@ -437,8 +428,8 @@ void mp_msg_update_msglevels(struct mpv_global *global)
|
||||
root->use_terminal = opts->use_terminal;
|
||||
root->show_time = opts->msg_time;
|
||||
if (root->use_terminal) {
|
||||
root->color = opts->msg_color && isatty(STDOUT_FILENO);
|
||||
root->termosd = isatty(STDERR_FILENO);
|
||||
root->color = opts->msg_color && isatty(fileno(stdout));
|
||||
root->termosd = isatty(fileno(stderr));
|
||||
}
|
||||
|
||||
talloc_free(root->msglevels);
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "osdep/compiler.h"
|
||||
#include "compat/compiler.h"
|
||||
|
||||
struct mp_log;
|
||||
|
||||
|
||||
+2
-11
@@ -30,8 +30,7 @@
|
||||
struct playlist_entry *playlist_entry_new(const char *filename)
|
||||
{
|
||||
struct playlist_entry *e = talloc_zero(NULL, struct playlist_entry);
|
||||
char *local_filename = mp_file_url_to_filename(e, bstr0(filename));
|
||||
e->filename = local_filename ? local_filename : talloc_strdup(e, filename);
|
||||
e->filename = talloc_strdup(e, filename);
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -106,18 +105,10 @@ static void playlist_unlink(struct playlist *pl, struct playlist_entry *entry)
|
||||
entry->pl = NULL;
|
||||
}
|
||||
|
||||
void playlist_entry_unref(struct playlist_entry *e)
|
||||
{
|
||||
e->reserved--;
|
||||
if (e->reserved < 0)
|
||||
talloc_free(e);
|
||||
}
|
||||
|
||||
void playlist_remove(struct playlist *pl, struct playlist_entry *entry)
|
||||
{
|
||||
playlist_unlink(pl, entry);
|
||||
entry->removed = true;
|
||||
playlist_entry_unref(entry);
|
||||
talloc_free(entry);
|
||||
}
|
||||
|
||||
void playlist_clear(struct playlist *pl)
|
||||
|
||||
+1
-14
@@ -19,7 +19,7 @@
|
||||
#define MPLAYER_PLAYLIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
struct playlist_param {
|
||||
bstr name, value;
|
||||
@@ -41,17 +41,6 @@ struct playlist_entry {
|
||||
bool playback_short : 1;
|
||||
// Set to true if not at least 1 frame (audio or video) could be played.
|
||||
bool init_failed : 1;
|
||||
// Entry was removed with playlist_remove (etc.), but not deallocated.
|
||||
bool removed : 1;
|
||||
// Additional refcount. Normally (reserved==0), the entry is owned by the
|
||||
// playlist, and this can be used to keep the entry alive.
|
||||
int reserved;
|
||||
// Used to reject loading of unsafe entries from external playlists.
|
||||
// Can have any of the following bit flags set:
|
||||
// STREAM_SAFE_ONLY: only allow streams marked with is_safe
|
||||
// STREAM_NETWORK_ONLY: only allow streams marked with is_network
|
||||
// The value 0 allows everything.
|
||||
int stream_flags;
|
||||
};
|
||||
|
||||
struct playlist {
|
||||
@@ -94,6 +83,4 @@ struct playlist_entry *playlist_entry_from_index(struct playlist *pl, int index)
|
||||
struct mpv_global;
|
||||
struct playlist *playlist_parse_file(const char *file, struct mpv_global *global);
|
||||
|
||||
void playlist_entry_unref(struct playlist_entry *e);
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <libavutil/dict.h>
|
||||
#include "tags.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
void mp_tags_set_str(struct mp_tags *tags, const char *key, const char *value)
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef MP_TAGS_H
|
||||
#define MP_TAGS_H
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
struct mp_tags {
|
||||
char **keys;
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,26 +1,24 @@
|
||||
/*
|
||||
* This file is part of mpv video player.
|
||||
* Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* 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_WAYLAND_MEMFILE_H
|
||||
#define MPLAYER_WAYLAND_MEMFILE_H
|
||||
#ifndef MPV_LIBAV_COMPAT_H
|
||||
#define MPV_LIBAV_COMPAT_H
|
||||
|
||||
// create file decsriptor to memory space without filesystem representation
|
||||
// truncates to size immediately
|
||||
int memfile_create(off_t size);
|
||||
// There's nothing here. But for how long?
|
||||
|
||||
#endif /* MPLAYER_WAYLAND_MEMFILE_H */
|
||||
#endif /* MPV_LIBAV_COMPAT_H */
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_MPBSWAP_H
|
||||
#define MPLAYER_MPBSWAP_H
|
||||
|
||||
#include <libavutil/bswap.h>
|
||||
|
||||
#define bswap_16(x) av_bswap16(x)
|
||||
#define bswap_32(x) av_bswap32(x)
|
||||
#define be2me_16(x) av_be2ne16(x)
|
||||
#define be2me_32(x) av_be2ne32(x)
|
||||
#define le2me_16(x) av_le2ne16(x)
|
||||
#define le2me_32(x) av_le2ne32(x)
|
||||
#define le2me_64(x) av_le2ne64(x)
|
||||
|
||||
#endif /* MPLAYER_MPBSWAP_H */
|
||||
+6
-44
@@ -97,6 +97,12 @@ static const struct mp_codec_tag mp_audio_codec_tags[] = {
|
||||
{MKTAG('s', 'a', 'm', 'r'), "amr_nb"},
|
||||
{MKTAG('s', 'a', 'w', 'b'), "amr_wb"},
|
||||
{MKTAG('v', 'r', 'b', 's'), "vorbis"},
|
||||
// Special cased in ad_lavc:
|
||||
{0 , "pcm"},
|
||||
{0x1 , "pcm"}, // lavf: pcm_s16le
|
||||
{0x3 , "pcm"}, // lavf: pcm_f32le
|
||||
{0xfffe , "pcm"},
|
||||
{MKTAG('t', 'w', 'o', 's'), "pcm"},
|
||||
// ------- internal mplayer FourCCs ------
|
||||
{MKTAG('O', 'p', 'u', 's'), "opus"}, // demux_mkv.c
|
||||
{MKTAG('a', 'L', 'a', 'C'), "alac"}, // demux_mkv.c
|
||||
@@ -352,28 +358,6 @@ static const char *lookup_tag(const struct mp_codec_tag *mp_table,
|
||||
return id == AV_CODEC_ID_NONE ? NULL : mp_codec_from_av_codec_id(id);
|
||||
}
|
||||
|
||||
static const char *pcm_le[] = {"pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"};
|
||||
static const char *pcm_be[] = {"pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"};
|
||||
|
||||
static const char *map_audio_pcm_tag(uint32_t tag, int bits)
|
||||
{
|
||||
int bytes = (bits + 7) / 8;
|
||||
switch (tag) {
|
||||
case 0x0: // Microsoft PCM
|
||||
case 0x1:
|
||||
case 0xfffe: // MS PCM, Extended
|
||||
return bytes >= 1 && bytes <= 4 ? pcm_le[bytes - 1] : NULL;
|
||||
case 0x3: // IEEE float
|
||||
return bits == 64 ? "pcm_f64le" : "pcm_f32le";
|
||||
case 0x20776172:// 'raw '
|
||||
return bits == 8 ? "pcm_u8" : "pcm_s16be";
|
||||
case MKTAG('t', 'w', 'o', 's'): // demux_mkv.c internal
|
||||
return bytes >= 1 && bytes <= 4 ? pcm_be[bytes - 1] : NULL;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void mp_set_codec_from_tag(struct sh_stream *sh)
|
||||
{
|
||||
switch (sh->type) {
|
||||
@@ -386,12 +370,6 @@ void mp_set_codec_from_tag(struct sh_stream *sh)
|
||||
sh->codec = lookup_tag(mp_audio_codec_tags,
|
||||
avformat_get_riff_audio_tags(),
|
||||
sh->format);
|
||||
if (sh->audio && sh->audio->bits_per_coded_sample) {
|
||||
const char *codec =
|
||||
map_audio_pcm_tag(sh->format, sh->audio->bits_per_coded_sample);
|
||||
if (codec)
|
||||
sh->codec = codec;
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
@@ -405,19 +383,3 @@ uint32_t mp_video_fourcc_alias(uint32_t fourcc)
|
||||
}
|
||||
return fourcc;
|
||||
}
|
||||
|
||||
void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
|
||||
bool is_be)
|
||||
{
|
||||
// This uses libavcodec pcm codec names, e.g. "pcm_u16le".
|
||||
char codec[64] = "pcm_";
|
||||
if (is_float) {
|
||||
mp_snprintf_cat(codec, sizeof(codec), "f");
|
||||
} else {
|
||||
mp_snprintf_cat(codec, sizeof(codec), sign ? "s" : "u");
|
||||
}
|
||||
mp_snprintf_cat(codec, sizeof(codec), "%d", bits);
|
||||
if (bits != 8)
|
||||
mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le");
|
||||
sh->codec = talloc_strdup(sh->audio, codec);
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
#define MP_CODEC_TAGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint32_t mp_video_fourcc_alias(uint32_t fourcc);
|
||||
|
||||
@@ -27,7 +26,4 @@ struct sh_stream;
|
||||
|
||||
void mp_set_codec_from_tag(struct sh_stream *sh);
|
||||
|
||||
void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
|
||||
bool is_be);
|
||||
|
||||
#endif
|
||||
+21
-57
@@ -109,9 +109,7 @@ struct demux_internal {
|
||||
bool warned_queue_overflow;
|
||||
bool last_eof; // last actual global EOF status
|
||||
bool eof; // whether we're in EOF state (reset for retry)
|
||||
bool idle;
|
||||
bool autoselect;
|
||||
double min_secs;
|
||||
int min_packs;
|
||||
int min_bytes;
|
||||
|
||||
@@ -139,16 +137,10 @@ struct demux_stream {
|
||||
bool eof; // end of demuxed stream? (true if all buffer empty)
|
||||
size_t packs; // number of packets in buffer
|
||||
size_t bytes; // total bytes of packets in buffer
|
||||
double base_ts; // timestamp of the last packet returned to decoder
|
||||
double last_ts; // timestamp of the last packet added to queue
|
||||
struct demux_packet *head;
|
||||
struct demux_packet *tail;
|
||||
};
|
||||
|
||||
// If one of the values is NOPTS, always pick the other one.
|
||||
#define MP_PTS_MIN(a, b) ((a) == MP_NOPTS_VALUE || ((a) > (b)) ? (b) : (a))
|
||||
#define MP_PTS_MAX(a, b) ((a) == MP_NOPTS_VALUE || ((a) < (b)) ? (b) : (a))
|
||||
|
||||
static void demuxer_sort_chapters(demuxer_t *demuxer);
|
||||
static void *demux_thread(void *pctx);
|
||||
static void update_cache(struct demux_internal *in);
|
||||
@@ -165,7 +157,6 @@ static void ds_flush(struct demux_stream *ds)
|
||||
ds->head = ds->tail = NULL;
|
||||
ds->packs = 0;
|
||||
ds->bytes = 0;
|
||||
ds->last_ts = ds->base_ts = MP_NOPTS_VALUE;
|
||||
ds->eof = false;
|
||||
ds->active = false;
|
||||
}
|
||||
@@ -303,7 +294,6 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
// first packet in stream
|
||||
ds->head = ds->tail = dp;
|
||||
}
|
||||
|
||||
// obviously not true anymore
|
||||
ds->eof = false;
|
||||
in->last_eof = in->eof = false;
|
||||
@@ -313,17 +303,11 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
if (stream->type != STREAM_VIDEO && dp->pts == MP_NOPTS_VALUE)
|
||||
dp->pts = dp->dts;
|
||||
|
||||
double ts = dp->dts == MP_NOPTS_VALUE ? dp->pts : dp->dts;
|
||||
if (ts != MP_NOPTS_VALUE && (ts > ds->last_ts || ts + 10 < ds->last_ts))
|
||||
ds->last_ts = ts;
|
||||
if (ds->base_ts == MP_NOPTS_VALUE)
|
||||
ds->base_ts = ds->last_ts;
|
||||
|
||||
MP_DBG(in, "append packet to %s: size=%d pts=%f dts=%f pos=%"PRIi64" "
|
||||
"[num=%zd size=%zd]\n", stream_type_name(stream->type),
|
||||
dp->len, dp->pts, dp->dts, dp->pos, ds->packs, ds->bytes);
|
||||
|
||||
if (ds->in->wakeup_cb && !ds->head->next)
|
||||
if (ds->in->wakeup_cb)
|
||||
ds->in->wakeup_cb(ds->in->wakeup_cb_ctx);
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
@@ -334,7 +318,6 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
static bool read_packet(struct demux_internal *in)
|
||||
{
|
||||
in->eof = false;
|
||||
in->idle = true;
|
||||
|
||||
// Check if we need to read a new packet. We do this if all queues are below
|
||||
// the minimum, or if a stream explicitly needs new packets. Also includes
|
||||
@@ -343,12 +326,10 @@ static bool read_packet(struct demux_internal *in)
|
||||
size_t packs = 0, bytes = 0;
|
||||
for (int n = 0; n < in->d_buffer->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_buffer->streams[n]->ds;
|
||||
active |= ds->active;
|
||||
active |= ds->selected && ds->active;
|
||||
read_more |= ds->active && !ds->head;
|
||||
packs += ds->packs;
|
||||
bytes += ds->bytes;
|
||||
if (ds->active && ds->last_ts != MP_NOPTS_VALUE && in->min_secs > 0)
|
||||
read_more |= ds->last_ts - ds->base_ts < in->min_secs;
|
||||
}
|
||||
MP_DBG(in, "packets=%zd, bytes=%zd, active=%d, more=%d\n",
|
||||
packs, bytes, active, read_more);
|
||||
@@ -379,7 +360,6 @@ static bool read_packet(struct demux_internal *in)
|
||||
|
||||
// Actually read a packet. Drop the lock while doing so, because waiting
|
||||
// for disk or network I/O can take time.
|
||||
in->idle = false;
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
struct demuxer *demux = in->d_thread;
|
||||
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
|
||||
@@ -495,12 +475,8 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
|
||||
ds->bytes -= pkt->len;
|
||||
ds->packs--;
|
||||
|
||||
double ts = pkt->dts == MP_NOPTS_VALUE ? pkt->pts : pkt->dts;
|
||||
if (ts != MP_NOPTS_VALUE)
|
||||
ds->base_ts = ts;
|
||||
|
||||
// This implies this function is actually called from "the" user thread.
|
||||
if (pkt->pos >= ds->in->d_user->filepos)
|
||||
if (pkt && pkt->pos >= 0)
|
||||
ds->in->d_user->filepos = pkt->pos;
|
||||
|
||||
return pkt;
|
||||
@@ -582,6 +558,18 @@ bool demux_has_packet(struct sh_stream *sh)
|
||||
return has_packet;
|
||||
}
|
||||
|
||||
// Return whether EOF was returned with an earlier packet read.
|
||||
bool demux_stream_eof(struct sh_stream *sh)
|
||||
{
|
||||
bool eof = false;
|
||||
if (sh) {
|
||||
pthread_mutex_lock(&sh->ds->in->lock);
|
||||
eof = sh->ds->eof && !sh->ds->head;
|
||||
pthread_mutex_unlock(&sh->ds->in->lock);
|
||||
}
|
||||
return eof;
|
||||
}
|
||||
|
||||
// Read and return any packet we find.
|
||||
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
|
||||
{
|
||||
@@ -815,8 +803,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
.d_thread = talloc(demuxer, struct demuxer),
|
||||
.d_buffer = talloc(demuxer, struct demuxer),
|
||||
.d_user = demuxer,
|
||||
.min_secs = stream->uncached_stream ? demuxer->opts->demuxer_min_secs_cache
|
||||
: demuxer->opts->demuxer_min_secs,
|
||||
.min_packs = demuxer->opts->demuxer_min_packs,
|
||||
.min_bytes = demuxer->opts->demuxer_min_bytes,
|
||||
};
|
||||
@@ -925,8 +911,6 @@ static void flush_locked(demuxer_t *demuxer)
|
||||
demuxer->in->warned_queue_overflow = false;
|
||||
demuxer->in->eof = false;
|
||||
demuxer->in->last_eof = false;
|
||||
demuxer->in->idle = true;
|
||||
demuxer->filepos = -1; // implicitly synchronized
|
||||
}
|
||||
|
||||
// clear the packet queues
|
||||
@@ -966,6 +950,11 @@ int demux_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *demux_info_get(demuxer_t *demuxer, const char *opt)
|
||||
{
|
||||
return mp_tags_get_str(demuxer->metadata, opt);
|
||||
}
|
||||
|
||||
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
|
||||
enum stream_type t, int id)
|
||||
{
|
||||
@@ -1117,10 +1106,6 @@ static void update_cache(struct demux_internal *in)
|
||||
// must be called locked
|
||||
static int cached_stream_control(struct demux_internal *in, int cmd, void *arg)
|
||||
{
|
||||
// If the cache is active, wake up the thread to possibly update cache state.
|
||||
if (in->stream_cache_size >= 0)
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
|
||||
switch (cmd) {
|
||||
case STREAM_CTRL_GET_CACHE_SIZE:
|
||||
if (in->stream_cache_size < 0)
|
||||
@@ -1165,29 +1150,7 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
|
||||
}
|
||||
case DEMUXER_CTRL_SWITCHED_TRACKS:
|
||||
in->tracks_switched = true;
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
return DEMUXER_CTRL_OK;
|
||||
case DEMUXER_CTRL_GET_READER_STATE: {
|
||||
struct demux_ctrl_reader_state *r = arg;
|
||||
*r = (struct demux_ctrl_reader_state){
|
||||
.eof = in->last_eof,
|
||||
.ts_range = {MP_NOPTS_VALUE, MP_NOPTS_VALUE},
|
||||
.ts_duration = -1,
|
||||
};
|
||||
for (int n = 0; n < in->d_user->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_user->streams[n]->ds;
|
||||
if (ds->active) {
|
||||
r->underrun |= !ds->head;
|
||||
r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
|
||||
r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
|
||||
}
|
||||
}
|
||||
r->idle = (in->idle && !r->underrun) || r->eof;
|
||||
r->underrun &= !r->idle;
|
||||
if (r->ts_range[0] != MP_NOPTS_VALUE && r->ts_range[1] != MP_NOPTS_VALUE)
|
||||
r->ts_duration = r->ts_range[1] - r->ts_range[0];
|
||||
return DEMUXER_CTRL_OK;
|
||||
}
|
||||
}
|
||||
return DEMUXER_CTRL_DONTKNOW;
|
||||
}
|
||||
@@ -1198,6 +1161,7 @@ int demux_control(demuxer_t *demuxer, int cmd, void *arg)
|
||||
|
||||
if (in->threading) {
|
||||
pthread_mutex_lock(&in->lock);
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
int cr = cached_demux_control(in, cmd, arg);
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
if (cr != DEMUXER_CTRL_DONTKNOW)
|
||||
|
||||
+9
-8
@@ -25,7 +25,7 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "common/common.h"
|
||||
#include "common/tags.h"
|
||||
#include "packet.h"
|
||||
@@ -35,6 +35,11 @@
|
||||
// and the demuxer pretends EOF was reached.
|
||||
#define MAX_PACKS 16000
|
||||
#define MAX_PACK_BYTES (400 * 1024 * 1024)
|
||||
// Minimum total size of packets queued - the demuxer thread will read more
|
||||
// packets, until either number or total size of the packets exceed the minimum.
|
||||
// This can actually be configured with command line options.
|
||||
#define MIN_PACKS 64
|
||||
#define MIN_PACK_BYTES (5 * 1024 * 1024)
|
||||
|
||||
enum demuxer_type {
|
||||
DEMUXER_TYPE_GENERIC = 0,
|
||||
@@ -54,13 +59,6 @@ enum demux_ctrl {
|
||||
DEMUXER_CTRL_RESYNC,
|
||||
DEMUXER_CTRL_IDENTIFY_PROGRAM,
|
||||
DEMUXER_CTRL_STREAM_CTRL,
|
||||
DEMUXER_CTRL_GET_READER_STATE,
|
||||
};
|
||||
|
||||
struct demux_ctrl_reader_state {
|
||||
bool eof, underrun, idle;
|
||||
double ts_range[2]; // start, end
|
||||
double ts_duration;
|
||||
};
|
||||
|
||||
struct demux_ctrl_stream_ctrl {
|
||||
@@ -241,6 +239,7 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
|
||||
bool demux_stream_is_selected(struct sh_stream *stream);
|
||||
double demux_get_next_pts(struct sh_stream *sh);
|
||||
bool demux_has_packet(struct sh_stream *sh);
|
||||
bool demux_stream_eof(struct sh_stream *sh);
|
||||
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
|
||||
|
||||
struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
|
||||
@@ -256,6 +255,8 @@ void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *c
|
||||
void demux_flush(struct demuxer *demuxer);
|
||||
int demux_seek(struct demuxer *demuxer, float rel_seek_secs, int flags);
|
||||
|
||||
char *demux_info_get(struct demuxer *demuxer, const char *opt);
|
||||
|
||||
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
|
||||
|
||||
void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "demux.h"
|
||||
#include "stream/stream.h"
|
||||
|
||||
|
||||
+2
-11
@@ -135,23 +135,20 @@ static void add_streams(demuxer_t *demuxer)
|
||||
}
|
||||
}
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, src->type);
|
||||
if (!sh)
|
||||
break;
|
||||
assert(p->num_streams == n); // directly mapped
|
||||
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
|
||||
// Copy all stream fields that might be relevant
|
||||
sh->codec = talloc_strdup(sh, src->codec);
|
||||
sh->format = src->format;
|
||||
sh->lav_headers = src->lav_headers;
|
||||
if (src->video) {
|
||||
if (sh && src->video) {
|
||||
double ar;
|
||||
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
|
||||
== STREAM_OK)
|
||||
sh->video->aspect = ar;
|
||||
}
|
||||
if (src->audio)
|
||||
if (sh && src->audio)
|
||||
sh->audio = src->audio;
|
||||
get_disc_lang(demuxer->stream, sh);
|
||||
}
|
||||
reselect_streams(demuxer);
|
||||
}
|
||||
@@ -311,12 +308,6 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
|
||||
// Can be seekable even if the stream isn't.
|
||||
demuxer->seekable = true;
|
||||
|
||||
// With cache enabled, the stream can be seekable. This causes demux_lavf.c
|
||||
// (actually libavformat/mpegts.c) to seek sometimes when reading a packet.
|
||||
// It does this to seek back a bit in case the current file position points
|
||||
// into the middle of a packet.
|
||||
demuxer->stream->seekable = false;
|
||||
|
||||
add_dvd_streams(demuxer);
|
||||
add_streams(demuxer);
|
||||
add_stream_chapters(demuxer);
|
||||
|
||||
+40
-70
@@ -39,12 +39,13 @@
|
||||
# include <libavutil/display.h>
|
||||
#endif
|
||||
#include <libavutil/opt.h>
|
||||
#include "compat/libav.h"
|
||||
|
||||
#include "options/options.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/tags.h"
|
||||
#include "common/av_common.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
@@ -233,7 +234,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
priv->filename = remove_prefix(priv->filename, prefixes);
|
||||
|
||||
char *avdevice_format = NULL;
|
||||
if (s->uncached_type == STREAMTYPE_AVDEVICE) {
|
||||
if (s->type == STREAMTYPE_AVDEVICE) {
|
||||
// always require filename in the form "format:filename"
|
||||
char *sep = strchr(priv->filename, ':');
|
||||
if (!sep) {
|
||||
@@ -429,19 +430,6 @@ static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return a dictionary entry as (decimal) integer.
|
||||
static int dict_get_decimal(AVDictionary *dict, const char *entry, int def)
|
||||
{
|
||||
AVDictionaryEntry *e = av_dict_get(dict, entry, NULL, 0);
|
||||
if (e && e->value) {
|
||||
char *end = NULL;
|
||||
long int r = strtol(e->value, &end, 10);
|
||||
if (end && !end[0] && r >= INT_MIN && r <= INT_MAX)
|
||||
return r;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
static void handle_stream(demuxer_t *demuxer, int i)
|
||||
{
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
@@ -460,7 +448,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
sh->format = codec->codec_tag;
|
||||
|
||||
// probably unneeded
|
||||
mp_chmap_set_unknown(&sh_audio->channels, codec->channels);
|
||||
mp_chmap_from_channels(&sh_audio->channels, codec->channels);
|
||||
if (codec->channel_layout)
|
||||
mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
|
||||
sh_audio->samplerate = codec->sample_rate;
|
||||
@@ -479,11 +467,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
|
||||
sh->attached_picture = new_demux_packet_from(st->attached_pic.data,
|
||||
st->attached_pic.size);
|
||||
if (sh->attached_picture) {
|
||||
sh->attached_picture->pts = 0;
|
||||
talloc_steal(sh, sh->attached_picture);
|
||||
sh->attached_picture->keyframe = true;
|
||||
}
|
||||
sh->attached_picture->pts = 0;
|
||||
talloc_steal(sh, sh->attached_picture);
|
||||
sh->attached_picture->keyframe = true;
|
||||
}
|
||||
|
||||
sh->format = codec->codec_tag;
|
||||
@@ -520,9 +506,13 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
if (sd)
|
||||
sh_video->rotate = -av_display_rotation_get((uint32_t *)sd);
|
||||
#else
|
||||
int rot = dict_get_decimal(st->metadata, "rotate", -1);
|
||||
if (rot >= 0)
|
||||
sh_video->rotate = rot;
|
||||
AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
|
||||
if (rot && rot->value) {
|
||||
char *end = NULL;
|
||||
long int r = strtol(rot->value, &end, 10);
|
||||
if (end && !end[0])
|
||||
sh_video->rotate = r;
|
||||
}
|
||||
#endif
|
||||
sh_video->rotate = ((sh_video->rotate % 360) + 360) % 360;
|
||||
|
||||
@@ -600,9 +590,6 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
||||
if (lang && lang->value)
|
||||
sh->lang = talloc_strdup(sh, lang->value);
|
||||
sh->hls_bitrate = dict_get_decimal(st->metadata, "variant_bitrate", 0);
|
||||
if (!sh->title && sh->hls_bitrate > 0)
|
||||
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
|
||||
}
|
||||
|
||||
select_tracks(demuxer, i);
|
||||
@@ -619,29 +606,10 @@ static void add_new_streams(demuxer_t *demuxer)
|
||||
|
||||
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
|
||||
{
|
||||
#if HAVE_AVFORMAT_METADATA_UPDATE_FLAG
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
if (priv->avfc->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->metadata);
|
||||
priv->avfc->event_flags = 0;
|
||||
demux_changed(demuxer, DEMUX_EVENT_METADATA);
|
||||
}
|
||||
if (priv->merge_track_metadata) {
|
||||
for (int n = 0; n < priv->num_streams; n++) {
|
||||
AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
|
||||
if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, st->metadata);
|
||||
st->event_flags = 0;
|
||||
demux_changed(demuxer, DEMUX_EVENT_METADATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
|
||||
#if HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
int md_size;
|
||||
const uint8_t *md;
|
||||
if (!pkt)
|
||||
return;
|
||||
md = av_packet_get_side_data(pkt, AV_PKT_DATA_METADATA_UPDATE, &md_size);
|
||||
if (md && priv->merge_track_metadata) {
|
||||
AVDictionary *dict = NULL;
|
||||
@@ -772,6 +740,8 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
add_new_streams(demuxer);
|
||||
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
|
||||
|
||||
// Often useful with OGG audio-only files, which have metadata in the audio
|
||||
// track metadata instead of the main metadata.
|
||||
if (demuxer->num_streams == 1) {
|
||||
@@ -782,9 +752,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
}
|
||||
}
|
||||
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
|
||||
update_metadata(demuxer, NULL);
|
||||
|
||||
demuxer->ts_resets_possible = priv->avif->flags & AVFMT_TS_DISCONT;
|
||||
|
||||
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
|
||||
@@ -793,21 +760,23 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_avpacket(void *pkt)
|
||||
{
|
||||
av_free_packet(pkt);
|
||||
}
|
||||
|
||||
static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
{
|
||||
lavf_priv_t *priv = demux->priv;
|
||||
demux_packet_t *dp;
|
||||
|
||||
AVPacket *pkt = &(AVPacket){0};
|
||||
AVPacket *pkt = talloc(NULL, AVPacket);
|
||||
int r = av_read_frame(priv->avfc, pkt);
|
||||
if (r < 0) {
|
||||
av_free_packet(pkt);
|
||||
if (r == AVERROR(EAGAIN))
|
||||
return 1;
|
||||
if (r == AVERROR_EOF)
|
||||
return 0;
|
||||
MP_WARN(demux, "error reading packet.\n");
|
||||
return -1;
|
||||
talloc_free(pkt);
|
||||
return r == AVERROR(EAGAIN) ? 1 : -1; // eof
|
||||
}
|
||||
talloc_set_destructor(pkt, destroy_avpacket);
|
||||
|
||||
add_new_streams(demux);
|
||||
update_metadata(demux, pkt);
|
||||
@@ -817,15 +786,18 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
AVStream *st = priv->avfc->streams[pkt->stream_index];
|
||||
|
||||
if (!demux_stream_is_selected(stream)) {
|
||||
av_free_packet(pkt);
|
||||
talloc_free(pkt);
|
||||
return 1; // don't signal EOF if skipping a packet
|
||||
}
|
||||
|
||||
struct demux_packet *dp = new_demux_packet_from_avpacket(pkt);
|
||||
if (!dp) {
|
||||
av_free_packet(pkt);
|
||||
return 1;
|
||||
}
|
||||
// If the packet has pointers to temporary fields that could be
|
||||
// overwritten/freed by next av_read_frame(), copy them to persistent
|
||||
// allocations so we can safely queue the packet for any length of time.
|
||||
if (av_dup_packet(pkt) < 0)
|
||||
abort();
|
||||
|
||||
dp = new_demux_packet_fromdata(pkt->data, pkt->size);
|
||||
dp->avpacket = talloc_steal(dp, pkt);
|
||||
|
||||
if (pkt->pts != AV_NOPTS_VALUE)
|
||||
dp->pts = pkt->pts * av_q2d(st->time_base);
|
||||
@@ -841,7 +813,6 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
} else if (dp->dts != MP_NOPTS_VALUE) {
|
||||
priv->last_pts = dp->dts * AV_TIME_BASE;
|
||||
}
|
||||
av_free_packet(pkt);
|
||||
demux_add_packet(stream, dp);
|
||||
return 1;
|
||||
}
|
||||
@@ -1010,13 +981,12 @@ redo:
|
||||
* call the new API instead of relying on av_seek_frame() to do this
|
||||
* for us.
|
||||
*/
|
||||
// avio_flush() is designed for write-only streams, and does the wrong
|
||||
// thing when reading. Flush it manually instead.
|
||||
stream_drop_buffers(demuxer->stream);
|
||||
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end = priv->avfc->pb->buffer;
|
||||
priv->avfc->pb->pos = stream_tell(demuxer->stream);
|
||||
av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
|
||||
AVSEEK_FLAG_BYTE);
|
||||
// avio_flush() is designed for write-only streams, and does the wrong
|
||||
// thing when reading. Flush it manually instead.
|
||||
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end;
|
||||
stream_drop_buffers(demuxer->stream);
|
||||
return DEMUXER_CTRL_OK;
|
||||
default:
|
||||
return DEMUXER_CTRL_NOTIMPL;
|
||||
|
||||
+4
-6
@@ -77,12 +77,10 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer)
|
||||
bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE);
|
||||
if (data.len) {
|
||||
demux_packet_t *dp = new_demux_packet(data.len);
|
||||
if (dp) {
|
||||
memcpy(dp->buffer, data.start, data.len);
|
||||
dp->pts = mf->curr_frame / mf->sh->fps;
|
||||
dp->keyframe = true;
|
||||
demux_add_packet(demuxer->streams[0], dp);
|
||||
}
|
||||
memcpy(dp->buffer, data.start, data.len);
|
||||
dp->pts = mf->curr_frame / mf->sh->fps;
|
||||
dp->keyframe = true;
|
||||
demux_add_packet(demuxer->streams[0], dp);
|
||||
}
|
||||
talloc_free(data.start);
|
||||
}
|
||||
|
||||
+140
-132
@@ -44,15 +44,13 @@
|
||||
#include "talloc.h"
|
||||
#include "common/av_common.h"
|
||||
#include "options/options.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "stream/stream.h"
|
||||
#include "video/csputils.h"
|
||||
#include "demux.h"
|
||||
#include "stheader.h"
|
||||
#include "ebml.h"
|
||||
#include "matroska.h"
|
||||
#include "codec_tags.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
|
||||
@@ -104,7 +102,6 @@ typedef struct mkv_track {
|
||||
bool v_dwidth_set, v_dheight_set;
|
||||
double v_frate;
|
||||
uint32_t colorspace;
|
||||
int stereo_mode;
|
||||
|
||||
uint32_t a_formattag;
|
||||
uint32_t a_channels, a_bps;
|
||||
@@ -507,15 +504,6 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
|
||||
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
|
||||
(unsigned int)track->colorspace);
|
||||
}
|
||||
if (video->n_stereo_mode) {
|
||||
const char *name = MP_STEREO3D_NAME(video->stereo_mode);
|
||||
if (name) {
|
||||
track->stereo_mode = video->stereo_mode;
|
||||
MP_VERBOSE(demuxer, "| + StereoMode: %s\n", name);
|
||||
} else {
|
||||
MP_WARN(demuxer, "Unknown StereoMode: %d\n", (int)video->stereo_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1149,34 +1137,45 @@ static const videocodec_info_t vinfo[] = {
|
||||
|
||||
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
{
|
||||
MP_BITMAPINFOHEADER *bih = &(MP_BITMAPINFOHEADER){0};
|
||||
unsigned char *extradata;
|
||||
unsigned int extradata_size = 0;
|
||||
struct sh_stream *sh;
|
||||
sh_video_t *sh_v;
|
||||
bool raw = false;
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
|
||||
if (!sh)
|
||||
return 1;
|
||||
track->stream = sh;
|
||||
sh_video_t *sh_v = sh->video;
|
||||
sh->demuxer_id = track->tnum;
|
||||
sh->title = talloc_strdup(sh_v, track->name);
|
||||
|
||||
if (track->ms_compat) { /* MS compatibility mode */
|
||||
// The private_data contains a BITMAPINFOHEADER struct
|
||||
if (track->private_data == NULL || track->private_size < 40)
|
||||
MP_BITMAPINFOHEADER *src;
|
||||
|
||||
if (track->private_data == NULL
|
||||
|| track->private_size < sizeof(*bih))
|
||||
return 1;
|
||||
|
||||
unsigned char *h = track->private_data;
|
||||
if (track->v_width == 0)
|
||||
track->v_width = AV_RL32(h + 4); // biWidth
|
||||
if (track->v_height == 0)
|
||||
track->v_height = AV_RL32(h + 8); // biHeight
|
||||
sh_v->bits_per_coded_sample = AV_RL16(h + 14); // biBitCount
|
||||
sh->format = AV_RL32(h + 16); // biCompression
|
||||
src = (MP_BITMAPINFOHEADER *) track->private_data;
|
||||
bih->biSize = le2me_32(src->biSize);
|
||||
bih->biWidth = le2me_32(src->biWidth);
|
||||
bih->biHeight = le2me_32(src->biHeight);
|
||||
bih->biPlanes = le2me_16(src->biPlanes);
|
||||
bih->biBitCount = le2me_16(src->biBitCount);
|
||||
bih->biCompression = le2me_32(src->biCompression);
|
||||
bih->biSizeImage = le2me_32(src->biSizeImage);
|
||||
bih->biXPelsPerMeter = le2me_32(src->biXPelsPerMeter);
|
||||
bih->biYPelsPerMeter = le2me_32(src->biYPelsPerMeter);
|
||||
bih->biClrUsed = le2me_32(src->biClrUsed);
|
||||
bih->biClrImportant = le2me_32(src->biClrImportant);
|
||||
extradata = track->private_data + sizeof(*bih);
|
||||
extradata_size = track->private_size - sizeof(*bih);
|
||||
|
||||
extradata = track->private_data + 40;
|
||||
extradata_size = track->private_size - 40;
|
||||
if (track->v_width == 0)
|
||||
track->v_width = bih->biWidth;
|
||||
if (track->v_height == 0)
|
||||
track->v_height = bih->biHeight;
|
||||
} else {
|
||||
sh_v->bits_per_coded_sample = 24;
|
||||
bih->biSize = sizeof(*bih);
|
||||
bih->biWidth = track->v_width;
|
||||
bih->biHeight = track->v_height;
|
||||
bih->biBitCount = 24;
|
||||
bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount / 8;
|
||||
|
||||
if (track->private_size >= RVPROPERTIES_SIZE
|
||||
&& (!strcmp(track->codec_id, MKV_V_REALV10)
|
||||
@@ -1190,26 +1189,30 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
src = (uint8_t *) track->private_data + RVPROPERTIES_SIZE;
|
||||
|
||||
cnt = track->private_size - RVPROPERTIES_SIZE;
|
||||
bih->biSize = 48 + cnt;
|
||||
bih->biPlanes = 1;
|
||||
type2 = AV_RB32(src - 4);
|
||||
if (type2 == 0x10003000 || type2 == 0x10003001)
|
||||
sh->format = MP_FOURCC('R', 'V', '1', '3');
|
||||
bih->biCompression = MP_FOURCC('R', 'V', '1', '3');
|
||||
else
|
||||
sh->format = MP_FOURCC('R', 'V', track->codec_id[9], '0');
|
||||
bih->biCompression =
|
||||
MP_FOURCC('R', 'V', track->codec_id[9], '0');
|
||||
// copy type1 and type2 info from rv properties
|
||||
extradata_size = cnt + 8;
|
||||
extradata = src - 8;
|
||||
track->realmedia = 1;
|
||||
} else if (strcmp(track->codec_id, MKV_V_UNCOMPRESSED) == 0) {
|
||||
// raw video, "like AVI" - this is a FourCC
|
||||
sh->format = track->colorspace;
|
||||
bih->biCompression = track->colorspace;
|
||||
raw = true;
|
||||
} else {
|
||||
const videocodec_info_t *vi = vinfo;
|
||||
while (vi->id && strcmp(vi->id, track->codec_id))
|
||||
vi++;
|
||||
sh->format = vi->fourcc;
|
||||
bih->biCompression = vi->fourcc;
|
||||
if (vi->extradata && track->private_data
|
||||
&& (track->private_size > 0)) {
|
||||
bih->biSize += track->private_size;
|
||||
extradata = track->private_data;
|
||||
extradata_size = track->private_size;
|
||||
}
|
||||
@@ -1229,10 +1232,22 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
return 1;
|
||||
}
|
||||
|
||||
sh_v->coded_width = track->v_width;
|
||||
sh_v->coded_height = track->v_height;
|
||||
sh_v->extradata = talloc_memdup(sh_v, extradata, extradata_size);
|
||||
sh_v->extradata_len = extradata_size;
|
||||
sh = new_sh_stream(demuxer, STREAM_VIDEO);
|
||||
if (!sh)
|
||||
return 1;
|
||||
track->stream = sh;
|
||||
sh_v = sh->video;
|
||||
sh->demuxer_id = track->tnum;
|
||||
sh->title = talloc_strdup(sh_v, track->name);
|
||||
sh_v->bih = talloc_size(sh_v, sizeof(MP_BITMAPINFOHEADER) + extradata_size);
|
||||
if (!sh_v->bih) {
|
||||
MP_FATAL(demuxer, "Memory allocation failure!\n");
|
||||
abort();
|
||||
}
|
||||
*sh_v->bih = *bih;
|
||||
if (extradata_size)
|
||||
memcpy(sh_v->bih + 1, extradata, extradata_size);
|
||||
sh->format = sh_v->bih->biCompression;
|
||||
if (raw) {
|
||||
sh->codec = "rawvideo";
|
||||
} else {
|
||||
@@ -1261,7 +1276,6 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
|
||||
sh_v->avi_dts = track->ms_compat;
|
||||
sh_v->stereo_mode = track->stereo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1304,6 +1318,15 @@ static const struct mkv_audio_tag {
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static void copy_audio_private_data(sh_audio_t *sh, mkv_track_t *track)
|
||||
{
|
||||
if (!track->ms_compat && track->private_size) {
|
||||
sh->codecdata = talloc_size(sh, track->private_size);
|
||||
sh->codecdata_len = track->private_size;
|
||||
memcpy(sh->codecdata, track->private_data, track->private_size);
|
||||
}
|
||||
}
|
||||
|
||||
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
{
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_AUDIO);
|
||||
@@ -1312,9 +1335,6 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
track->stream = sh;
|
||||
sh_audio_t *sh_a = sh->audio;
|
||||
|
||||
unsigned char *extradata = NULL;
|
||||
int extradata_len = 0;
|
||||
|
||||
if (track->language && (strcmp(track->language, "und") != 0))
|
||||
sh->lang = talloc_strdup(sh_a, track->language);
|
||||
sh->demuxer_id = track->tnum;
|
||||
@@ -1322,25 +1342,30 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
sh->default_track = track->default_track;
|
||||
if (!track->a_osfreq)
|
||||
track->a_osfreq = track->a_sfreq;
|
||||
|
||||
if (track->ms_compat) {
|
||||
// The private_data contains a WAVEFORMATEX struct
|
||||
if (track->private_size < 18)
|
||||
if (track->private_size < sizeof(*sh_a->wf))
|
||||
goto error;
|
||||
MP_VERBOSE(demuxer, "track with MS compat audio.\n");
|
||||
unsigned char *h = track->private_data;
|
||||
track->a_formattag = AV_RL16(h + 0); // wFormatTag
|
||||
if (track->a_channels == 0)
|
||||
track->a_channels = AV_RL16(h + 2); // nChannels
|
||||
MP_WAVEFORMATEX *wf = (MP_WAVEFORMATEX *) track->private_data;
|
||||
sh_a->wf = talloc_zero_size(sh_a, track->private_size);
|
||||
sh_a->wf->wFormatTag = le2me_16(wf->wFormatTag);
|
||||
sh_a->wf->nChannels = le2me_16(wf->nChannels);
|
||||
sh_a->wf->nSamplesPerSec = le2me_32(wf->nSamplesPerSec);
|
||||
sh_a->wf->nAvgBytesPerSec = le2me_32(wf->nAvgBytesPerSec);
|
||||
sh_a->wf->nBlockAlign = le2me_16(wf->nBlockAlign);
|
||||
sh_a->wf->wBitsPerSample = le2me_16(wf->wBitsPerSample);
|
||||
sh_a->wf->cbSize = track->private_size - sizeof(*sh_a->wf);
|
||||
memcpy(sh_a->wf + 1, wf + 1,
|
||||
track->private_size - sizeof(*sh_a->wf));
|
||||
if (track->a_osfreq == 0.0)
|
||||
track->a_osfreq = AV_RL32(h + 4); // nSamplesPerSec
|
||||
sh_a->bitrate = AV_RL32(h + 8) * 8; // nAvgBytesPerSec
|
||||
sh_a->block_align = AV_RL16(h + 12); // nBlockAlign
|
||||
track->a_osfreq = sh_a->wf->nSamplesPerSec;
|
||||
if (track->a_channels == 0)
|
||||
track->a_channels = sh_a->wf->nChannels;
|
||||
if (track->a_bps == 0)
|
||||
track->a_bps = AV_RL16(h + 14); // wBitsPerSample
|
||||
extradata = track->private_data + 18;
|
||||
extradata_len = track->private_size - 18;
|
||||
track->a_bps = sh_a->wf->wBitsPerSample;
|
||||
track->a_formattag = sh_a->wf->wFormatTag;
|
||||
} else {
|
||||
sh_a->wf = talloc_zero(sh_a, MP_WAVEFORMATEX);
|
||||
for (int i = 0; ; i++) {
|
||||
const struct mkv_audio_tag *t = mkv_audio_tags + i;
|
||||
if (t->id == NULL)
|
||||
@@ -1359,21 +1384,23 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
|
||||
sh->format = track->a_formattag;
|
||||
mp_chmap_set_unknown(&sh_a->channels, track->a_channels);
|
||||
sh_a->wf->wFormatTag = track->a_formattag;
|
||||
mp_chmap_from_channels(&sh_a->channels, track->a_channels);
|
||||
sh_a->wf->nChannels = track->a_channels;
|
||||
sh_a->samplerate = (uint32_t) track->a_osfreq;
|
||||
sh_a->wf->nSamplesPerSec = (uint32_t) track->a_osfreq;
|
||||
if (track->a_bps == 0)
|
||||
sh_a->bits_per_coded_sample = 16;
|
||||
sh_a->wf->wBitsPerSample = 16;
|
||||
else
|
||||
sh_a->bits_per_coded_sample = track->a_bps;
|
||||
sh_a->wf->wBitsPerSample = track->a_bps;
|
||||
if (track->a_formattag == 0x0055) { /* MP3 || MP2 */
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1152;
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1152;
|
||||
} else if ((track->a_formattag == 0x2000) /* AC3 */
|
||||
|| track->a_formattag == MP_FOURCC('E', 'A', 'C', '3')
|
||||
|| (track->a_formattag == 0x2001)) { /* DTS */
|
||||
sh_a->bits_per_coded_sample = 0;
|
||||
sh_a->bitrate = 0;
|
||||
sh_a->block_align = 0;
|
||||
talloc_free(sh_a->wf);
|
||||
sh_a->wf = NULL;
|
||||
} else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */
|
||||
if (!strcmp(track->codec_id, MKV_A_PCM_BE))
|
||||
sh->format = MP_FOURCC('t', 'w', 'o', 's');
|
||||
@@ -1381,23 +1408,17 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
/* ok */
|
||||
} else if (!strcmp(track->codec_id, MKV_A_QDMC)
|
||||
|| !strcmp(track->codec_id, MKV_A_QDMC2)) {
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1486;
|
||||
if (!extradata_len) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1486;
|
||||
copy_audio_private_data(sh_a, track);
|
||||
} else if (track->a_formattag == MP_FOURCC('M', 'P', '4', 'A')) {
|
||||
int profile, srate_idx;
|
||||
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1024;
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1024;
|
||||
|
||||
if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) {
|
||||
if (!extradata_len) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
copy_audio_private_data(sh_a, track);
|
||||
} else {
|
||||
/* Recreate the 'private data' */
|
||||
/* which faad2 uses in its initialization */
|
||||
@@ -1419,6 +1440,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
/* HE-AAC (aka SBR AAC) */
|
||||
sh_a->codecdata_len = 5;
|
||||
|
||||
sh_a->samplerate = sh_a->wf->nSamplesPerSec = track->a_osfreq;
|
||||
srate_idx = aac_get_sample_rate_index(sh_a->samplerate);
|
||||
sh_a->codecdata[2] = AAC_SYNC_EXTENSION_TYPE >> 3;
|
||||
sh_a->codecdata[3] = ((AAC_SYNC_EXTENSION_TYPE & 0x07) << 5) | 5;
|
||||
@@ -1431,19 +1453,21 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('v', 'r', 'b', 's')) {
|
||||
/* VORBIS */
|
||||
if (track->private_size == 0 || (track->ms_compat && !extradata_len))
|
||||
if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize)
|
||||
goto error;
|
||||
if (!track->ms_compat) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
if (track->private_size > 0x1000000)
|
||||
goto error;
|
||||
sh_a->wf->cbSize = track->private_size;
|
||||
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
|
||||
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
|
||||
memcpy((unsigned char *) (sh_a->wf + 1), track->private_data,
|
||||
sh_a->wf->cbSize);
|
||||
}
|
||||
} else if (!strcmp(track->codec_id, MKV_A_OPUS)
|
||||
|| !strcmp(track->codec_id, MKV_A_OPUS_EXP)) {
|
||||
sh->format = MP_FOURCC('O', 'p', 'u', 's');
|
||||
if (!track->ms_compat) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
copy_audio_private_data(sh_a, track);
|
||||
} else if (!strncmp(track->codec_id, MKV_A_REALATRC, 7)) {
|
||||
if (track->private_size < RAPROPERTIES4_SIZE)
|
||||
goto error;
|
||||
@@ -1452,13 +1476,13 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
int codecdata_length, version;
|
||||
int flavor;
|
||||
|
||||
sh_a->bitrate = 0; /* FIXME !? */
|
||||
sh_a->wf->nAvgBytesPerSec = 0; /* FIXME !? */
|
||||
|
||||
version = AV_RB16(src + 4);
|
||||
flavor = AV_RB16(src + 22);
|
||||
track->coded_framesize = AV_RB32(src + 24);
|
||||
track->sub_packet_h = AV_RB16(src + 40);
|
||||
sh_a->block_align = track->audiopk_size = AV_RB16(src + 42);
|
||||
sh_a->wf->nBlockAlign = track->audiopk_size = AV_RB16(src + 42);
|
||||
track->sub_packet_size = AV_RB16(src + 44);
|
||||
if (version == 4) {
|
||||
src += RAPROPERTIES4_SIZE;
|
||||
@@ -1481,25 +1505,27 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
if (codecdata_length < 0 || codecdata_length > 0x1000000)
|
||||
goto error;
|
||||
src += 4;
|
||||
extradata_len = codecdata_length;
|
||||
extradata = src;
|
||||
sh_a->wf->cbSize = codecdata_length;
|
||||
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
|
||||
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
|
||||
memcpy(((char *) (sh_a->wf + 1)), src, codecdata_length);
|
||||
|
||||
switch (track->a_formattag) {
|
||||
case MP_FOURCC('a', 't', 'r', 'c'):
|
||||
sh_a->bitrate = atrc_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->sub_packet_size;
|
||||
sh_a->wf->nAvgBytesPerSec = atrc_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->sub_packet_size;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('c', 'o', 'o', 'k'):
|
||||
sh_a->bitrate = cook_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->sub_packet_size;
|
||||
sh_a->wf->nAvgBytesPerSec = cook_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->sub_packet_size;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('s', 'i', 'p', 'r'):
|
||||
sh_a->bitrate = sipr_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->coded_framesize;
|
||||
sh_a->wf->nAvgBytesPerSec = sipr_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->coded_framesize;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('2', '8', '_', '8'):
|
||||
sh_a->bitrate = 3600 * 8;
|
||||
sh_a->block_align = track->coded_framesize;
|
||||
sh_a->wf->nAvgBytesPerSec = 3600;
|
||||
sh_a->wf->nBlockAlign = track->coded_framesize;
|
||||
audiobuf:
|
||||
track->audio_buf =
|
||||
talloc_array_size(track, track->sub_packet_h, track->audiopk_size);
|
||||
@@ -1513,17 +1539,16 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
|| (track->a_formattag == 0xf1ac)) {
|
||||
unsigned char *ptr;
|
||||
int size;
|
||||
sh_a->bits_per_coded_sample = 0;
|
||||
sh_a->bitrate = 0;
|
||||
sh_a->block_align = 0;
|
||||
talloc_free(sh_a->wf);
|
||||
sh_a->wf = NULL;
|
||||
|
||||
if (!track->ms_compat) {
|
||||
ptr = track->private_data;
|
||||
size = track->private_size;
|
||||
} else {
|
||||
sh->format = MP_FOURCC('f', 'L', 'a', 'C');
|
||||
ptr = extradata;
|
||||
size = extradata_len;
|
||||
ptr = track->private_data + sizeof(*sh_a->wf);
|
||||
size = track->private_size - sizeof(*sh_a->wf);
|
||||
}
|
||||
if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' || ptr[2] != 'a'
|
||||
|| ptr[3] != 'C') {
|
||||
@@ -1547,10 +1572,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K') ||
|
||||
track->a_formattag == MP_FOURCC('T', 'R', 'H', 'D')) {
|
||||
if (!track->ms_compat) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
copy_audio_private_data(sh_a, track);
|
||||
} else if (track->a_formattag == MP_FOURCC('T', 'T', 'A', '1')) {
|
||||
sh_a->codecdata_len = 30;
|
||||
sh_a->codecdata = talloc_zero_size(sh_a, sh_a->codecdata_len);
|
||||
@@ -1560,7 +1582,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
memcpy(data + 0, "TTA1", 4);
|
||||
AV_WL16(data + 4, 1);
|
||||
AV_WL16(data + 6, sh_a->channels.num);
|
||||
AV_WL16(data + 8, sh_a->bits_per_coded_sample);
|
||||
AV_WL16(data + 8, sh_a->wf->wBitsPerSample);
|
||||
AV_WL32(data + 10, track->a_osfreq);
|
||||
// Bogus: last frame won't be played.
|
||||
AV_WL32(data + 14, 0);
|
||||
@@ -1576,14 +1598,6 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
|
||||
mp_set_codec_from_tag(sh);
|
||||
|
||||
if (track->private_size > 0x1000000 || extradata_len > 0x1000000)
|
||||
goto error;
|
||||
|
||||
if (!sh_a->codecdata && extradata) {
|
||||
sh_a->codecdata = talloc_memdup(sh_a, extradata, extradata_len);
|
||||
sh_a->codecdata_len = extradata_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@@ -1938,7 +1952,11 @@ static int64_t real_fix_timestamp(unsigned char *buf, int len, int64_t timestamp
|
||||
*kf_base = timestamp;
|
||||
*kf_pts = pts;
|
||||
} else {
|
||||
timestamp = *kf_base - ((*kf_pts - pts) & 0x1FFF);
|
||||
if (pict_type != 3) {
|
||||
timestamp = *kf_base + ((pts - *kf_pts) & 0x1FFF);
|
||||
} else {
|
||||
timestamp = *kf_base - ((*kf_pts - pts) & 0x1FFF);
|
||||
}
|
||||
}
|
||||
|
||||
return timestamp;
|
||||
@@ -1952,8 +1970,6 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
|
||||
int64_t timestamp = mkv_d->last_pts * 1000;
|
||||
|
||||
dp = new_demux_packet_from(data.start, data.len);
|
||||
if (!dp)
|
||||
return;
|
||||
|
||||
if (mkv_d->v_skip_to_keyframe) {
|
||||
dp->pts = mkv_d->last_pts;
|
||||
@@ -1962,7 +1978,7 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
|
||||
} else {
|
||||
dp->pts =
|
||||
real_fix_timestamp(dp->buffer, dp->len, timestamp,
|
||||
track->stream->format,
|
||||
track->stream->video->bih->biCompression,
|
||||
&track->rv_kf_base, &track->rv_kf_pts) * 0.001;
|
||||
}
|
||||
dp->pos = mkv_d->last_filepos;
|
||||
@@ -2065,15 +2081,13 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
if (++(track->sub_packet_cnt) == sph) {
|
||||
track->sub_packet_cnt = 0;
|
||||
// apk_usize has same range as coded_framesize in worst case
|
||||
uint32_t apk_usize = track->stream->audio->block_align;
|
||||
uint32_t apk_usize = track->stream->audio->wf->nBlockAlign;
|
||||
if (apk_usize > audiobuf_size)
|
||||
goto error;
|
||||
// Release all the audio packets
|
||||
for (int x = 0; x < sph * w / apk_usize; x++) {
|
||||
dp = new_demux_packet_from(track->audio_buf + x * apk_usize,
|
||||
apk_usize);
|
||||
if (!dp)
|
||||
goto error;
|
||||
/* Put timestamp only on packets that correspond to original
|
||||
* audio packets in file */
|
||||
dp->pts = (x * apk_usize % w) ? MP_NOPTS_VALUE :
|
||||
@@ -2085,8 +2099,6 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
}
|
||||
} else { // Not a codec that requires reordering
|
||||
dp = new_demux_packet_from(buffer, size);
|
||||
if (!dp)
|
||||
goto error;
|
||||
if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe)
|
||||
dp->pts = MP_NOPTS_VALUE;
|
||||
else
|
||||
@@ -2099,7 +2111,7 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
}
|
||||
return;
|
||||
error:
|
||||
MP_ERR(demuxer, "RealAudio packet extraction or decryption error.\n");
|
||||
MP_ERR(demuxer, "RealAudio decrypting error.\n");
|
||||
}
|
||||
|
||||
static void mkv_seek_reset(demuxer_t *demuxer)
|
||||
@@ -2404,21 +2416,17 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
|
||||
while (raw.start && mkv_parse_packet(track, &raw, &buffer)) {
|
||||
demux_packet_t *dp =
|
||||
new_demux_packet_from(buffer.start, buffer.len);
|
||||
if (!dp)
|
||||
break;
|
||||
dp->keyframe = keyframe;
|
||||
/* If default_duration is 0, assume no pts value is known
|
||||
* for packets after the first one (rather than all pts
|
||||
* values being the same). Also, don't use it for extra
|
||||
* packets resulting from parsing. */
|
||||
if (p == 0 || (p == i && track->default_duration))
|
||||
* values being the same) */
|
||||
if (p == 0 || track->default_duration)
|
||||
dp->pts = mkv_d->last_pts + p * track->default_duration;
|
||||
p++;
|
||||
if (track->ms_compat)
|
||||
MPSWAP(double, dp->pts, dp->dts);
|
||||
if (p == 0)
|
||||
dp->duration = block_duration / 1e9;
|
||||
dp->duration = block_duration / 1e9;
|
||||
demux_add_packet(stream, dp);
|
||||
p++;
|
||||
}
|
||||
talloc_free_children(track->parser_tmp);
|
||||
}
|
||||
|
||||
+18
-44
@@ -29,62 +29,33 @@
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
#include "stheader.h"
|
||||
#include "codec_tags.h"
|
||||
#include "audio/format.h"
|
||||
|
||||
#include "video/img_format.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
#include "osdep/endian.h"
|
||||
|
||||
struct demux_rawaudio_opts {
|
||||
struct mp_chmap channels;
|
||||
int samplerate;
|
||||
int aformat;
|
||||
};
|
||||
|
||||
// Ad-hoc schema to systematically encode the format as int
|
||||
#define PCM(sign, is_float, bits, is_be) \
|
||||
((sign) | ((is_float) << 1) | ((is_be) << 2) | ((bits) << 3))
|
||||
#define NE (BYTE_ORDER == BIG_ENDIAN)
|
||||
|
||||
#define OPT_BASE_STRUCT struct demux_rawaudio_opts
|
||||
const struct m_sub_options demux_rawaudio_conf = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_CHMAP("channels", channels, CONF_MIN, .min = 1),
|
||||
OPT_INTRANGE("rate", samplerate, 0, 1000, 8 * 48000),
|
||||
OPT_CHOICE("format", aformat, 0,
|
||||
({"u8", PCM(0, 0, 8, 0)},
|
||||
{"s8", PCM(1, 0, 8, 0)},
|
||||
{"u16le", PCM(0, 0, 16, 0)}, {"u16be", PCM(0, 0, 16, 1)},
|
||||
{"s16le", PCM(1, 0, 16, 0)}, {"u16be", PCM(1, 0, 16, 1)},
|
||||
{"u24le", PCM(0, 0, 24, 0)}, {"u24be", PCM(0, 0, 24, 1)},
|
||||
{"s24le", PCM(1, 0, 24, 0)}, {"s24be", PCM(1, 0, 24, 1)},
|
||||
{"u32le", PCM(0, 0, 32, 0)}, {"u32be", PCM(0, 0, 32, 1)},
|
||||
{"s32le", PCM(1, 0, 32, 0)}, {"s32be", PCM(1, 0, 32, 1)},
|
||||
{"floatle", PCM(0, 1, 32, 0)}, {"floatbe", PCM(0, 1, 32, 1)},
|
||||
{"doublele",PCM(0, 1, 64, 0)}, {"doublebe", PCM(0, 1, 64, 1)},
|
||||
{"u16", PCM(0, 0, 16, NE)},
|
||||
{"s16", PCM(1, 0, 16, NE)},
|
||||
{"u24", PCM(0, 0, 24, NE)},
|
||||
{"s24", PCM(1, 0, 24, NE)},
|
||||
{"u32", PCM(0, 0, 32, NE)},
|
||||
{"s32", PCM(1, 0, 32, NE)},
|
||||
{"float", PCM(0, 1, 32, NE)},
|
||||
{"double", PCM(0, 1, 64, NE)})),
|
||||
OPT_AUDIOFORMAT("format", aformat, 0),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct demux_rawaudio_opts),
|
||||
.defaults = &(const struct demux_rawaudio_opts){
|
||||
// Note that currently, stream_cdda expects exactly these parameters!
|
||||
.channels = MP_CHMAP_INIT_STEREO,
|
||||
.samplerate = 44100,
|
||||
.aformat = PCM(1, 0, 16, 0), // s16le
|
||||
.aformat = AF_FORMAT_S16,
|
||||
},
|
||||
};
|
||||
|
||||
#undef PCM
|
||||
#undef NE
|
||||
|
||||
struct demux_rawvideo_opts {
|
||||
int vformat;
|
||||
int mp_format;
|
||||
@@ -128,20 +99,28 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
|
||||
struct demux_rawaudio_opts *opts = demuxer->opts->demux_rawaudio;
|
||||
struct sh_stream *sh;
|
||||
sh_audio_t *sh_audio;
|
||||
MP_WAVEFORMATEX *w;
|
||||
|
||||
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
|
||||
return -1;
|
||||
|
||||
if ((opts->aformat & AF_FORMAT_SPECIAL_MASK) != 0)
|
||||
return -1;
|
||||
|
||||
sh = new_sh_stream(demuxer, STREAM_AUDIO);
|
||||
sh_audio = sh->audio;
|
||||
sh->codec = "mp-pcm";
|
||||
sh->format = opts->aformat;
|
||||
sh_audio->wf = w = talloc_zero(sh, MP_WAVEFORMATEX);
|
||||
w->wFormatTag = 0;
|
||||
sh_audio->channels = opts->channels;
|
||||
sh_audio->force_channels = true;
|
||||
sh_audio->samplerate = opts->samplerate;
|
||||
|
||||
int f = opts->aformat;
|
||||
// See PCM(): sign float bits endian
|
||||
mp_set_pcm_codec(sh, f & 1, f & 2, f >> 3, f & 4);
|
||||
int samplesize = ((f >> 3) + 7) / 8;
|
||||
w->nChannels = sh_audio->channels.num;
|
||||
w->nSamplesPerSec = sh_audio->samplerate = opts->samplerate;
|
||||
int samplesize = af_fmt2bps(opts->aformat);
|
||||
w->nAvgBytesPerSec = sh_audio->samplerate * samplesize * w->nChannels;
|
||||
w->nBlockAlign = w->nChannels * samplesize;
|
||||
w->wBitsPerSample = 8 * samplesize;
|
||||
w->cbSize = 0;
|
||||
|
||||
struct priv *p = talloc_ptrtype(demuxer, p);
|
||||
demuxer->priv = p;
|
||||
@@ -248,11 +227,6 @@ static int raw_fill_buffer(demuxer_t *demuxer)
|
||||
return 0;
|
||||
|
||||
struct demux_packet *dp = new_demux_packet(p->frame_size * p->read_frames);
|
||||
if (!dp) {
|
||||
MP_ERR(demuxer, "Can't read packet.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dp->pos = stream_tell(demuxer->stream);
|
||||
dp->pts = (dp->pos / p->frame_size) / p->frame_rate;
|
||||
|
||||
|
||||
+38
-21
@@ -8,11 +8,9 @@
|
||||
#include "options/options.h"
|
||||
|
||||
#include "demux.h"
|
||||
#include "codec_tags.h"
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "video/img_fourcc.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "stream/tv.h"
|
||||
@@ -106,9 +104,16 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
switch(audio_format)
|
||||
{
|
||||
// This is the only format any of the current inputs generate.
|
||||
case AF_FORMAT_S16:
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S8:
|
||||
case AF_FORMAT_U16_LE:
|
||||
case AF_FORMAT_U16_BE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S16_BE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
case AF_FORMAT_S32_BE:
|
||||
break;
|
||||
case AF_FORMAT_MPEG2:
|
||||
default:
|
||||
MP_ERR(tvh, "Audio type '%s' unsupported!\n",
|
||||
af_fmt_to_str(audio_format));
|
||||
@@ -125,11 +130,27 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
|
||||
&nchannels);
|
||||
mp_chmap_from_channels(&sh_audio->channels, nchannels);
|
||||
|
||||
// s16ne
|
||||
mp_set_pcm_codec(sh_a, true, false, 16, BYTE_ORDER == BIG_ENDIAN);
|
||||
sh_a->codec = "mp-pcm";
|
||||
sh_a->format = audio_format;
|
||||
|
||||
int samplesize = af_fmt2bps(audio_format);
|
||||
int block_align = samplesize * sh_audio->channels.num;
|
||||
int bytes_per_second = sh_audio->samplerate * block_align;
|
||||
|
||||
sh_audio->bitrate = bytes_per_second * 8;
|
||||
|
||||
// emulate WF for win32 codecs:
|
||||
sh_audio->wf = talloc_zero(sh_audio, MP_WAVEFORMATEX);
|
||||
sh_audio->wf->wFormatTag = sh_a->format;
|
||||
sh_audio->wf->nChannels = sh_audio->channels.num;
|
||||
sh_audio->wf->wBitsPerSample = samplesize * 8;
|
||||
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
|
||||
sh_audio->wf->nBlockAlign = block_align;
|
||||
sh_audio->wf->nAvgBytesPerSec = bytes_per_second;
|
||||
|
||||
MP_VERBOSE(tvh, " TV audio: %d channels, %d bits, %d Hz\n",
|
||||
nchannels, 16, sh_audio->samplerate);
|
||||
sh_audio->wf->nChannels, sh_audio->wf->wBitsPerSample,
|
||||
sh_audio->wf->nSamplesPerSec);
|
||||
}
|
||||
no_audio:
|
||||
|
||||
@@ -183,30 +204,26 @@ static int demux_tv_fill_buffer(demuxer_t *demux)
|
||||
if (want_audio && tvh->tv_param->audio &&
|
||||
tvh->functions->control(tvh->priv,
|
||||
TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
|
||||
{
|
||||
{
|
||||
len = tvh->functions->get_audio_framesize(tvh->priv);
|
||||
|
||||
dp=new_demux_packet(len);
|
||||
if (dp) {
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
|
||||
demux_add_packet(want_audio, dp);
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
|
||||
demux_add_packet(want_audio, dp);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================== ADD VIDEO PACKET =================== */
|
||||
|
||||
if (want_video && tvh->functions->control(tvh->priv,
|
||||
TVI_CONTROL_IS_VIDEO, 0) == TVI_CONTROL_TRUE)
|
||||
{
|
||||
len = tvh->functions->get_video_framesize(tvh->priv);
|
||||
{
|
||||
len = tvh->functions->get_video_framesize(tvh->priv);
|
||||
dp=new_demux_packet(len);
|
||||
if (dp) {
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
|
||||
demux_add_packet(want_video, dp);
|
||||
}
|
||||
}
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
|
||||
demux_add_packet(want_video, dp);
|
||||
}
|
||||
|
||||
if (tvh->tv_param->scan) tv_scan(tvh);
|
||||
return 1;
|
||||
|
||||
+4
-7
@@ -34,6 +34,7 @@
|
||||
#include "talloc.h"
|
||||
#include "ebml.h"
|
||||
#include "stream/stream.h"
|
||||
#include "compat/mpbswap.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
// Whether the id is a known Matroska level 1 element (allowed as element on
|
||||
@@ -419,16 +420,12 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
|
||||
if (num_elems[i] && type->fields[i].multiple) {
|
||||
char *ptr = s + type->fields[i].offset;
|
||||
switch (type->fields[i].desc->type) {
|
||||
case EBML_TYPE_SUBELEMENTS: {
|
||||
size_t max = 1000000000 / type->fields[i].desc->size;
|
||||
if (num_elems[i] > max) {
|
||||
MP_ERR(ctx, "Too many subelements.\n");
|
||||
num_elems[i] = max;
|
||||
}
|
||||
case EBML_TYPE_SUBELEMENTS:
|
||||
num_elems[i] = FFMIN(num_elems[i],
|
||||
1000000000 / type->fields[i].desc->size);
|
||||
int sz = num_elems[i] * type->fields[i].desc->size;
|
||||
*(generic_struct **) ptr = talloc_zero_size(ctx->talloc_ctx, sz);
|
||||
break;
|
||||
}
|
||||
case EBML_TYPE_UINT:
|
||||
*(uint64_t **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
||||
uint64_t, num_elems[i]);
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
struct mp_log;
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_MS_HDR_H
|
||||
#define MPLAYER_MS_HDR_H
|
||||
|
||||
#include "compat/mpbswap.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
// These structs must be binary-compatible to the native win32 types,
|
||||
// because demux_mkv.c uses them directly.
|
||||
|
||||
typedef struct __attribute__((__packed__)) MP_WAVEFORMATEX {
|
||||
unsigned short wFormatTag;
|
||||
unsigned short nChannels;
|
||||
unsigned int nSamplesPerSec;
|
||||
unsigned int nAvgBytesPerSec;
|
||||
unsigned short nBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
unsigned short cbSize;
|
||||
} MP_WAVEFORMATEX;
|
||||
|
||||
typedef struct __attribute__((__packed__)) MP_BITMAPINFOHEADER {
|
||||
int biSize;
|
||||
int biWidth;
|
||||
int biHeight;
|
||||
short biPlanes;
|
||||
short biBitCount;
|
||||
int biCompression;
|
||||
int biSizeImage;
|
||||
int biXPelsPerMeter;
|
||||
int biYPelsPerMeter;
|
||||
int biClrUsed;
|
||||
int biClrImportant;
|
||||
} MP_BITMAPINFOHEADER;
|
||||
|
||||
#endif /* MPLAYER_MS_HDR_H */
|
||||
+50
-43
@@ -29,60 +29,55 @@
|
||||
static void packet_destroy(void *ptr)
|
||||
{
|
||||
struct demux_packet *dp = ptr;
|
||||
av_packet_unref(dp->avpacket);
|
||||
talloc_free(dp->avpacket);
|
||||
av_free(dp->allocation);
|
||||
}
|
||||
|
||||
// This actually preserves only data and side data, not PTS/DTS/pos/etc.
|
||||
// It also allows avpkt->data==NULL with avpkt->size!=0 - the libavcodec API
|
||||
// does not allow it, but we do it to simplify new_demux_packet().
|
||||
struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
|
||||
static struct demux_packet *create_packet(size_t len)
|
||||
{
|
||||
if (avpkt->size > 1000000000)
|
||||
return NULL;
|
||||
if (len > 1000000000) {
|
||||
fprintf(stderr, "Attempt to allocate demux packet over 1 GB!\n");
|
||||
abort();
|
||||
}
|
||||
struct demux_packet *dp = talloc(NULL, struct demux_packet);
|
||||
talloc_set_destructor(dp, packet_destroy);
|
||||
*dp = (struct demux_packet) {
|
||||
.len = len,
|
||||
.pts = MP_NOPTS_VALUE,
|
||||
.dts = MP_NOPTS_VALUE,
|
||||
.duration = -1,
|
||||
.pos = -1,
|
||||
.stream = -1,
|
||||
.avpacket = talloc_zero(dp, AVPacket),
|
||||
};
|
||||
av_init_packet(dp->avpacket);
|
||||
int r = -1;
|
||||
if (avpkt->data) {
|
||||
// We hope that this function won't need/access AVPacket input padding,
|
||||
// because otherwise new_demux_packet_from() wouldn't work.
|
||||
r = av_packet_ref(dp->avpacket, avpkt);
|
||||
} else {
|
||||
r = av_new_packet(dp->avpacket, avpkt->size);
|
||||
}
|
||||
if (r < 0) {
|
||||
*dp->avpacket = (AVPacket){0};
|
||||
talloc_free(dp);
|
||||
return NULL;
|
||||
}
|
||||
dp->buffer = dp->avpacket->data;
|
||||
dp->len = dp->avpacket->size;
|
||||
return dp;
|
||||
}
|
||||
|
||||
// Input data doesn't need to be padded.
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len)
|
||||
{
|
||||
if (len > INT_MAX)
|
||||
return NULL;
|
||||
AVPacket pkt = { .data = data, .size = len };
|
||||
return new_demux_packet_from_avpacket(&pkt);
|
||||
}
|
||||
|
||||
struct demux_packet *new_demux_packet(size_t len)
|
||||
{
|
||||
if (len > INT_MAX)
|
||||
return NULL;
|
||||
AVPacket pkt = { .data = NULL, .size = len };
|
||||
return new_demux_packet_from_avpacket(&pkt);
|
||||
struct demux_packet *dp = create_packet(len);
|
||||
dp->buffer = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (!dp->buffer) {
|
||||
fprintf(stderr, "Memory allocation failure!\n");
|
||||
abort();
|
||||
}
|
||||
memset(dp->buffer + len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
dp->allocation = dp->buffer;
|
||||
return dp;
|
||||
}
|
||||
|
||||
// data must already have suitable padding, and does not copy the data
|
||||
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len)
|
||||
{
|
||||
struct demux_packet *dp = create_packet(len);
|
||||
dp->buffer = data;
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len)
|
||||
{
|
||||
struct demux_packet *dp = new_demux_packet(len);
|
||||
memcpy(dp->buffer, data, len);
|
||||
return dp;
|
||||
}
|
||||
|
||||
void demux_packet_shorten(struct demux_packet *dp, size_t len)
|
||||
@@ -97,17 +92,29 @@ void free_demux_packet(struct demux_packet *dp)
|
||||
talloc_free(dp);
|
||||
}
|
||||
|
||||
static void destroy_avpacket(void *pkt)
|
||||
{
|
||||
av_free_packet(pkt);
|
||||
}
|
||||
|
||||
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
|
||||
{
|
||||
struct demux_packet *new = NULL;
|
||||
if (dp->avpacket) {
|
||||
new = new_demux_packet_from_avpacket(dp->avpacket);
|
||||
} else {
|
||||
// Some packets might be not created by new_demux_packet*().
|
||||
new = new_demux_packet_from(dp->buffer, dp->len);
|
||||
assert(dp->buffer == dp->avpacket->data);
|
||||
assert(dp->len == dp->avpacket->size);
|
||||
AVPacket *newavp = talloc_zero(NULL, AVPacket);
|
||||
talloc_set_destructor(newavp, destroy_avpacket);
|
||||
av_init_packet(newavp);
|
||||
if (av_packet_ref(newavp, dp->avpacket) < 0)
|
||||
abort();
|
||||
new = new_demux_packet_fromdata(newavp->data, newavp->size);
|
||||
new->avpacket = newavp;
|
||||
}
|
||||
if (!new) {
|
||||
new = new_demux_packet(dp->len);
|
||||
memcpy(new->buffer, dp->buffer, new->len);
|
||||
}
|
||||
if (!new)
|
||||
return NULL;
|
||||
new->pts = dp->pts;
|
||||
new->dts = dp->dts;
|
||||
new->duration = dp->duration;
|
||||
|
||||
+4
-2
@@ -34,11 +34,13 @@ typedef struct demux_packet {
|
||||
bool keyframe;
|
||||
int stream; // source stream index
|
||||
struct demux_packet *next;
|
||||
struct AVPacket *avpacket; // keep the buffer allocation
|
||||
void *allocation;
|
||||
struct AVPacket *avpacket; // original libavformat packet (demux_lavf)
|
||||
} demux_packet_t;
|
||||
|
||||
struct demux_packet *new_demux_packet(size_t len);
|
||||
struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt);
|
||||
// data must already have suitable padding
|
||||
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len);
|
||||
void demux_packet_shorten(struct demux_packet *dp, size_t len);
|
||||
void free_demux_packet(struct demux_packet *dp);
|
||||
|
||||
+5
-10
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "audio/chmap.h"
|
||||
|
||||
#include "ms_hdr.h"
|
||||
struct MPOpts;
|
||||
struct demuxer;
|
||||
|
||||
@@ -55,7 +55,6 @@ struct sh_stream {
|
||||
char *title;
|
||||
char *lang; // language code
|
||||
bool default_track; // container default track flag
|
||||
int hls_bitrate;
|
||||
|
||||
// stream is a picture (such as album art)
|
||||
struct demux_packet *attached_picture;
|
||||
@@ -67,10 +66,10 @@ struct sh_stream {
|
||||
typedef struct sh_audio {
|
||||
int samplerate;
|
||||
struct mp_chmap channels;
|
||||
bool force_channels;
|
||||
int bitrate; // compressed bits/sec
|
||||
int block_align;
|
||||
int bits_per_coded_sample;
|
||||
// win32-compatible codec parameters:
|
||||
MP_WAVEFORMATEX *wf;
|
||||
// note codec extradata may be either under "wf" or "codecdata"
|
||||
unsigned char *codecdata;
|
||||
int codecdata_len;
|
||||
struct replaygain_data *replaygain_data;
|
||||
@@ -81,13 +80,9 @@ typedef struct sh_video {
|
||||
float fps; // frames per second (set only if constant fps)
|
||||
float aspect; // aspect ratio stored in the file (for prescaling)
|
||||
int bitrate; // compressed bits/sec
|
||||
int bits_per_coded_sample;
|
||||
int coded_width, coded_height;
|
||||
unsigned char *extradata;
|
||||
int extradata_len;
|
||||
int disp_w, disp_h; // display size
|
||||
int rotate; // intended display rotation, in degrees, [0, 359]
|
||||
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
|
||||
MP_BITMAPINFOHEADER *bih;
|
||||
} sh_video_t;
|
||||
|
||||
typedef struct sh_sub {
|
||||
|
||||
+4
-3
@@ -86,10 +86,11 @@
|
||||
#
|
||||
# Disable the behavior that the player will pause if the cache goes below a
|
||||
# certain fill size.
|
||||
#cache-pause=no
|
||||
#cache-pause-below=no
|
||||
#
|
||||
# Read ahead about 5 seconds of audio and video packets.
|
||||
#demuxer-readahead-secs=5.0
|
||||
# Enable demuxer-based cache. This reads ahead a number of packets, and the
|
||||
# player won't freeze completely if network is slow during playback.
|
||||
#demuxer-thread=yes
|
||||
|
||||
# Display English subtitles if available.
|
||||
#slang=en
|
||||
|
||||
+15
-14
@@ -33,8 +33,8 @@
|
||||
#MOUSE_BTN2 cycle pause # toggle pause on/off
|
||||
#MOUSE_BTN3 seek 10
|
||||
#MOUSE_BTN4 seek -10
|
||||
#MOUSE_BTN5 add volume 2
|
||||
#MOUSE_BTN6 add volume -2
|
||||
#MOUSE_BTN5 add volume 1
|
||||
#MOUSE_BTN6 add volume -1
|
||||
|
||||
# Mouse wheels, touchpad or other input devices that have axes
|
||||
# if the input devices supports precise scrolling it will also scale the
|
||||
@@ -67,7 +67,7 @@
|
||||
#q quit
|
||||
#Q quit_watch_later
|
||||
#q {encode} quit
|
||||
#ESC set fullscreen no
|
||||
#ESC quit
|
||||
#ESC {encode} quit
|
||||
#p cycle pause # toggle pause/playback mode
|
||||
#. frame_step # advance one frame and pause
|
||||
@@ -84,10 +84,10 @@
|
||||
#x add sub-delay +0.1 # add
|
||||
#ctrl++ add audio-delay 0.100 # this changes audio/video sync
|
||||
#ctrl+- add audio-delay -0.100
|
||||
#9 add volume -2
|
||||
#/ add volume -2
|
||||
#0 add volume 2
|
||||
#* add volume 2
|
||||
#9 add volume -1
|
||||
#/ add volume -1
|
||||
#0 add volume 1
|
||||
#* add volume 1
|
||||
#m cycle mute
|
||||
#1 add contrast -1
|
||||
#2 add contrast 1
|
||||
@@ -126,13 +126,12 @@
|
||||
#REWIND seek -60
|
||||
#NEXT playlist_next
|
||||
#PREV playlist_prev
|
||||
#VOLUME_UP add volume 2
|
||||
#VOLUME_DOWN add volume -2
|
||||
#VOLUME_UP add volume 1
|
||||
#VOLUME_DOWN add volume -1
|
||||
#MUTE cycle mute
|
||||
#CLOSE_WIN quit
|
||||
#CLOSE_WIN {encode} quit
|
||||
#E cycle edition # next edition
|
||||
#ctrl+c quit
|
||||
|
||||
# Apple Remote section
|
||||
#AR_PLAY cycle pause
|
||||
@@ -145,9 +144,9 @@
|
||||
#AR_PREV_HOLD seek -120
|
||||
#AR_MENU show_progress
|
||||
#AR_MENU_HOLD cycle mute
|
||||
#AR_VUP add volume 2
|
||||
#AR_VUP add volume 1
|
||||
#AR_VUP_HOLD add chapter 1
|
||||
#AR_VDOWN add volume -2
|
||||
#AR_VDOWN add volume -1
|
||||
#AR_VDOWN_HOLD add chapter -1
|
||||
|
||||
# Joystick section
|
||||
@@ -161,8 +160,8 @@
|
||||
#JOY_AXIS1_PLUS seek -60
|
||||
#JOY_BTN0 cycle pause
|
||||
#JOY_BTN1 cycle osd
|
||||
#JOY_BTN2 add volume 2
|
||||
#JOY_BTN3 add volume -2
|
||||
#JOY_BTN2 add volume 1
|
||||
#JOY_BTN3 add volume -1
|
||||
|
||||
# For dvdnav:// and bdnav://
|
||||
|
||||
@@ -207,3 +206,5 @@
|
||||
# ? cycle sub-forced-only # toggle DVD forced subs
|
||||
# ? cycle program # cycle transport stream programs
|
||||
# ? stop # stop playback (quit or enter idle mode)
|
||||
# ? cycle quvi-format 1 # cycle streaming video quality (semi-broken)
|
||||
# ? cycle quvi-format -1
|
||||
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user